void put_handle (struct shim_handle * hdl) { int ref_count = REF_DEC(hdl->ref_count); #ifdef DEBUG_REF debug("put handle %p(%s) (ref_count = %d)\n", hdl, __handle_name(hdl), ref_count); #endif if (!ref_count) { if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->hput) hdl->fs->fs_ops->hput(hdl); qstrfree(&hdl->path); qstrfree(&hdl->uri); if (hdl->pal_handle) DkObjectClose(hdl->pal_handle); if (hdl->dentry) put_dentry(hdl->dentry); if (hdl->fs) put_mount(hdl->fs); destroy_handle(hdl); } }
int shim_do_readlink (const char * file, char * buf, int bufsize) { if (!file) return -EFAULT; if (bufsize <= 0) return -EINVAL; /* The correct behavior is to return -EINVAL if file is not a symbolic link */ return -EINVAL; #if 0 int ret; struct shim_dentry * dent = NULL; if ((ret = path_lookupat(NULL, file, LOOKUP_ACCESS, &dent)) < 0) return ret; char * relpath; int len; relpath = dentry_get_path(dent, true, &len); if (len > bufsize) len = bufsize; memcpy(buf, relpath, len); put_dentry(dent); return len; #endif }
int shim_do_openat (int dfd, const char * filename, int flags, int mode) { if (!filename || test_user_string(filename)) return -EFAULT; if (*filename == '/') return shim_do_open(filename, flags, mode); struct shim_dentry * dir = NULL; int ret = 0; if ((ret = path_startat(dfd, &dir)) < 0) return ret; struct shim_handle * hdl = get_new_handle(); if (!hdl) { ret = -ENOMEM; goto out; } ret = open_namei(hdl, dir, filename, flags, mode, NULL); if (ret < 0) goto out_hdl; ret = set_new_fd_handle(hdl, flags & O_CLOEXEC ? FD_CLOEXEC : 0, NULL); out_hdl: put_handle(hdl); out: put_dentry(dir); return ret; }
void close_handle (struct shim_handle * hdl) { int opened = REF_DEC(hdl->opened); #ifdef DEBUG_REF debug("close handle %p(%s) (opened = %d)\n", hdl, __handle_name(hdl), opened); #endif if (!opened) { if (hdl->type == TYPE_DIR) { struct shim_dir_handle * dir = &hdl->info.dir; if (dir->dot) { put_dentry(dir->dot); dir->dot = NULL; } if (dir->dotdot) { put_dentry(dir->dotdot); dir->dotdot = NULL; } while (*dir->ptr) { struct shim_dentry * dent = *dir->ptr; put_dentry(dent); *(dir->ptr++) = NULL; } } else { if (hdl->fs && hdl->fs->fs_ops && hdl->fs->fs_ops->close) hdl->fs->fs_ops->close(hdl); } } put_handle(hdl); }
static int proc_thread_link_stat (const char * name, struct stat * buf) { struct shim_dentry * dent; int ret = find_thread_link(name, NULL, &dent, NULL); if (ret < 0) return ret; if (!dent->fs || !dent->fs->d_ops || !dent->fs->d_ops->stat) { ret = -EACCES; goto out; } ret = dent->fs->d_ops->stat(dent, buf); out: put_dentry(dent); return ret; }
static int proc_thread_link_mode (const char * name, mode_t * mode) { struct shim_dentry * dent; int ret = find_thread_link(name, NULL, &dent, NULL); if (ret < 0) return ret; if (!dent->fs || !dent->fs->d_ops || !dent->fs->d_ops->mode) { ret = -EACCES; goto out; } ret = dent->fs->d_ops->mode(dent, mode, true); out: put_dentry(dent); return ret; }
static int proc_thread_link_open (struct shim_handle * hdl, const char * name, int flags) { struct shim_dentry * dent; int ret = find_thread_link(name, NULL, &dent, NULL); if (ret < 0) return ret; if (!dent->fs || !dent->fs->d_ops || !dent->fs->d_ops->open) { ret = -EACCES; goto out; } ret = dent->fs->d_ops->open(hdl, dent, flags); out: put_dentry(dent); return 0; }
int shim_do_lstat (const char * file, struct stat * stat) { if (!file) return -EFAULT; int ret; struct shim_dentry * dent = NULL; if ((ret = path_lookupat(NULL, file, LOOKUP_ACCESS, &dent)) < 0) goto out; struct shim_mount * fs = dent->fs; if (!fs->d_ops || !fs->d_ops->stat) { ret = -EACCES; goto out_dentry; } ret = fs->d_ops->stat(dent, stat); out_dentry: put_dentry(dent); out: return ret; }
size_t shim_do_getdents64 (int fd, struct linux_dirent64 * buf, size_t count) { if (!buf || test_user_memory(buf, count, true)) return -EFAULT; struct shim_handle * hdl = get_fd_handle(fd, NULL, NULL); if (!hdl) return -EBADF; int ret = -EACCES; if (hdl->type != TYPE_DIR) { ret = -ENOTDIR; goto out; } /* DEP 3/3/17: Properly handle an unlinked directory */ if (hdl->dentry->state & DENTRY_NEGATIVE) { ret = -ENOENT; goto out; } lock(&hdl->lock); struct shim_dir_handle * dirhdl = &hdl->info.dir; struct shim_dentry * dent = hdl->dentry; struct linux_dirent64 * b = buf; int bytes = 0; /* If we haven't listed the directory, do this first */ if (!(dent->state & DENTRY_LISTED)) { ret = list_directory_dentry(dent); if (ret) goto out; } #define DIRENT_SIZE(len) (sizeof(struct linux_dirent64) + (len) + 1) #define ASSIGN_DIRENT(dent, name, type) \ do { \ int len = strlen(name); \ if (bytes + DIRENT_SIZE(len) > count) \ goto done; \ \ b->d_ino = (dent)->ino; \ b->d_off = ++dirhdl->offset; \ b->d_reclen = DIRENT_SIZE(len); \ b->d_type = (type); \ \ memcpy(b->d_name, name, len + 1); \ \ b = (void *) b + DIRENT_SIZE(len); \ bytes += DIRENT_SIZE(len); \ } while(0) if (dirhdl->dot) { ASSIGN_DIRENT(dirhdl->dot, ".", LINUX_DT_DIR); put_dentry(dirhdl->dot); dirhdl->dot = NULL; } if (dirhdl->dotdot) { ASSIGN_DIRENT(dirhdl->dotdot, "..", LINUX_DT_DIR); put_dentry(dirhdl->dotdot); dirhdl->dotdot = NULL; } if (dirhdl->ptr == (void *) -1) { ret = list_directory_handle(dent, hdl); if (ret) goto out; } while (dirhdl->ptr && *dirhdl->ptr) { dent = *dirhdl->ptr; /* DEP 3/3/17: We need to filter negative dentries */ if (!(dent->state & DENTRY_NEGATIVE)) ASSIGN_DIRENT(dent, dentry_get_name(dent), get_dirent_type(dent->type)); put_dentry(dent); *(dirhdl->ptr++) = NULL; } #undef DIRENT_SIZE #undef ASSIGN_DIRENT done: ret = bytes; /* DEP 3/3/17: Properly detect EINVAL case, where buffer is too small to * hold anything */ if (bytes == 0 && (dirhdl->dot || dirhdl->dotdot || (dirhdl->ptr && *dirhdl->ptr))) ret = -EINVAL; unlock(&hdl->lock); out: put_handle(hdl); return ret; }
size_t shim_do_getdents64 (int fd, struct linux_dirent64 * buf, size_t count) { struct shim_handle * hdl = get_fd_handle(fd, NULL, NULL); if (!hdl) return -EBADF; int ret = -EACCES; if (hdl->type != TYPE_DIR) { ret = -ENOTDIR; goto out; } lock(hdl->lock); struct shim_dir_handle * dirhdl = &hdl->info.dir; struct shim_dentry * dent = hdl->dentry; struct linux_dirent64 * b = buf; int bytes = 0; #define DIRENT_SIZE(len) (sizeof(struct linux_dirent64) + (len) + 1) #define ASSIGN_DIRENT(dent, name, type) \ do { \ int len = strlen(name); \ if (bytes + DIRENT_SIZE(len) > count) \ goto done; \ \ b->d_ino = dent->ino; \ b->d_off = ++dirhdl->offset; \ b->d_reclen = DIRENT_SIZE(len); \ b->d_type = type ? : get_dirent_type(dent->mode); \ \ memcpy(b->d_name, name, len + 1); \ \ b = (void *) b + DIRENT_SIZE(len); \ bytes += DIRENT_SIZE(len); \ } while(0) if (dirhdl->dot) { ASSIGN_DIRENT(dirhdl->dot, ".", LINUX_DT_DIR); put_dentry(dirhdl->dot); dirhdl->dot = NULL; } if (dirhdl->dotdot) { ASSIGN_DIRENT(dirhdl->dotdot, "..", LINUX_DT_DIR); put_dentry(dirhdl->dotdot); dirhdl->dotdot = NULL; } while (*dirhdl->ptr) { dent = *dirhdl->ptr; ASSIGN_DIRENT(dent, dentry_get_name(dent), 0); put_dentry(dent); *(dirhdl->ptr++) = NULL; } #undef DIRENT_SIZE #undef ASSIGN_DIRENT done: ret = bytes; unlock(hdl->lock); out: put_handle(hdl); return ret; }
static int find_thread_link (const char * name, struct shim_qstr * link, struct shim_dentry ** dentptr, struct shim_thread ** threadptr) { const char * next, * nextnext; int next_len; int pid = parse_thread_name(name, &next, &next_len, &nextnext); if (pid < 0) return pid; struct shim_thread * thread = lookup_thread(pid); struct shim_dentry * dent = NULL; int ret = 0; if (!thread) return -ENOENT; if (!thread->in_vm) { ret = -ENOENT; goto out; } lock(&thread->lock); if (next_len == static_strlen("root") && !memcmp(next, "root", next_len)) { dent = thread->root; get_dentry(dent); } if (next_len == static_strlen("cwd") && !memcmp(next, "cwd", next_len)) { dent = thread->cwd; get_dentry(dent); } if (next_len == static_strlen("exe") && !memcmp(next, "exe", next_len)) { struct shim_handle * exec = thread->exec; if (!exec->dentry) { unlock(&thread->lock); ret = -EINVAL; goto out; } dent = exec->dentry; get_dentry(dent); } unlock(&thread->lock); if (nextnext) { struct shim_dentry * next_dent = NULL; ret = path_lookupat(dent, nextnext, 0, &next_dent, dent->fs); if (ret < 0) goto out; put_dentry(dent); dent = next_dent; } if (link) { int size; char * path = dentry_get_path(dent, true, &size); qstrsetstr(link, path, size); } if (dentptr) { get_dentry(dent); *dentptr = dent; } if (threadptr) { get_thread(thread); *threadptr = thread; } ret = 0; out: if (dent) put_dentry(dent); if (thread) put_thread(thread); return ret; }
static int isolate_fs (struct config_store * cfg, const char * path) { struct shim_dentry * dent = NULL; int ret = 0; if ((ret = path_lookupat(NULL, path, LOOKUP_OPEN, &dent)) < 0) return ret; if (!(dent->state & DENTRY_ISDIRECTORY)) { put_dentry(dent); return -ENOTDIR; } int dpath_len = 0; char * dpath = dentry_get_path(dent, true, &dpath_len); bool root_created = false; char t[CONFIG_MAX], u[CONFIG_MAX]; int nkeys, keybuf_size = CONFIG_MAX; char * keybuf = __alloca(keybuf_size); while ((nkeys = get_config_entries(cfg, "fs.mount.other", keybuf, keybuf_size)) == -ENAMETOOLONG) { keybuf_size *= 2; keybuf = __alloca(keybuf_size); } if (nkeys <= 0) goto root; char k[CONFIG_MAX], p[CONFIG_MAX]; memcpy(k, "fs.mount.other.", 15); const char * key = keybuf, * next = NULL; for (int n = 0 ; n < nkeys ; key = next, n++) { for (next = key ; *next ; next++); next++; int key_len = next - key - 1; memcpy(k + 15, key, key_len); char * kp = k + 15 + key_len; int ulen, plen; bool is_chroot = false; /* Skip FS that are not chroot */ memcpy(kp, ".type", 6); if ((ret = get_config(cfg, k, t, CONFIG_MAX)) <= 0) continue; if (ret == 6 || !memcmp(t, "chroot", 6)) is_chroot = true; memcpy(kp, ".uri", 5); if ((ulen = get_config(cfg, k, u, CONFIG_MAX)) <= 0) continue; memcpy(kp, ".path", 6); if ((plen = get_config(cfg, k, p, CONFIG_MAX)) <= 0) continue; if (plen >= dpath_len) { if (!memcmp(p, dpath, dpath_len)) { if (!p[dpath_len]) { root_created = true; debug("kept file rule: %s => %s\n", p, u); continue; } if (p[dpath_len] != '/') goto remove; /* keep this FS */ continue; } else { remove: if (!is_chroot) { debug("kept file rule: %s => %s\n", p, u); continue; } set_config(cfg, k, NULL); memcpy(kp, ".type", 6); set_config(cfg, k, NULL); memcpy(kp, ".uri", 5); set_config(cfg, k, NULL); debug("deleted file rule: %s => %s\n", p, u); } } else { if (memcmp(p, dpath, plen)) goto remove; assert(dpath[plen]); if (dpath[plen] != '/') goto remove; if (!is_chroot) { root_created = true; debug("kept file rule: %s => %s\n", p, u); continue; } append_uri(u, ulen, dpath + plen, dpath_len - plen); set_config(cfg, k, dpath); memcpy(kp, "uri", 5); set_config(cfg, k, u); root_created = true; debug("added file rule: %s => %s\n", dpath, u); } } root: if ((ret = get_config(cfg, "fs.mount.root.uri", u, CONFIG_MAX)) > 0) { int prefix_len = ret; if ((ret = get_config(cfg, "fs.mount.root.type", t, CONFIG_MAX)) > 0 && ret == 6 && !memcmp(t, "chroot", 6)) { /* remove the root FS */ set_config(cfg, "fs.mount.root.uri", NULL); set_config(cfg, "fs.mount.root.type", NULL); debug("deleted file rule: root\n"); /* add another FS as part of the original root FS */ if (!root_created) { append_uri(u, prefix_len, dpath, dpath_len); set_config(cfg, "fs.mount.other.root.path", dpath); set_config(cfg, "fs.mount.other.root.uri", u); set_config(cfg, "fs.mount.other.root.type", "chroot"); debug("added file rule: %s => %s\n", dpath, u); } } } return 0; }