Beispiel #1
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;
}
Beispiel #2
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;
}