/** * nfs_free_unlinkdata - release data from a sillydelete operation. * @data: pointer to unlink structure.
*/ staticvoid
nfs_free_unlinkdata(struct nfs_unlinkdata *data)
{
put_cred(data->cred);
kfree(data->args.name.name);
kfree(data);
}
/** * nfs_async_unlink_done - Sillydelete post-processing * @task: rpc_task of the sillydelete * @calldata: pointer to nfs_unlinkdata * * Do the directory attribute update.
*/ staticvoid nfs_async_unlink_done(struct rpc_task *task, void *calldata)
{ struct nfs_unlinkdata *data = calldata; struct inode *dir = d_inode(data->dentry->d_parent);
trace_nfs_sillyrename_unlink(data, task->tk_status); if (!NFS_PROTO(dir)->unlink_done(task, dir))
rpc_restart_call_prepare(task);
}
/** * nfs_async_unlink_release - Release the sillydelete data. * @calldata: struct nfs_unlinkdata to release * * We need to call nfs_put_unlinkdata as a 'tk_release' task since the * rpc_task would be freed too.
*/ staticvoid nfs_async_unlink_release(void *calldata)
{ struct nfs_unlinkdata *data = calldata; struct dentry *dentry = data->dentry; struct super_block *sb = dentry->d_sb;
down_read_non_owner(&NFS_I(dir)->rmdir_sem);
alias = d_alloc_parallel(dentry->d_parent, &data->args.name, &data->wq); if (IS_ERR(alias)) {
up_read_non_owner(&NFS_I(dir)->rmdir_sem); return 0;
} if (!d_in_lookup(alias)) { int ret; void *devname_garbage = NULL;
/* * Hey, we raced with lookup... See if we need to transfer * the sillyrename information to the aliased dentry.
*/
spin_lock(&alias->d_lock); if (d_really_is_positive(alias) &&
!nfs_compare_fh(NFS_FH(inode), NFS_FH(d_inode(alias))) &&
!(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
devname_garbage = alias->d_fsdata;
alias->d_fsdata = data;
alias->d_flags |= DCACHE_NFSFS_RENAMED;
ret = 1;
} else
ret = 0;
spin_unlock(&alias->d_lock);
dput(alias);
up_read_non_owner(&NFS_I(dir)->rmdir_sem); /* * If we'd displaced old cached devname, free it. At that * point dentry is definitely not a root, so we won't need * that anymore.
*/
kfree(devname_garbage); return ret;
}
data->dentry = alias;
nfs_do_call_unlink(inode, data); return 1;
}
/** * nfs_async_unlink - asynchronous unlinking of a file * @dentry: parent directory of dentry * @name: name of dentry to unlink
*/ staticint
nfs_async_unlink(struct dentry *dentry, conststruct qstr *name)
{ struct nfs_unlinkdata *data; int status = -ENOMEM; void *devname_garbage = NULL;
data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) goto out;
data->args.name.name = kstrdup(name->name, GFP_KERNEL); if (!data->args.name.name) goto out_free;
data->args.name.len = name->len;
status = -EBUSY;
spin_lock(&dentry->d_lock); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto out_unlock;
dentry->d_flags |= DCACHE_NFSFS_RENAMED;
devname_garbage = dentry->d_fsdata;
dentry->d_fsdata = data;
spin_unlock(&dentry->d_lock); /* * If we'd displaced old cached devname, free it. At that * point dentry is definitely not a root, so we won't need * that anymore.
*/
kfree(devname_garbage); return 0;
out_unlock:
spin_unlock(&dentry->d_lock);
put_cred(data->cred);
kfree(data->args.name.name);
out_free:
kfree(data);
out: return status;
}
/** * nfs_complete_unlink - Initialize completion of the sillydelete * @dentry: dentry to delete * @inode: inode * * Since we're most likely to be called by dentry_iput(), we * only use the dentry to find the sillydelete. We then copy the name * into the qstr.
*/ void
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
{ struct nfs_unlinkdata *data;
if (NFS_STALE(inode) || !nfs_call_unlink(dentry, inode, data))
nfs_free_unlinkdata(data);
}
/* Cancel a queued async unlink. Called when a sillyrename run fails. */ staticvoid
nfs_cancel_async_unlink(struct dentry *dentry)
{
spin_lock(&dentry->d_lock); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { struct nfs_unlinkdata *data = dentry->d_fsdata;
/** * nfs_async_rename_release - Release the sillyrename data. * @calldata: the struct nfs_renamedata to be released
*/ staticvoid nfs_async_rename_release(void *calldata)
{ struct nfs_renamedata *data = calldata; struct super_block *sb = data->old_dir->i_sb;
if (d_really_is_positive(data->old_dentry))
nfs_mark_for_revalidate(d_inode(data->old_dentry));
/* The result of the rename is unknown. Play it safe by
* forcing a new lookup */ if (data->cancelled) {
spin_lock(&data->old_dir->i_lock);
nfs_force_lookup_revalidate(data->old_dir);
spin_unlock(&data->old_dir->i_lock); if (data->new_dir != data->old_dir) {
spin_lock(&data->new_dir->i_lock);
nfs_force_lookup_revalidate(data->new_dir);
spin_unlock(&data->new_dir->i_lock);
}
}
/** * nfs_async_rename - perform an asynchronous rename operation * @old_dir: directory that currently holds the dentry to be renamed * @new_dir: target directory for the rename * @old_dentry: original dentry to be renamed * @new_dentry: dentry to which the old_dentry should be renamed * @complete: Function to run on successful completion * * It's expected that valid references to the dentries and inodes are held
*/ struct rpc_task *
nfs_async_rename(struct inode *old_dir, struct inode *new_dir, struct dentry *old_dentry, struct dentry *new_dentry, void (*complete)(struct rpc_task *, struct nfs_renamedata *))
{ struct nfs_renamedata *data; struct rpc_message msg = { }; struct rpc_task_setup task_setup_data = {
.rpc_message = &msg,
.callback_ops = &nfs_rename_ops,
.workqueue = nfsiod_workqueue,
.rpc_client = NFS_CLIENT(old_dir),
.flags = RPC_TASK_ASYNC | RPC_TASK_CRED_NOREF,
};
if (nfs_server_capable(old_dir, NFS_CAP_MOVEABLE) &&
nfs_server_capable(new_dir, NFS_CAP_MOVEABLE))
task_setup_data.flags |= RPC_TASK_MOVEABLE;
data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) return ERR_PTR(-ENOMEM);
task_setup_data.task = &data->task;
task_setup_data.callback_data = data;
/* * Perform tasks needed when a sillyrename is done such as cancelling the * queued async unlink if it failed.
*/ staticvoid
nfs_complete_sillyrename(struct rpc_task *task, struct nfs_renamedata *data)
{ struct dentry *dentry = data->old_dentry;
if (task->tk_status != 0) {
nfs_cancel_async_unlink(dentry); return;
}
}
/** * 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)
{ staticunsignedint sillycounter; unsignedchar silly[SILLYNAME_LEN + 1]; unsignedlonglong fileid; struct dentry *sdentry; struct inode *inode = d_inode(dentry); struct rpc_task *task; int error = -EBUSY;
dfprintk(VFS, "NFS: trying to rename %pd to %s\n",
dentry, silly);
sdentry = lookup_noperm(&QSTR(silly), dentry->d_parent); /* * 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 (d_inode(sdentry) != NULL); /* need negative lookup */
ihold(inode);
/* queue unlink first. Can't do this from rpc_release as it * has to allocate memory
*/
error = nfs_async_unlink(dentry, &sdentry->d_name); if (error) goto out_dput;
/* run the rename task, undo unlink if it fails */
task = nfs_async_rename(dir, dir, dentry, sdentry,
nfs_complete_sillyrename); 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));
spin_lock(&inode->i_lock);
NFS_I(inode)->attr_gencount = nfs_inc_attr_generation_counter();
nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
NFS_INO_INVALID_CTIME |
NFS_INO_REVAL_FORCED);
spin_unlock(&inode->i_lock);
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:
iput(inode);
dput(sdentry);
out: return error;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.