From: Stefan Hajnoczi <stefa...@redhat.com> Do not expose lo_dirp pointers to clients.
Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com> --- contrib/virtiofsd/passthrough_ll.c | 100 +++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 25 deletions(-) diff --git a/contrib/virtiofsd/passthrough_ll.c b/contrib/virtiofsd/passthrough_ll.c index f718c951f7..9f82166079 100644 --- a/contrib/virtiofsd/passthrough_ll.c +++ b/contrib/virtiofsd/passthrough_ll.c @@ -56,22 +56,10 @@ #define HAVE_POSIX_FALLOCATE 1 -/* We are re-using pointers to our `struct lo_dirp` - elements as inodes. This means that we must be able to - store uintptr_t values in a fuse_ino_t variable. The following - incantation checks this condition at compile time. */ -#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus -_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), - "fuse_ino_t too small to hold uintptr_t values!"); -#else -struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ - { unsigned _uintptr_to_must_hold_fuse_ino_t: - ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; -#endif - struct lo_map_elem { union { struct lo_inode *inode; + struct lo_dirp *dirp; ssize_t freelist; }; bool in_use; @@ -118,6 +106,7 @@ struct lo_data { int timeout_set; struct lo_inode root; /* protected by lo->mutex */ struct lo_map ino_map; /* protected by lo->mutex */ + struct lo_map dirp_map; /* protected by lo->mutex */ }; static const struct fuse_opt lo_opts[] = { @@ -252,6 +241,19 @@ static void lo_map_remove(struct lo_map *map, size_t key) map->freelist = key; } +/* Assumes lo->mutex is held */ +static ssize_t lo_add_dirp_mapping(fuse_req_t req, struct lo_dirp *dirp) +{ + struct lo_map_elem *elem; + + elem = lo_map_alloc_elem(&lo_data(req)->dirp_map); + if (!elem) + return -1; + + elem->dirp = dirp; + return elem - lo_data(req)->dirp_map.elems; +} + /* Assumes lo->mutex is held */ static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) { @@ -820,16 +822,28 @@ struct lo_dirp { off_t offset; }; -static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) +static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) { - return (struct lo_dirp *) (uintptr_t) fi->fh; + struct lo_data *lo = lo_data(req); + struct lo_map_elem *elem; + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->dirp_map, fi->fh); + pthread_mutex_unlock(&lo->mutex); + if (!elem) + return NULL; + + return elem->dirp; } static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { int error = ENOMEM; struct lo_data *lo = lo_data(req); - struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp)); + struct lo_dirp *d; + ssize_t fh; + + d = calloc(1, sizeof(struct lo_dirp)); if (d == NULL) goto out_err; @@ -844,7 +858,13 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi d->offset = 0; d->entry = NULL; - fi->fh = (uintptr_t) d; + pthread_mutex_lock(&lo->mutex); + fh = lo_add_dirp_mapping(req, d); + pthread_mutex_unlock(&lo->mutex); + if (fh == -1) + goto out_err; + + fi->fh = fh; if (lo->cache == CACHE_ALWAYS) fi->keep_cache = 1; fuse_reply_open(req, fi); @@ -854,6 +874,8 @@ out_errno: error = errno; out_err: if (d) { + if (d->dp) + closedir(d->dp); if (d->fd != -1) close(d->fd); free(d); @@ -870,19 +892,21 @@ static int is_dot_or_dotdot(const char *name) static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, struct fuse_file_info *fi, int plus) { - struct lo_dirp *d = lo_dirp(fi); - char *buf; + struct lo_dirp *d; + char *buf = NULL; char *p; size_t rem = size; - int err; + int err = ENOMEM; (void) ino; + d = lo_dirp(req, fi); + if (!d) + goto error; + buf = calloc(1, size); - if (!buf) { - err = ENOMEM; + if (!buf) goto error; - } p = buf; if (offset != d->offset) { @@ -974,8 +998,21 @@ static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { - struct lo_dirp *d = lo_dirp(fi); + struct lo_data *lo = lo_data(req); + struct lo_dirp *d; + (void) ino; + + d = lo_dirp(req, fi); + if (!d) { + fuse_reply_err(req, EBADF); + return; + } + + pthread_mutex_lock(&lo->mutex); + lo_map_remove(&lo->dirp_map, fi->fh); + pthread_mutex_unlock(&lo->mutex); + closedir(d->dp); free(d); fuse_reply_err(req, 0); @@ -1023,8 +1060,18 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi) { int res; - int fd = dirfd(lo_dirp(fi)->dp); + struct lo_dirp *d; + int fd; + (void) ino; + + d = lo_dirp(req, fi); + if (!d) { + fuse_reply_err(req, EBADF); + return; + } + + fd = dirfd(d->dp); if (datasync) res = fdatasync(fd); else @@ -1504,6 +1551,8 @@ int main(int argc, char *argv[]) root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); root_elem->inode = &lo.root; + lo_map_init(&lo.dirp_map); + if (fuse_parse_cmdline(&args, &opts) != 0) return 1; if (opts.show_help) { @@ -1597,6 +1646,7 @@ err_out2: err_out1: fuse_opt_free_args(&args); + lo_map_destroy(&lo.dirp_map); lo_map_destroy(&lo.ino_map); if (lo.root.fd >= 0) -- 2.23.0