/*
 * Code common to create, mkdir, and mknod.
 */
static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
				struct nfs_fattr *fattr)
{
	struct inode *inode;
	int error = -EACCES;

	/* We may have been initialized further down */
	if (dentry->d_inode)
		return 0;
	if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) {
		struct inode *dir = dentry->d_parent->d_inode;
		error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
		if (error)
			goto out_err;
	}
	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
	if (inode) {
		d_instantiate(dentry, inode);
		nfs_renew_times(dentry);
		nfs_set_verifier(dentry, nfs_save_change_attribute(dentry->d_parent->d_inode));
		return 0;
	}
	error = -ENOMEM;
out_err:
	d_drop(dentry);
	return error;
}
/*  We do silly rename. In case sillyrename() returns -EBUSY, the inode
 *  belongs to an active ".nfs..." file and we return -EBUSY.
 *
 *  If sillyrename() returns 0, we do nothing, otherwise we unlink.
 */
static int nfs_unlink(struct inode *dir, struct dentry *dentry)
{
	int error;
	int need_rehash = 0;

	dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
		dir->i_ino, dentry->d_name.name);

	lock_kernel();
	spin_lock(&dcache_lock);
	spin_lock(&dentry->d_lock);
	if (atomic_read(&dentry->d_count) > 1) {
		spin_unlock(&dentry->d_lock);
		spin_unlock(&dcache_lock);
		error = nfs_sillyrename(dir, dentry);
		unlock_kernel();
		return error;
	}
	if (!d_unhashed(dentry)) {
		__d_drop(dentry);
		need_rehash = 1;
	}
	spin_unlock(&dentry->d_lock);
	spin_unlock(&dcache_lock);
	error = nfs_safe_remove(dentry);
	if (!error) {
		nfs_renew_times(dentry);
		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
	} else if (need_rehash)
		d_rehash(dentry);
	unlock_kernel();
	return error;
}
static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
	struct inode *inode = NULL;
	int error = 0;

	/* Check that we are indeed trying to open this file */
	if (!is_atomic_open(dir, nd))
		goto no_open;

	if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
		error = -ENAMETOOLONG;
		goto out;
	}
	dentry->d_op = NFS_PROTO(dir)->dentry_ops;

	/* Let vfs_create() deal with O_EXCL */
	if (nd->intent.open.flags & O_EXCL)
		goto no_entry;

	/* Open the file on the server */
	lock_kernel();
	/* Revalidate parent directory attribute cache */
	nfs_revalidate_inode(NFS_SERVER(dir), dir);

	if (nd->intent.open.flags & O_CREAT) {
		nfs_begin_data_update(dir);
		inode = nfs4_atomic_open(dir, dentry, nd);
		nfs_end_data_update(dir);
	} else
		inode = nfs4_atomic_open(dir, dentry, nd);
	unlock_kernel();
	if (IS_ERR(inode)) {
		error = PTR_ERR(inode);
		switch (error) {
			/* Make a negative dentry */
			case -ENOENT:
				inode = NULL;
				break;
			/* This turned out not to be a regular file */
			case -ELOOP:
				if (!(nd->intent.open.flags & O_NOFOLLOW))
					goto no_open;
			/* case -EISDIR: */
			/* case -EINVAL: */
			default:
				goto out;
		}
	}
no_entry:
	d_add(dentry, inode);
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out:
	BUG_ON(error > 0);
	return ERR_PTR(error);
no_open:
	return nfs_lookup(dir, dentry, nd);
}
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
	struct inode *inode = NULL;
	int error;
	struct nfs_fh fhandle;
	struct nfs_fattr fattr;

	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name);

	error = -ENAMETOOLONG;
	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
		goto out;

	error = -ENOMEM;
	dentry->d_op = NFS_PROTO(dir)->dentry_ops;

	lock_kernel();
	/* Revalidate parent directory attribute cache */
	nfs_revalidate_inode(NFS_SERVER(dir), dir);

	/* If we're doing an exclusive create, optimize away the lookup */
	if (nfs_is_exclusive_create(dir, nd))
		goto no_entry;

	error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
	if (error != 0) {
		error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name,
				&fhandle, &fattr);
		if (error == -ENOENT)
			goto no_entry;
		if (error != 0)
			goto out_unlock;
	}
	error = -EACCES;
	inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
	if (!inode)
		goto out_unlock;
no_entry:
	error = 0;
	d_add(dentry, inode);
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_unlock:
	unlock_kernel();
out:
	BUG_ON(error > 0);
	return ERR_PTR(error);
}
static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
{
	struct dentry *parent = NULL;
	struct inode *inode = dentry->d_inode;
	struct inode *dir;
	unsigned long verifier;
	int openflags, ret = 0;

	/* NFS only supports OPEN for regular files */
	if (inode && !S_ISREG(inode->i_mode))
		goto no_open;
	parent = dget_parent(dentry);
	dir = parent->d_inode;
	if (!is_atomic_open(dir, nd))
		goto no_open;
	openflags = nd->intent.open.flags;
	if (openflags & O_CREAT) {
		/* If this is a negative dentry, just drop it */
		if (!inode)
			goto out;
		/* If this is exclusive open, just revalidate */
		if (openflags & O_EXCL)
			goto no_open;
	}
	/* We can't create new files, or truncate existing ones here */
	openflags &= ~(O_CREAT|O_TRUNC);

	/*
	 * Note: we're not holding inode->i_sem and so may be racing with
	 * operations that change the directory. We therefore save the
	 * change attribute *before* we do the RPC call.
	 */
	lock_kernel();
	verifier = nfs_save_change_attribute(dir);
	ret = nfs4_open_revalidate(dir, dentry, openflags);
	if (!ret)
		nfs_set_verifier(dentry, verifier);
	unlock_kernel();
out:
	dput(parent);
	if (!ret)
		d_drop(dentry);
	return ret;
no_open:
	dput(parent);
	return nfs_lookup_revalidate(dentry, nd);
}
/*
 * Following a failed create operation, we drop the dentry rather
 * than retain a negative dentry. This avoids a problem in the event
 * that the operation succeeded on the server, but an error in the
 * reply path made it appear to have failed.
 */
static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	struct iattr attr;
	struct inode *inode;
	int error;
	int open_flags = 0;

	dfprintk(VFS, "NFS: create(%s/%ld, %s\n", dir->i_sb->s_id, 
		dir->i_ino, dentry->d_name.name);

	attr.ia_mode = mode;
	attr.ia_valid = ATTR_MODE;

	if (nd && (nd->flags & LOOKUP_CREATE))
		open_flags = nd->intent.open.flags;

	/*
	 * The 0 argument passed into the create function should one day
	 * contain the O_EXCL flag if requested. This allows NFSv3 to
	 * select the appropriate create strategy. Currently open_namei
	 * does not pass the create flags.
	 */
	lock_kernel();
	nfs_begin_data_update(dir);
	inode = NFS_PROTO(dir)->create(dir, &dentry->d_name, &attr, open_flags);
	nfs_end_data_update(dir);
	if (!IS_ERR(inode)) {
		d_instantiate(dentry, inode);
		nfs_renew_times(dentry);
		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
		error = 0;
	} else {
		error = PTR_ERR(inode);
		d_drop(dentry);
	}
	unlock_kernel();
	return error;
}
static int
nfs4_file_open(struct inode *inode, struct file *filp)
{
	struct nfs_open_context *ctx;
	struct dentry *dentry = filp->f_path.dentry;
	struct dentry *parent = NULL;
	struct inode *dir;
	unsigned openflags = filp->f_flags;
	struct iattr attr;
	int opened = 0;
	int err;

	/*
	 * If no cached dentry exists or if it's negative, NFSv4 handled the
	 * opens in ->lookup() or ->create().
	 *
	 * We only get this far for a cached positive dentry.  We skipped
	 * revalidation, so handle it here by dropping the dentry and returning
	 * -EOPENSTALE.  The VFS will retry the lookup/create/open.
	 */

	dprintk("NFS: open file(%s/%s)\n",
		dentry->d_parent->d_name.name,
		dentry->d_name.name);

	if ((openflags & O_ACCMODE) == 3)
		openflags--;

	/* We can't create new files here */
	openflags &= ~(O_CREAT|O_EXCL);

	parent = dget_parent(dentry);
	dir = parent->d_inode;

	ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
	err = PTR_ERR(ctx);
	if (IS_ERR(ctx))
		goto out;

	attr.ia_valid = ATTR_OPEN;
	if (openflags & O_TRUNC) {
		attr.ia_valid |= ATTR_SIZE;
		attr.ia_size = 0;
		nfs_wb_all(inode);
	}

	inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, &opened);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		switch (err) {
		case -EPERM:
		case -EACCES:
		case -EDQUOT:
		case -ENOSPC:
		case -EROFS:
			goto out_put_ctx;
		default:
			goto out_drop;
		}
	}
	if (inode != dentry->d_inode)
		goto out_drop;

	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
	nfs_file_set_open_context(filp, ctx);
	nfs_fscache_open_file(inode, filp);
	err = 0;

out_put_ctx:
	put_nfs_open_context(ctx);
out:
	dput(parent);
	return err;

out_drop:
	d_drop(dentry);
	err = -EOPENSTALE;
	goto out_put_ctx;
}
Exemple #8
0
/**
 * nfs_sillyrename - Perform a silly-rename of a dentry
 * @dir: inode of directory that contains dentry
 * @dentry: dentry to be sillyrenamed
 *
 * NFSv2/3 is stateless and the server doesn't know when the client is
 * holding a file open. To prevent application problems when a file is
 * unlinked while it's still open, the client performs a "silly-rename".
 * That is, it renames the file to a hidden file in the same directory,
 * and only performs the unlink once the last reference to it is put.
 *
 * The final cleanup is done during dentry_iput.
 *
 * (Note: NFSv4 is stateful, and has opens, so in theory an NFSv4 server
 * could take responsibility for keeping open files referenced.  The server
 * would also need to ensure that opened-but-deleted files were kept over
 * reboots.  However, we may not assume a server does so.  (RFC 5661
 * does provide an OPEN4_RESULT_PRESERVE_UNLINKED flag that a server can
 * use to advertise that it does this; some day we may take advantage of
 * it.))
 */
int
nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{
    static unsigned int sillycounter;
    const int      fileidsize  = sizeof(NFS_FILEID(dentry->d_inode))*2;
    const int      countersize = sizeof(sillycounter)*2;
    const int      slen        = sizeof(".nfs")+fileidsize+countersize-1;
    char           silly[slen+1];
    struct dentry *sdentry;
    struct rpc_task *task;
    int            error = -EIO;

    dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
             dentry->d_parent->d_name.name, dentry->d_name.name,
             dentry->d_count);
    nfs_inc_stats(dir, NFSIOS_SILLYRENAME);

    /*
     * We don't allow a dentry to be silly-renamed twice.
     */
    error = -EBUSY;
    if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
        goto out;

    sprintf(silly, ".nfs%*.*Lx",
            fileidsize, fileidsize,
            (unsigned long long)NFS_FILEID(dentry->d_inode));

    /* Return delegation in anticipation of the rename */
    NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);

    sdentry = NULL;
    do {
        char *suffix = silly + slen - countersize;

        dput(sdentry);
        sillycounter++;
        sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);

        dfprintk(VFS, "NFS: trying to rename %s to %s\n",
                 dentry->d_name.name, silly);

        sdentry = lookup_one_len(silly, dentry->d_parent, slen);
        /*
         * N.B. Better to return EBUSY here ... it could be
         * dangerous to delete the file while it's in use.
         */
        if (IS_ERR(sdentry))
            goto out;
    } while (sdentry->d_inode != NULL); /* need negative lookup */

    /* queue unlink first. Can't do this from rpc_release as it
     * has to allocate memory
     */
    error = nfs_async_unlink(dir, dentry);
    if (error)
        goto out_dput;

    /* populate unlinkdata with the right dname */
    error = nfs_copy_dname(sdentry,
                           (struct nfs_unlinkdata *)dentry->d_fsdata);
    if (error) {
        nfs_cancel_async_unlink(dentry);
        goto out_dput;
    }

    /* run the rename task, undo unlink if it fails */
    task = nfs_async_rename(dir, dir, dentry, sdentry);
    if (IS_ERR(task)) {
        error = -EBUSY;
        nfs_cancel_async_unlink(dentry);
        goto out_dput;
    }

    /* wait for the RPC task to complete, unless a SIGKILL intervenes */
    error = rpc_wait_for_completion_task(task);
    if (error == 0)
        error = task->tk_status;
    switch (error) {
    case 0:
        /* The rename succeeded */
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        d_move(dentry, sdentry);
        break;
    case -ERESTARTSYS:
        /* The result of the rename is unknown. Play it safe by
         * forcing a new lookup */
        d_drop(dentry);
        d_drop(sdentry);
    }
    rpc_put_task(task);
out_dput:
    dput(sdentry);
out:
    return error;
}
/*
 * This is called every time the dcache has a lookup hit,
 * and we should check whether we can really trust that
 * lookup.
 *
 * NOTE! The hit can be a negative hit too, don't assume
 * we have an inode!
 *
 * If the parent directory is seen to have changed, we throw out the
 * cached dentry and do a new lookup.
 */
static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
{
	struct inode *dir;
	struct inode *inode;
	struct dentry *parent;
	int error;
	struct nfs_fh fhandle;
	struct nfs_fattr fattr;
	unsigned long verifier;
	int isopen = 0;

	parent = dget_parent(dentry);
	lock_kernel();
	dir = parent->d_inode;
	inode = dentry->d_inode;

	if (nd && !(nd->flags & LOOKUP_CONTINUE) && (nd->flags & LOOKUP_OPEN))
		isopen = 1;

	if (!inode) {
		if (nfs_neg_need_reval(dir, dentry, nd))
			goto out_bad;
		goto out_valid;
	}

	if (is_bad_inode(inode)) {
		dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
			dentry->d_parent->d_name.name, dentry->d_name.name);
		goto out_bad;
	}

	/* Revalidate parent directory attribute cache */
	nfs_revalidate_inode(NFS_SERVER(dir), dir);

	/* Force a full look up iff the parent directory has changed */
	if (nfs_check_verifier(dir, dentry)) {
		if (nfs_lookup_verify_inode(inode, isopen))
			goto out_zap_parent;
		goto out_valid;
	}

	/*
	 * Note: we're not holding inode->i_sem and so may be racing with
	 * operations that change the directory. We therefore save the
	 * change attribute *before* we do the RPC call.
	 */
	verifier = nfs_save_change_attribute(dir);
	error = nfs_cached_lookup(dir, dentry, &fhandle, &fattr);
	if (!error) {
		if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
			goto out_bad;
		if (nfs_lookup_verify_inode(inode, isopen))
			goto out_zap_parent;
		goto out_valid_renew;
	}

	if (NFS_STALE(inode))
		goto out_bad;

	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
	if (error)
		goto out_bad;
	if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
		goto out_bad;
	if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
		goto out_bad;

 out_valid_renew:
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, verifier);
 out_valid:
	unlock_kernel();
	dput(parent);
	return 1;
out_zap_parent:
	nfs_zap_caches(dir);
 out_bad:
	NFS_CACHEINV(dir);
	if (inode && S_ISDIR(inode->i_mode)) {
		/* Purge readdir caches. */
		nfs_zap_caches(inode);
		/* If we have submounts, don't unhash ! */
		if (have_submounts(dentry))
			goto out_valid;
		shrink_dcache_parent(dentry);
	}
	d_drop(dentry);
	unlock_kernel();
	dput(parent);
	return 0;
}
/*
 * RENAME
 * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
 * different file handle for the same inode after a rename (e.g. when
 * moving to a different directory). A fail-safe method to do so would
 * be to look up old_dir/old_name, create a link to new_dir/new_name and
 * rename the old file using the sillyrename stuff. This way, the original
 * file in old_dir will go away when the last process iput()s the inode.
 *
 * FIXED.
 * 
 * It actually works quite well. One needs to have the possibility for
 * at least one ".nfs..." file in each directory the file ever gets
 * moved or linked to which happens automagically with the new
 * implementation that only depends on the dcache stuff instead of
 * using the inode layer
 *
 * Unfortunately, things are a little more complicated than indicated
 * above. For a cross-directory move, we want to make sure we can get
 * rid of the old inode after the operation.  This means there must be
 * no pending writes (if it's a file), and the use count must be 1.
 * If these conditions are met, we can drop the dentries before doing
 * the rename.
 */
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
		      struct inode *new_dir, struct dentry *new_dentry)
{
	struct inode *old_inode = old_dentry->d_inode;
	struct inode *new_inode = new_dentry->d_inode;
	struct dentry *dentry = NULL, *rehash = NULL;
	int error = -EBUSY;

	/*
	 * To prevent any new references to the target during the rename,
	 * we unhash the dentry and free the inode in advance.
	 */
	lock_kernel();
	if (!d_unhashed(new_dentry)) {
		d_drop(new_dentry);
		rehash = new_dentry;
	}

	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
		 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
		 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
		 atomic_read(&new_dentry->d_count));

	/*
	 * First check whether the target is busy ... we can't
	 * safely do _any_ rename if the target is in use.
	 *
	 * For files, make a copy of the dentry and then do a 
	 * silly-rename. If the silly-rename succeeds, the
	 * copied dentry is hashed and becomes the new target.
	 */
	if (!new_inode)
		goto go_ahead;
	if (S_ISDIR(new_inode->i_mode))
		goto out;
	else if (atomic_read(&new_dentry->d_count) > 1) {
		int err;
		/* copy the target dentry's name */
		dentry = d_alloc(new_dentry->d_parent,
				 &new_dentry->d_name);
		if (!dentry)
			goto out;

		/* silly-rename the existing target ... */
		err = nfs_sillyrename(new_dir, new_dentry);
		if (!err) {
			new_dentry = rehash = dentry;
			new_inode = NULL;
			/* instantiate the replacement target */
			d_instantiate(new_dentry, NULL);
		}

		/* dentry still busy? */
		if (atomic_read(&new_dentry->d_count) > 1) {
#ifdef NFS_PARANOIA
			printk("nfs_rename: target %s/%s busy, d_count=%d\n",
			       new_dentry->d_parent->d_name.name,
			       new_dentry->d_name.name,
			       atomic_read(&new_dentry->d_count));
#endif
			goto out;
		}
	}

go_ahead:
	/*
	 * ... prune child dentries and writebacks if needed.
	 */
	if (atomic_read(&old_dentry->d_count) > 1) {
		nfs_wb_all(old_inode);
		shrink_dcache_parent(old_dentry);
	}

	if (new_inode)
		d_delete(new_dentry);

	nfs_begin_data_update(old_dir);
	nfs_begin_data_update(new_dir);
	nfs_begin_data_update(old_inode);
	error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
					   new_dir, &new_dentry->d_name);
	nfs_end_data_update(old_inode);
	nfs_end_data_update(new_dir);
	nfs_end_data_update(old_dir);
out:
	if (rehash)
		d_rehash(rehash);
	if (!error) {
		if (!S_ISDIR(old_inode->i_mode))
			d_move(old_dentry, new_dentry);
		nfs_renew_times(new_dentry);
		nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir));
	}

	/* new dentry created? */
	if (dentry)
		dput(dentry);
	unlock_kernel();
	return error;
}
static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{
	static unsigned int sillycounter;
	const int      i_inosize  = sizeof(dir->i_ino)*2;
	const int      countersize = sizeof(sillycounter)*2;
	const int      slen       = strlen(".nfs") + i_inosize + countersize;
	char           silly[slen+1];
	struct qstr    qsilly;
	struct dentry *sdentry;
	int            error = -EIO;

	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name, 
		atomic_read(&dentry->d_count));

#ifdef NFS_PARANOIA
if (!dentry->d_inode)
printk("NFS: silly-renaming %s/%s, negative dentry??\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
#endif
	/*
	 * We don't allow a dentry to be silly-renamed twice.
	 */
	error = -EBUSY;
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out;

	sprintf(silly, ".nfs%*.*lx",
		i_inosize, i_inosize, dentry->d_inode->i_ino);

	sdentry = NULL;
	do {
		char *suffix = silly + slen - countersize;

		dput(sdentry);
		sillycounter++;
		sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);

		dfprintk(VFS, "trying to rename %s to %s\n",
			 dentry->d_name.name, silly);
		
		sdentry = lookup_one_len(silly, dentry->d_parent, slen);
		/*
		 * N.B. Better to return EBUSY here ... it could be
		 * dangerous to delete the file while it's in use.
		 */
		if (IS_ERR(sdentry))
			goto out;
	} while(sdentry->d_inode != NULL); /* need negative lookup */

	qsilly.name = silly;
	qsilly.len  = strlen(silly);
	nfs_begin_data_update(dir);
	if (dentry->d_inode) {
		nfs_begin_data_update(dentry->d_inode);
		error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
				dir, &qsilly);
		nfs_end_data_update(dentry->d_inode);
	} else
		error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
				dir, &qsilly);
	nfs_end_data_update(dir);
	if (!error) {
		nfs_renew_times(dentry);
		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
		d_move(dentry, sdentry);
		error = nfs_async_unlink(dentry);
 		/* If we return 0 we don't unlink */
	}
	dput(sdentry);
out:
	return error;
}
Exemple #12
0
int
nfs_sillyrename(struct inode *dir, struct dentry *dentry)
{
	static unsigned int sillycounter;
	const int      fileidsize  = sizeof(NFS_FILEID(dentry->d_inode))*2;
	const int      countersize = sizeof(sillycounter)*2;
	const int      slen        = sizeof(".nfs")+fileidsize+countersize-1;
	char           silly[slen+1];
	struct dentry *sdentry;
	struct rpc_task *task;
	int            error = -EIO;

	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
		dentry->d_parent->d_name.name, dentry->d_name.name,
		dentry->d_count);
	nfs_inc_stats(dir, NFSIOS_SILLYRENAME);

	error = -EBUSY;
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out;

	sprintf(silly, ".nfs%*.*Lx",
		fileidsize, fileidsize,
		(unsigned long long)NFS_FILEID(dentry->d_inode));

	
	nfs_inode_return_delegation(dentry->d_inode);

	sdentry = NULL;
	do {
		char *suffix = silly + slen - countersize;

		dput(sdentry);
		sillycounter++;
		sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);

		dfprintk(VFS, "NFS: trying to rename %s to %s\n",
				dentry->d_name.name, silly);

		sdentry = lookup_one_len(silly, dentry->d_parent, slen);
		if (IS_ERR(sdentry))
			goto out;
	} while (sdentry->d_inode != NULL); 

	error = nfs_async_unlink(dir, dentry);
	if (error)
		goto out_dput;

	
	error = nfs_copy_dname(sdentry,
				(struct nfs_unlinkdata *)dentry->d_fsdata);
	if (error) {
		nfs_cancel_async_unlink(dentry);
		goto out_dput;
	}

	
	task = nfs_async_rename(dir, dir, dentry, sdentry);
	if (IS_ERR(task)) {
		error = -EBUSY;
		nfs_cancel_async_unlink(dentry);
		goto out_dput;
	}

	
	error = rpc_wait_for_completion_task(task);
	if (error == 0)
		error = task->tk_status;
	switch (error) {
	case 0:
		/* The rename succeeded */
		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
		d_move(dentry, sdentry);
		break;
	case -ERESTARTSYS:
		/* The result of the rename is unknown. Play it safe by
		 * forcing a new lookup */
		d_drop(dentry);
		d_drop(sdentry);
	}
	rpc_put_task(task);
out_dput:
	dput(sdentry);
out:
	return error;
}