Пример #1
0
/*
 * read an AFS directory
 */
static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir)
{
	unsigned fpos;
	int ret;

	_enter("{%Ld,{%lu}}", file->f_pos, file->f_path.dentry->d_inode->i_ino);

	fpos = file->f_pos;
	ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, cookie, filldir);
	file->f_pos = fpos;

	_leave(" = %d", ret);
	return ret;
} /* end afs_dir_readdir() */
Пример #2
0
/*
 * do a lookup in a directory
 * - just returns the FID the dentry name maps to if found
 */
static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
			 struct afs_fid *fid, struct key *key)
{
	struct afs_lookup_cookie cookie;
	struct afs_super_info *as;
	unsigned fpos;
	int ret;

	_enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name);

	as = dir->i_sb->s_fs_info;

	/* search the directory */
	cookie.name	= dentry->d_name.name;
	cookie.nlen	= dentry->d_name.len;
	cookie.fid.vid	= as->volume->vid;
	cookie.found	= 0;

	fpos = 0;
	ret = afs_dir_iterate(dir, &fpos, &cookie, afs_lookup_filldir,
			      key);
	if (ret < 0) {
		_leave(" = %d [iter]", ret);
		return ret;
	}

	ret = -ENOENT;
	if (!cookie.found) {
		_leave(" = -ENOENT [not found]");
		return -ENOENT;
	}

	*fid = cookie.fid;
	_leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique);
	return 0;
}
Пример #3
0
/*
 * do a lookup in a directory
 * - just returns the FID the dentry name maps to if found
 */
static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
			 struct afs_fid *fid, struct key *key)
{
	struct afs_super_info *as = dir->i_sb->s_fs_info;
	struct afs_lookup_cookie cookie = {
		.ctx.actor = afs_lookup_filldir,
		.name = dentry->d_name,
		.fid.vid = as->volume->vid
	};
	int ret;

	_enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);

	/* search the directory */
	ret = afs_dir_iterate(dir, &cookie.ctx, key);
	if (ret < 0) {
		_leave(" = %d [iter]", ret);
		return ret;
	}

	ret = -ENOENT;
	if (!cookie.found) {
		_leave(" = -ENOENT [not found]");
		return -ENOENT;
	}

	*fid = cookie.fid;
	_leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique);
	return 0;
}

/*
 * Try to auto mount the mountpoint with pseudo directory, if the autocell
 * operation is setted.
 */
static struct inode *afs_try_auto_mntpt(
	int ret, struct dentry *dentry, struct inode *dir, struct key *key,
	struct afs_fid *fid)
{
	const char *devname = dentry->d_name.name;
	struct afs_vnode *vnode = AFS_FS_I(dir);
	struct inode *inode;

	_enter("%d, %p{%pd}, {%x:%u}, %p",
	       ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key);

	if (ret != -ENOENT ||
	    !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
		goto out;

	inode = afs_iget_autocell(dir, devname, strlen(devname), key);
	if (IS_ERR(inode)) {
		ret = PTR_ERR(inode);
		goto out;
	}

	*fid = AFS_FS_I(inode)->fid;
	_leave("= %p", inode);
	return inode;

out:
	_leave("= %d", ret);
	return ERR_PTR(ret);
}

/*
 * look up an entry in a directory
 */
static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
				 unsigned int flags)
{
	struct afs_vnode *vnode;
	struct afs_fid fid;
	struct inode *inode;
	struct key *key;
	int ret;

	vnode = AFS_FS_I(dir);

	_enter("{%x:%u},%p{%pd},",
	       vnode->fid.vid, vnode->fid.vnode, dentry, dentry);

	ASSERTCMP(d_inode(dentry), ==, NULL);

	if (dentry->d_name.len >= AFSNAMEMAX) {
		_leave(" = -ENAMETOOLONG");
		return ERR_PTR(-ENAMETOOLONG);
	}

	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
		_leave(" = -ESTALE");
		return ERR_PTR(-ESTALE);
	}

	key = afs_request_key(vnode->volume->cell);
	if (IS_ERR(key)) {
		_leave(" = %ld [key]", PTR_ERR(key));
		return ERR_CAST(key);
	}

	ret = afs_validate(vnode, key);
	if (ret < 0) {
		key_put(key);
		_leave(" = %d [val]", ret);
		return ERR_PTR(ret);
	}

	ret = afs_do_lookup(dir, dentry, &fid, key);
	if (ret < 0) {
		inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid);
		if (!IS_ERR(inode)) {
			key_put(key);
			goto success;
		}

		ret = PTR_ERR(inode);
		key_put(key);
		if (ret == -ENOENT) {
			d_add(dentry, NULL);
			_leave(" = NULL [negative]");
			return NULL;
		}
		_leave(" = %d [do]", ret);
		return ERR_PTR(ret);
	}
	dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version;

	/* instantiate the dentry */
	inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL);
	key_put(key);
	if (IS_ERR(inode)) {
		_leave(" = %ld", PTR_ERR(inode));
		return ERR_CAST(inode);
	}

success:
	d_add(dentry, inode);
	_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%u }",
	       fid.vnode,
	       fid.unique,
	       d_inode(dentry)->i_ino,
	       d_inode(dentry)->i_generation);

	return NULL;
}

/*
 * check that a dentry lookup hit has found a valid entry
 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
 *   inode
 */
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct afs_vnode *vnode, *dir;
	struct afs_fid uninitialized_var(fid);
	struct dentry *parent;
	struct key *key;
	void *dir_version;
	int ret;

	if (flags & LOOKUP_RCU)
		return -ECHILD;

	vnode = AFS_FS_I(d_inode(dentry));

	if (d_really_is_positive(dentry))
		_enter("{v={%x:%u} n=%pd fl=%lx},",
		       vnode->fid.vid, vnode->fid.vnode, dentry,
		       vnode->flags);
	else
		_enter("{neg n=%pd}", dentry);

	key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell);
	if (IS_ERR(key))
		key = NULL;

	/* lock down the parent dentry so we can peer at it */
	parent = dget_parent(dentry);
	dir = AFS_FS_I(d_inode(parent));

	/* validate the parent directory */
	if (test_bit(AFS_VNODE_MODIFIED, &dir->flags))
		afs_validate(dir, key);

	if (test_bit(AFS_VNODE_DELETED, &dir->flags)) {
		_debug("%pd: parent dir deleted", dentry);
		goto out_bad;
	}

	dir_version = (void *) (unsigned long) dir->status.data_version;
	if (dentry->d_fsdata == dir_version)
		goto out_valid; /* the dir contents are unchanged */

	_debug("dir modified");

	/* search the directory for this vnode */
	ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key);
	switch (ret) {
	case 0:
		/* the filename maps to something */
		if (d_really_is_negative(dentry))
			goto out_bad;
		if (is_bad_inode(d_inode(dentry))) {
			printk("kAFS: afs_d_revalidate: %pd2 has bad inode\n",
			       dentry);
			goto out_bad;
		}

		/* if the vnode ID has changed, then the dirent points to a
		 * different file */
		if (fid.vnode != vnode->fid.vnode) {
			_debug("%pd: dirent changed [%u != %u]",
			       dentry, fid.vnode,
			       vnode->fid.vnode);
			goto not_found;
		}

		/* if the vnode ID uniqifier has changed, then the file has
		 * been deleted and replaced, and the original vnode ID has
		 * been reused */
		if (fid.unique != vnode->fid.unique) {
			_debug("%pd: file deleted (uq %u -> %u I:%u)",
			       dentry, fid.unique,
			       vnode->fid.unique,
			       d_inode(dentry)->i_generation);
			spin_lock(&vnode->lock);
			set_bit(AFS_VNODE_DELETED, &vnode->flags);
			spin_unlock(&vnode->lock);
			goto not_found;
		}
		goto out_valid;

	case -ENOENT:
		/* the filename is unknown */
		_debug("%pd: dirent not found", dentry);
		if (d_really_is_positive(dentry))
			goto not_found;
		goto out_valid;

	default:
		_debug("failed to iterate dir %pd: %d",
		       parent, ret);
		goto out_bad;
	}

out_valid:
	dentry->d_fsdata = dir_version;
	dput(parent);
	key_put(key);
	_leave(" = 1 [valid]");
	return 1;

	/* the dirent, if it exists, now points to a different vnode */
not_found:
	spin_lock(&dentry->d_lock);
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
	spin_unlock(&dentry->d_lock);

out_bad:
	_debug("dropping dentry %pd2", dentry);
	dput(parent);
	key_put(key);

	_leave(" = 0 [bad]");
	return 0;
}

/*
 * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
 * sleep)
 * - called from dput() when d_count is going to 0.
 * - return 1 to request dentry be unhashed, 0 otherwise
 */
static int afs_d_delete(const struct dentry *dentry)
{
	_enter("%pd", dentry);

	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto zap;

	if (d_really_is_positive(dentry) &&
	    (test_bit(AFS_VNODE_DELETED,   &AFS_FS_I(d_inode(dentry))->flags) ||
	     test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(d_inode(dentry))->flags)))
		goto zap;

	_leave(" = 0 [keep]");
	return 0;

zap:
	_leave(" = 1 [zap]");
	return 1;
}
Пример #4
0
/*
 * read an AFS directory
 */
static int afs_readdir(struct file *file, struct dir_context *ctx)
{
	return afs_dir_iterate(file_inode(file), 
			      ctx, file->private_data);
}
Пример #5
0
/*
 * check that a dentry lookup hit has found a valid entry
 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
 *   inode
 * (derived from nfs_lookup_revalidate)
 */
static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
{
	struct afs_dir_lookup_cookie cookie;
	struct dentry *parent;
	struct inode *inode, *dir;
	unsigned fpos;
	int ret;

	_enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name);

	/* lock down the parent dentry so we can peer at it */
	parent = dget_parent(dentry->d_parent);

	dir = parent->d_inode;
	inode = dentry->d_inode;

	/* handle a negative dentry */
	if (!inode)
		goto out_bad;

	/* handle a bad inode */
	if (is_bad_inode(inode)) {
		printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n",
		       dentry->d_parent->d_name.name, dentry->d_name.name);
		goto out_bad;
	}

	/* force a full look up if the parent directory changed since last the
	 * server was consulted
	 * - otherwise this inode must still exist, even if the inode details
	 *   themselves have changed
	 */
	if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED)
		afs_vnode_fetch_status(AFS_FS_I(dir));

	if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) {
		_debug("%s: parent dir deleted", dentry->d_name.name);
		goto out_bad;
	}

	if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) {
		_debug("%s: file already deleted", dentry->d_name.name);
		goto out_bad;
	}

	if ((unsigned long) dentry->d_fsdata !=
	    (unsigned long) AFS_FS_I(dir)->status.version) {
		_debug("%s: parent changed %lu -> %u",
		       dentry->d_name.name,
		       (unsigned long) dentry->d_fsdata,
		       (unsigned) AFS_FS_I(dir)->status.version);

		/* search the directory for this vnode */
		cookie.name	= dentry->d_name.name;
		cookie.nlen	= dentry->d_name.len;
		cookie.fid.vid	= AFS_FS_I(inode)->volume->vid;
		cookie.found	= 0;

		fpos = 0;
		ret = afs_dir_iterate(dir, &fpos, &cookie,
				      afs_dir_lookup_filldir);
		if (ret < 0) {
			_debug("failed to iterate dir %s: %d",
			       parent->d_name.name, ret);
			goto out_bad;
		}

		if (!cookie.found) {
			_debug("%s: dirent not found", dentry->d_name.name);
			goto not_found;
		}

		/* if the vnode ID has changed, then the dirent points to a
		 * different file */
		if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) {
			_debug("%s: dirent changed", dentry->d_name.name);
			goto not_found;
		}

		/* if the vnode ID uniqifier has changed, then the file has
		 * been deleted */
		if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) {
			_debug("%s: file deleted (uq %u -> %u I:%lu)",
			       dentry->d_name.name,
			       cookie.fid.unique,
			       AFS_FS_I(inode)->fid.unique,
			       inode->i_version);
			spin_lock(&AFS_FS_I(inode)->lock);
			AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED;
			spin_unlock(&AFS_FS_I(inode)->lock);
			invalidate_remote_inode(inode);
			goto out_bad;
		}

		dentry->d_fsdata =
			(void *) (unsigned long) AFS_FS_I(dir)->status.version;
	}

 out_valid:
	dput(parent);
	_leave(" = 1 [valid]");
	return 1;

	/* the dirent, if it exists, now points to a different vnode */
 not_found:
	spin_lock(&dentry->d_lock);
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
	spin_unlock(&dentry->d_lock);

 out_bad:
	if (inode) {
		/* don't unhash if we have submounts */
		if (have_submounts(dentry))
			goto out_valid;
	}

	shrink_dcache_parent(dentry);

	_debug("dropping dentry %s/%s",
	       dentry->d_parent->d_name.name, dentry->d_name.name);
	d_drop(dentry);

	dput(parent);

	_leave(" = 0 [bad]");
	return 0;
} /* end afs_d_revalidate() */
Пример #6
0
/*
 * look up an entry in a directory
 */
static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry,
				     struct nameidata *nd)
{
	struct afs_dir_lookup_cookie cookie;
	struct afs_super_info *as;
	struct afs_vnode *vnode;
	struct inode *inode;
	unsigned fpos;
	int ret;

	_enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name);

	/* insanity checks first */
	BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048);
	BUILD_BUG_ON(sizeof(union afs_dirent) != 32);

	if (dentry->d_name.len > 255) {
		_leave(" = -ENAMETOOLONG");
		return ERR_PTR(-ENAMETOOLONG);
	}

	vnode = AFS_FS_I(dir);
	if (vnode->flags & AFS_VNODE_DELETED) {
		_leave(" = -ESTALE");
		return ERR_PTR(-ESTALE);
	}

	as = dir->i_sb->s_fs_info;

	/* search the directory */
	cookie.name	= dentry->d_name.name;
	cookie.nlen	= dentry->d_name.len;
	cookie.fid.vid	= as->volume->vid;
	cookie.found	= 0;

	fpos = 0;
	ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir);
	if (ret < 0) {
		_leave(" = %d", ret);
		return ERR_PTR(ret);
	}

	ret = -ENOENT;
	if (!cookie.found) {
		_leave(" = %d", ret);
		return ERR_PTR(ret);
	}

	/* instantiate the dentry */
	ret = afs_iget(dir->i_sb, &cookie.fid, &inode);
	if (ret < 0) {
		_leave(" = %d", ret);
		return ERR_PTR(ret);
	}

	dentry->d_op = &afs_fs_dentry_operations;
	dentry->d_fsdata = (void *) (unsigned long) vnode->status.version;

	d_add(dentry, inode);
	_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }",
	       cookie.fid.vnode,
	       cookie.fid.unique,
	       dentry->d_inode->i_ino,
	       dentry->d_inode->i_version);

	return NULL;
} /* end afs_dir_lookup() */