Beispiel #1
0
static struct dentry *fuse_materialise_dentry(struct dentry *dentry,
					      struct inode *inode)
{
	struct dentry *newent;

	if (inode && S_ISDIR(inode->i_mode)) {
		struct fuse_conn *fc = get_fuse_conn(inode);

		mutex_lock(&fc->inst_mutex);
		newent = d_materialise_unique(dentry, inode);
		mutex_unlock(&fc->inst_mutex);
	} else {
		newent = d_materialise_unique(dentry, inode);
	}

	return newent;
}
Beispiel #2
0
/*
 * Find the dentry that matches "name". If there isn't one, create one. If it's
 * a negative dentry or the uniqueid changed, then drop it and recreate it.
 */
static struct dentry *
cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;

	cFYI(1, "For %s", name->name);

	if (parent->d_op && parent->d_op->d_hash)
		parent->d_op->d_hash(parent, parent->d_inode, name);
	else
		name->hash = full_name_hash(name->name, name->len);

	dentry = d_lookup(parent, name);
	if (dentry) {
		inode = dentry->d_inode;
		/* update inode in place if i_ino didn't change */
		if (inode && CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
			cifs_fattr_to_inode(inode, fattr);
			return dentry;
		}
		d_drop(dentry);
		dput(dentry);
	}

	/*
	 * If we know that the inode will need to be revalidated immediately,
	 * then don't create a new dentry for it. We'll end up doing an on
	 * the wire call either way and this spares us an invalidation.
	 */
	if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
		return NULL;

	dentry = d_alloc(parent, name);
	if (dentry == NULL)
		return NULL;

	inode = cifs_iget(sb, fattr);
	if (!inode) {
		dput(dentry);
		return NULL;
	}

	alias = d_materialise_unique(dentry, inode);
	if (alias != NULL) {
		dput(dentry);
		if (IS_ERR(alias))
			return NULL;
		dentry = alias;
	}

	return dentry;
}
Beispiel #3
0
/*
 * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
 *
 * Find the dentry that matches "name". If there isn't one, create one. If it's
 * a negative dentry or the uniqueid changed, then drop it and recreate it.
 */
static void
cifs_prime_dcache(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);

	cFYI(1, "%s: for %s", __func__, name->name);

	dentry = d_hash_and_lookup(parent, name);
	if (unlikely(IS_ERR(dentry)))
		return;

	if (dentry) {
		int err;

		inode = dentry->d_inode;
		if (inode) {
			/*
			 * If we're generating inode numbers, then we don't
			 * want to clobber the existing one with the one that
			 * the readdir code created.
			 */
			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
				fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;

			/* update inode in place if i_ino didn't change */
			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
				cifs_fattr_to_inode(inode, fattr);
				goto out;
			}
		}
		err = d_invalidate(dentry);
		dput(dentry);
		if (err)
			return;
	}

	dentry = d_alloc(parent, name);
	if (!dentry)
		return;

	inode = cifs_iget(sb, fattr);
	if (!inode)
		goto out;

	alias = d_materialise_unique(dentry, inode);
	if (alias && !IS_ERR(alias))
		dput(alias);
out:
	dput(dentry);
}
Beispiel #4
0
static struct dentry *
cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;

	cFYI(1, "For %s", name->name);

	if (parent->d_op && parent->d_op->d_hash)
		parent->d_op->d_hash(parent, parent->d_inode, name);
	else
		name->hash = full_name_hash(name->name, name->len);

	dentry = d_lookup(parent, name);
	if (dentry) {
		
		inode = dentry->d_inode;
		/* update inode in place if i_ino didn't change */
		if (inode && CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
			cifs_fattr_to_inode(inode, fattr);
 			return dentry;
		}
		d_drop(dentry);
		dput(dentry);
	}

	dentry = d_alloc(parent, name);
	if (dentry == NULL)
		return NULL;

	inode = cifs_iget(sb, fattr);
	if (!inode) {
		dput(dentry);
		return NULL;
	}

	alias = d_materialise_unique(dentry, inode);
	if (alias != NULL) {
		dput(dentry);
		if (IS_ERR(alias))
			return NULL;
		dentry = alias;
	}

	return dentry;
}
Beispiel #5
0
/*
 * Find the dentry that matches "name". If there isn't one, create one. If it's
 * a negative dentry or the uniqueid changed, then drop it and recreate it.
 */
static struct dentry *
cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;

	cFYI(1, "For %s", name->name);

	if (parent->d_op && parent->d_op->d_hash)
		parent->d_op->d_hash(parent, parent->d_inode, name);
	else
		name->hash = full_name_hash(name->name, name->len);

	dentry = d_lookup(parent, name);
	if (dentry) {
		/* FIXME: check for inode number changes? */
		if (dentry->d_inode != NULL)
			return dentry;
		d_drop(dentry);
		dput(dentry);
	}

	dentry = d_alloc(parent, name);
	if (dentry == NULL)
		return NULL;

	inode = cifs_iget(sb, fattr);
	if (!inode) {
		dput(dentry);
		return NULL;
	}

	alias = d_materialise_unique(dentry, inode);
	if (alias != NULL) {
		dput(dentry);
		if (IS_ERR(alias))
			return NULL;
		dentry = alias;
	}

	return dentry;
}
Beispiel #6
0
static struct dentry *vsnfs_lookup(struct inode *dir, struct dentry *dentry,
				   struct nameidata *nd)
{
	int ret;
	struct vsnfs_fh *newfh =
	    (struct vsnfs_fh *)kmalloc(sizeof(struct vsnfs_fh), GFP_KERNEL);
	struct inode *inode = NULL;

	vsnfs_trace(KERN_INFO, "VSNFS: lookup(%s/%s)\n",
		    dentry->d_parent->d_name.name, dentry->d_name.name);

	ret = -ENAMETOOLONG;

	if (dentry->d_name.len > VSNFS_MAXNAMLEN)
		goto out;

	ret = -ENOMEM;
	dentry->d_op = VSNFS_PROTO(dir)->dentry_ops;

	ret = vsnfs_do_lookup(dir, &dentry->d_name, newfh);
	if (ret == -ENOENT)
		goto no_entry;
	if (ret < 0)
		goto out;

	ret = -EINVAL;		/* We don't have permission checks */
	inode = vsnfs_fhget(dentry->d_sb, newfh);
	if (IS_ERR(inode))
		goto out;

no_entry:
	ret = 0;
	dentry = d_materialise_unique(dentry, inode);
	
	if (IS_ERR(dentry)) {
		vsnfs_trace(KERN_ERR,
			    "vsnfs_lookup:Error allocating dentry:%ld\n",
			    PTR_ERR(dentry));
		return dentry;
	}
	return dentry;
      out:
	return ERR_PTR(ret);
}
Beispiel #7
0
/*
 * Find the dentry that matches "name". If there isn't one, create one. If it's
 * a negative dentry or the uniqueid changed, then drop it and recreate it.
 */
static struct dentry *
cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;

	cFYI(1, ("For %s", name->name));

	dentry = d_lookup(parent, name);
	if (dentry) {
		/* FIXME: check for inode number changes? */
		if (dentry->d_inode != NULL)
			return dentry;
		d_drop(dentry);
		dput(dentry);
	}

	dentry = d_alloc(parent, name);
	if (dentry == NULL)
		return NULL;

	inode = cifs_iget(sb, fattr);
	if (!inode) {
		dput(dentry);
		return NULL;
	}

	if (CIFS_SB(sb)->tcon->nocase)
		dentry->d_op = &cifs_ci_dentry_ops;
	else
		dentry->d_op = &cifs_dentry_ops;

	alias = d_materialise_unique(dentry, inode);
	if (alias != NULL) {
		dput(dentry);
		if (IS_ERR(alias))
			return NULL;
		dentry = alias;
	}

	return dentry;
}
Beispiel #8
0
static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
				  unsigned int flags)
{
	int err;
	struct fuse_entry_out outarg;
	struct inode *inode;
	struct dentry *newent;
	bool outarg_valid = true;

	err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
			       &outarg, &inode);
	if (err == -ENOENT) {
		outarg_valid = false;
		err = 0;
	}
	if (err)
		goto out_err;

	err = -EIO;
	if (inode && get_node_id(inode) == FUSE_ROOT_ID)
		goto out_iput;

	newent = d_materialise_unique(entry, inode);
	err = PTR_ERR(newent);
	if (IS_ERR(newent))
		goto out_err;

	entry = newent ? newent : entry;
	if (outarg_valid)
		fuse_change_entry_timeout(entry, &outarg);
	else
		fuse_invalidate_entry_cache(entry);

	fuse_advise_use_readdirplus(dir);
	return newent;

 out_iput:
	iput(inode);
 out_err:
	return ERR_PTR(err);
}
Beispiel #9
0
static struct dentry *kernfs_iop_lookup(struct inode *dir,
					struct dentry *dentry,
					unsigned int flags)
{
	struct dentry *ret;
	struct kernfs_node *parent = dentry->d_parent->d_fsdata;
	struct kernfs_node *kn;
	struct inode *inode;
	const void *ns = NULL;

	mutex_lock(&kernfs_mutex);

	if (kernfs_ns_enabled(parent))
		ns = kernfs_info(dir->i_sb)->ns;

	kn = kernfs_find_ns(parent, dentry->d_name.name, ns);

	/* no such entry */
	if (!kn) {
		ret = NULL;
		goto out_unlock;
	}
	kernfs_get(kn);
	dentry->d_fsdata = kn;

	/* attach dentry and inode */
	inode = kernfs_get_inode(dir->i_sb, kn);
	if (!inode) {
		ret = ERR_PTR(-ENOMEM);
		goto out_unlock;
	}

	/* instantiate and hash dentry */
	ret = d_materialise_unique(dentry, inode);
 out_unlock:
	mutex_unlock(&kernfs_mutex);
	return ret;
}
static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry,
				   unsigned int flags)
{
	struct dentry *ret = NULL;
	struct dentry *parent = dentry->d_parent;
	struct sysfs_dirent *parent_sd = parent->d_fsdata;
	struct sysfs_dirent *sd;
	struct inode *inode;
	enum kobj_ns_type type;
	const void *ns;

	mutex_lock(&sysfs_mutex);

	type = sysfs_ns_type(parent_sd);
	ns = sysfs_info(dir->i_sb)->ns[type];

	sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns);

	/* no such entry */
	if (!sd) {
		ret = ERR_PTR(-ENOENT);
		goto out_unlock;
	}
	dentry->d_fsdata = sysfs_get(sd);

	/* attach dentry and inode */
	inode = sysfs_get_inode(dir->i_sb, sd);
	if (!inode) {
		ret = ERR_PTR(-ENOMEM);
		goto out_unlock;
	}

	/* instantiate and hash dentry */
	ret = d_materialise_unique(dentry, inode);
 out_unlock:
	mutex_unlock(&sysfs_mutex);
	return ret;
}
Beispiel #11
0
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 = QSTR_INIT(dirent->name, dirent->namelen);
	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;

	inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
			  &o->attr, entry_attr_timeout(o), attr_version);
	if (!inode)
		goto out;

	alias = d_materialise_unique(dentry, inode);
	err = PTR_ERR(alias);
	if (IS_ERR(alias))
		goto out;

	if (alias) {
		dput(dentry);
		dentry = alias;
	}

found:
	if (fc->readdirplus_auto)
		set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
	fuse_change_entry_timeout(dentry, o);

	err = 0;
out:
	dput(dentry);
	return err;
}
Beispiel #12
0
/*
 * Get root dentry from superblock according to prefix path mount option.
 * Return dentry with refcount + 1 on success and NULL otherwise.
 */
static struct dentry *
cifs_get_root(struct smb_vol *vol, struct super_block *sb)
{
	int xid, rc;
	struct inode *inode;
	struct qstr name;
	struct dentry *dparent = NULL, *dchild = NULL, *alias;
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
	unsigned int i, full_len, len;
	char *full_path = NULL, *pstart;
	char sep;

	full_path = cifs_build_path_to_root(vol, cifs_sb,
					    cifs_sb_master_tcon(cifs_sb));
	if (full_path == NULL)
		return NULL;

	cFYI(1, "Get root dentry for %s", full_path);

	xid = GetXid();
	sep = CIFS_DIR_SEP(cifs_sb);
	dparent = dget(sb->s_root);
	full_len = strlen(full_path);
	full_path[full_len] = sep;
	pstart = full_path + 1;

	for (i = 1, len = 0; i <= full_len; i++) {
		if (full_path[i] != sep || !len) {
			len++;
			continue;
		}

		full_path[i] = 0;
		cFYI(1, "get dentry for %s", pstart);

		name.name = pstart;
		name.len = len;
		name.hash = full_name_hash(pstart, len);
		dchild = d_lookup(dparent, &name);
		if (dchild == NULL) {
			cFYI(1, "not exists");
			dchild = d_alloc(dparent, &name);
			if (dchild == NULL) {
				dput(dparent);
				dparent = NULL;
				goto out;
			}
		}

		cFYI(1, "get inode");
		if (dchild->d_inode == NULL) {
			cFYI(1, "not exists");
			inode = NULL;
			if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
				rc = cifs_get_inode_info_unix(&inode, full_path,
							      sb, xid);
			else
				rc = cifs_get_inode_info(&inode, full_path,
							 NULL, sb, xid, NULL);
			if (rc) {
				dput(dchild);
				dput(dparent);
				dparent = NULL;
				goto out;
			}
			alias = d_materialise_unique(dchild, inode);
			if (alias != NULL) {
				dput(dchild);
				if (IS_ERR(alias)) {
					dput(dparent);
					dparent = NULL;
					goto out;
				}
				dchild = alias;
			}
		}
		cFYI(1, "parent %p, child %p", dparent, dchild);

		dput(dparent);
		dparent = dchild;
		len = 0;
		pstart = full_path + i + 1;
		full_path[i] = sep;
	}
out:
	_FreeXid(xid);
	kfree(full_path);
	return dparent;
}
Beispiel #13
0
/*
 * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
 *
 * Find the dentry that matches "name". If there isn't one, create one. If it's
 * a negative dentry or the uniqueid changed, then drop it and recreate it.
 */
static void
cifs_prime_dcache(struct dentry *parent, struct qstr *name,
		    struct cifs_fattr *fattr)
{
	struct dentry *dentry, *alias;
	struct inode *inode;
	struct super_block *sb = parent->d_inode->i_sb;
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);

	cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);

	dentry = d_hash_and_lookup(parent, name);
	if (unlikely(IS_ERR(dentry)))
		return;

	if (dentry) {
		int err;

		inode = dentry->d_inode;
		if (inode) {
			/*
			 * If we're generating inode numbers, then we don't
			 * want to clobber the existing one with the one that
			 * the readdir code created.
			 */
			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
				fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;

			/* update inode in place if i_ino didn't change */
			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
				cifs_fattr_to_inode(inode, fattr);
				goto out;
			}
		}
		err = d_invalidate(dentry);
		dput(dentry);
		if (err)
			return;
	}

	/*
	 * If we know that the inode will need to be revalidated immediately,
	 * then don't create a new dentry for it. We'll end up doing an on
	 * the wire call either way and this spares us an invalidation.
	 */
	if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
		return;

	dentry = d_alloc(parent, name);
	if (!dentry)
		return;

	inode = cifs_iget(sb, fattr);
	if (!inode)
		goto out;

	alias = d_materialise_unique(dentry, inode);
	if (alias && !IS_ERR(alias))
		dput(alias);
out:
	dput(dentry);
}
Beispiel #14
0
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;
	}
	fc = get_fuse_conn(dir);

	name.hash = full_name_hash(name.name, name.len);
	dentry = d_lookup(parent, &name);
	if (dentry && dentry->d_inode) {
		inode = dentry->d_inode;
		if (get_node_id(inode) == o->nodeid) {
			struct fuse_inode *fi;
			fi = get_fuse_inode(inode);
			spin_lock(&fc->lock);
			fi->nlookup++;
			spin_unlock(&fc->lock);

			/*
			 * The other branch to 'found' comes via fuse_iget()
			 * which bumps nlookup inside
			 */
			goto found;
		}
		err = d_invalidate(dentry);
		if (err)
			goto out;
		dput(dentry);
		dentry = NULL;
	}

	dentry = d_alloc(parent, &name);
	err = -ENOMEM;
	if (!dentry)
		goto out;

	inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
			  &o->attr, entry_attr_timeout(o), attr_version);
	if (!inode)
		goto out;

	alias = d_materialise_unique(dentry, inode);
	err = PTR_ERR(alias);
	if (IS_ERR(alias))
		goto out;
	if (alias) {
		dput(dentry);
		dentry = alias;
	}

found:
	fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o),
			       attr_version);

	fuse_change_entry_timeout(dentry, o);

	err = 0;
out:
	if (dentry)
		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 (!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 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;
	}
	req->out.argpages = 1;
	req->num_pages = 1;
	req->pages[0] = page;
	req->page_descs[0].length = PAGE_SIZE;
	if (fc->do_readdirplus) {
		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 (fc->do_readdirplus) {
			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;
}