static int fuse_readdir(struct file *file, struct dir_context *ctx) { int plus, err; size_t nbytes; struct page *page; struct inode *inode = file_inode(file); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; u64 attr_version = 0; if (is_bad_inode(inode)) return -EIO; req = fuse_get_req(fc, 1); if (IS_ERR(req)) return PTR_ERR(req); page = alloc_page(GFP_KERNEL); if (!page) { fuse_put_request(fc, req); return -ENOMEM; } plus = fuse_use_readdirplus(inode, ctx); req->out.argpages = 1; req->num_pages = 1; req->pages[0] = page; req->page_descs[0].length = PAGE_SIZE; if (plus) { attr_version = fuse_get_attr_version(fc); fuse_read_fill(req, file, ctx->pos, PAGE_SIZE, FUSE_READDIRPLUS); } else { fuse_read_fill(req, file, ctx->pos, PAGE_SIZE, FUSE_READDIR); } fuse_lock_inode(inode); fuse_request_send(fc, req); fuse_unlock_inode(inode); nbytes = req->out.args[0].size; err = req->out.h.error; fuse_put_request(fc, req); if (!err) { if (plus) { err = parse_dirplusfile(page_address(page), nbytes, file, ctx, attr_version); } else { err = parse_dirfile(page_address(page), nbytes, file, ctx); } } __free_page(page); fuse_invalidate_atime(inode); return err; }
static size_t fuse_send_read(struct fuse_req *req, struct file *file, struct inode *inode, loff_t pos, size_t count) { struct fuse_conn *fc = get_fuse_conn(inode); fuse_read_fill(req, file, inode, pos, count, FUSE_READ); request_send(fc, req); return req->out.args[0].size; }
static void fuse_send_readpages(struct fuse_req *req, struct file *file, struct inode *inode) { struct fuse_conn *fc = get_fuse_conn(inode); loff_t pos = page_offset(req->pages[0]); size_t count = req->num_pages << PAGE_CACHE_SHIFT; req->out.page_zeroing = 1; fuse_read_fill(req, file, inode, pos, count, FUSE_READ); if (fc->async_read) { req->end = fuse_readpages_end; request_send_background(fc, req); } else { request_send(fc, req); fuse_readpages_end(fc, req); } }
static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) { int err; size_t nbytes; struct page *page; struct inode *inode = file->f_path.dentry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; if (is_bad_inode(inode)) return -EIO; req = fuse_get_req(fc); if (IS_ERR(req)) return PTR_ERR(req); page = alloc_page(GFP_KERNEL); if (!page) { fuse_put_request(fc, req); return -ENOMEM; } req->out.argpages = 1; req->num_pages = 1; req->pages[0] = page; fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIR); fuse_request_send(fc, req); nbytes = req->out.args[0].size; err = req->out.h.error; fuse_put_request(fc, req); if (!err) err = parse_dirfile(page_address(page), nbytes, file, dstbuf, filldir); __free_page(page); fuse_invalidate_attr(inode); /* atime changed */ return err; }
static int fuse_direntplus_link(struct file *file, struct fuse_direntplus *direntplus, u64 attr_version) { int err; struct fuse_entry_out *o = &direntplus->entry_out; struct fuse_dirent *dirent = &direntplus->dirent; struct dentry *parent = file->f_path.dentry; struct qstr name = { .len = dirent->namelen, .name = dirent->name }; struct dentry *dentry; struct dentry *alias; struct inode *dir = parent->d_inode; struct fuse_conn *fc; struct inode *inode; if (!o->nodeid) { /* * Unlike in the case of fuse_lookup, zero nodeid does not mean * ENOENT. Instead, it only means the userspace filesystem did * not want to return attributes/handle for this entry. * * So do nothing. */ return 0; } if (name.name[0] == '.') { /* * We could potentially refresh the attributes of the directory * and its parent? */ if (name.len == 1) return 0; if (name.name[1] == '.' && name.len == 2) return 0; } if (invalid_nodeid(o->nodeid)) return -EIO; if (!fuse_valid_type(o->attr.mode)) return -EIO; fc = get_fuse_conn(dir); name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); if (dentry) { inode = dentry->d_inode; if (!inode) { d_drop(dentry); } else if (get_node_id(inode) != o->nodeid || ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { err = d_invalidate(dentry); if (err) goto out; } else if (is_bad_inode(inode)) { err = -EIO; goto out; } else { struct fuse_inode *fi; fi = get_fuse_inode(inode); spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), attr_version); /* * The other branch to 'found' comes via fuse_iget() * which bumps nlookup inside */ goto found; } dput(dentry); } dentry = d_alloc(parent, &name); err = -ENOMEM; if (!dentry) goto out; dentry->d_op = &fuse_dentry_operations; inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, entry_attr_timeout(o), attr_version); if (!inode) goto out; alias = fuse_materialise_dentry(dentry, inode); err = PTR_ERR(alias); if (IS_ERR(alias)) goto out; if (alias) { dput(dentry); dentry = alias; } found: fuse_change_entry_timeout(dentry, o); err = 0; out: dput(dentry); return err; } static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, void *dstbuf, filldir_t filldir, u64 attr_version) { struct fuse_direntplus *direntplus; struct fuse_dirent *dirent; size_t reclen; int over = 0; int ret; while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) { direntplus = (struct fuse_direntplus *) buf; dirent = &direntplus->dirent; reclen = FUSE_DIRENTPLUS_SIZE(direntplus); if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX) return -EIO; if (reclen > nbytes) break; if (memchr(dirent->name, '/', dirent->namelen) != NULL) return -EIO; if (!over) { /* We fill entries into dstbuf only as much as it can hold. But we still continue iterating over remaining entries to link them. If not, we need to send a FORGET for each of those which we did not link. */ over = filldir(dstbuf, dirent->name, dirent->namelen, file->f_pos, dirent->ino, dirent->type); file->f_pos = dirent->off; } buf += reclen; nbytes -= reclen; ret = fuse_direntplus_link(file, direntplus, attr_version); if (ret) fuse_force_forget(file, direntplus->entry_out.nodeid); } return 0; } static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) { int plus, err; size_t nbytes; struct page *page; struct inode *inode = file->f_path.dentry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; u64 attr_version = 0; if (is_bad_inode(inode)) return -EIO; req = fuse_get_req(fc, 1); if (IS_ERR(req)) return PTR_ERR(req); page = alloc_page(GFP_KERNEL); if (!page) { fuse_put_request(fc, req); return -ENOMEM; } plus = fuse_use_readdirplus(inode, file); req->out.argpages = 1; req->num_pages = 1; req->pages[0] = page; req->page_descs[0].length = PAGE_SIZE; if (plus) { attr_version = fuse_get_attr_version(fc); fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIRPLUS); } else { fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIR); } fuse_request_send(fc, req); nbytes = req->out.args[0].size; err = req->out.h.error; fuse_put_request(fc, req); if (!err) { if (plus) { err = parse_dirplusfile(page_address(page), nbytes, file, dstbuf, filldir, attr_version); } else { err = parse_dirfile(page_address(page), nbytes, file, dstbuf, filldir); } } __free_page(page); fuse_invalidate_attr(inode); /* atime changed */ return err; }