diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-10-05 01:18:40 +0000 |
commit | 012bb3e61e5eced6c610f9e036372bf0c8def2d1 (patch) | |
tree | 87efc733f9b164e8c85c0336f92c8fb7eff6d183 /fs/nfs | |
parent | 625a1589d3d6464b5d90b8a0918789e3afffd220 (diff) |
Merge with Linux 2.4.0-test9. Please check DECstation, I had a number
of rejects to fixup while integrating Linus patches. I also found
that this kernel will only boot SMP on Origin; the UP kernel freeze
soon after bootup with SCSI timeout messages. I commit this anyway
since I found that the last CVS versions had the same problem.
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/Makefile | 2 | ||||
-rw-r--r-- | fs/nfs/dir.c | 30 | ||||
-rw-r--r-- | fs/nfs/file.c | 7 | ||||
-rw-r--r-- | fs/nfs/inode.c | 1 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 34 | ||||
-rw-r--r-- | fs/nfs/nfsroot.c | 1 | ||||
-rw-r--r-- | fs/nfs/proc.c | 27 | ||||
-rw-r--r-- | fs/nfs/unlink.c | 216 |
8 files changed, 298 insertions, 20 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 64ef9274b..52283d234 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -9,7 +9,7 @@ O_TARGET := nfs.o O_OBJS := inode.o file.o read.o write.o dir.o symlink.o proc.o \ - nfs2xdr.o flushd.o + nfs2xdr.o flushd.o unlink.o ifdef CONFIG_ROOT_NFS O_OBJS += nfsroot.o mount_clnt.o diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 11b8e5d79..ec4540f12 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -606,16 +606,8 @@ static void nfs_dentry_release(struct dentry *dentry) static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode) { if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { - struct dentry *dir = dentry->d_parent; - struct inode *dir_i = dir->d_inode; - int error; - lock_kernel(); - dir = dentry->d_parent; - dir_i = dir->d_inode; - nfs_zap_caches(dir_i); - NFS_CACHEINV(inode); - error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); + nfs_complete_unlink(dentry); unlock_kernel(); } iput(inode); @@ -817,14 +809,9 @@ static int nfs_sillyrename(struct inode *dir_i, struct dentry *dentry) dentry->d_parent->d_name.name, dentry->d_name.name, atomic_read(&dentry->d_count)); - /* - * Note that a silly-renamed file can be deleted once it's - * no longer in use -- it's just an ordinary file now. - */ - if (atomic_read(&dentry->d_count) == 1) { - dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + if (atomic_read(&dentry->d_count) == 1) goto out; /* No need to silly rename. */ - } + #ifdef NFS_PARANOIA if (!dentry->d_inode) @@ -868,7 +855,7 @@ dentry->d_parent->d_name.name, dentry->d_name.name); if (!error) { nfs_renew_times(dentry); d_move(dentry, sdentry); - dentry->d_flags |= DCACHE_NFSFS_RENAMED; + error = nfs_async_unlink(dentry); /* If we return 0 we don't unlink */ } dput(sdentry); @@ -908,12 +895,21 @@ static int nfs_safe_remove(struct dentry *dentry) #endif goto out; } + + /* If the dentry was sillyrenamed, we simply call d_delete() */ + if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { + error = 0; + goto out_delete; + } + nfs_zap_caches(dir_i); if (inode) NFS_CACHEINV(inode); error = NFS_PROTO(dir_i)->remove(dir, &dentry->d_name); if (error < 0) goto out; + + out_delete: /* * Free the inode */ diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 06f067eea..21ae090a3 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -291,10 +291,13 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) status = 0; /* - * Make sure we re-validate anything we've got cached. + * Make sure we clear the cache whenever we try to get the lock. * This makes locking act as a cache coherency point. */ out_ok: - NFS_CACHEINV(inode); + if ((cmd == F_SETLK || cmd == F_SETLKW) && fl->fl_type != F_UNLCK) { + nfs_wb_all(inode); /* we may have slept */ + nfs_zap_caches(inode); + } return status; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 5e7ced09e..b21592528 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -353,6 +353,7 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent) clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; + clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; clnt->cl_chatty = 1; server->client = clnt; diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 42191418f..d75e9eda0 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -267,6 +267,38 @@ nfs3_proc_remove(struct dentry *dir, struct qstr *name) } static int +nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct nfs3_diropargs *arg; + struct nfs_fattr *res; + + arg = (struct nfs3_diropargs *)kmalloc(sizeof(*arg)+sizeof(*res), GFP_KERNEL); + if (!arg) + return -ENOMEM; + res = (struct nfs_fattr*)(arg + 1); + arg->fh = NFS_FH(dir); + arg->name = name->name; + arg->len = name->len; + res->valid = 0; + msg->rpc_proc = NFS3PROC_REMOVE; + msg->rpc_argp = arg; + msg->rpc_resp = res; + return 0; +} + +static void +nfs3_proc_unlink_done(struct dentry *dir, struct rpc_message *msg) +{ + struct nfs_fattr *dir_attr; + + if (msg->rpc_argp) { + dir_attr = (struct nfs_fattr*)msg->rpc_resp; + nfs_refresh_inode(dir->d_inode, dir_attr); + kfree(msg->rpc_argp); + } +} + +static int nfs3_proc_rename(struct dentry *old_dir, struct qstr *old_name, struct dentry *new_dir, struct qstr *new_name) { @@ -469,6 +501,8 @@ struct nfs_rpc_ops nfs_v3_clientops = { NULL, /* commit */ nfs3_proc_create, nfs3_proc_remove, + nfs3_proc_unlink_setup, + nfs3_proc_unlink_done, nfs3_proc_rename, nfs3_proc_link, nfs3_proc_symlink, diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 7c3a7a0cc..ce05e1843 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -157,6 +157,7 @@ static struct nfs_bool_opts { #endif { "udp", ~NFS_MOUNT_TCP, 0 }, { "tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP }, + { "broken_suid",~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID }, { NULL, 0, 0 } }; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 574c26a15..be935c083 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -233,6 +233,31 @@ nfs_proc_remove(struct dentry *dir, struct qstr *name) } static int +nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) +{ + struct nfs_diropargs *arg; + + arg = (struct nfs_diropargs *)kmalloc(sizeof(*arg), GFP_KERNEL); + if (!arg) + return -ENOMEM; + arg->fh = NFS_FH(dir); + arg->name = name->name; + arg->len = name->len; + msg->rpc_proc = NFSPROC_REMOVE; + msg->rpc_argp = arg; + return 0; +} + +static void +nfs_proc_unlink_done(struct dentry *dir, struct rpc_message *msg) +{ + if (msg->rpc_argp) { + NFS_CACHEINV(dir->d_inode); + kfree(msg->rpc_argp); + } +} + +static int nfs_proc_rename(struct dentry *old_dir, struct qstr *old_name, struct dentry *new_dir, struct qstr *new_name) { @@ -365,6 +390,8 @@ struct nfs_rpc_ops nfs_v2_clientops = { NULL, /* commit */ nfs_proc_create, nfs_proc_remove, + nfs_proc_unlink_setup, + nfs_proc_unlink_done, nfs_proc_rename, nfs_proc_link, nfs_proc_symlink, diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c new file mode 100644 index 000000000..f4abfe7b3 --- /dev/null +++ b/fs/nfs/unlink.c @@ -0,0 +1,216 @@ +/* + * linux/fs/nfs/unlink.c + * + * nfs sillydelete handling + * + * NOTE: we rely on holding the BKL for list manipulation protection. + */ + +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/dcache.h> +#include <linux/sunrpc/sched.h> +#include <linux/sunrpc/clnt.h> +#include <linux/nfs_fs.h> + + +struct nfs_unlinkdata { + struct nfs_unlinkdata *next; + struct dentry *dir, *dentry; + struct qstr name; + struct rpc_task task; + struct rpc_cred *cred; + unsigned int count; +}; + +static struct nfs_unlinkdata *nfs_deletes; +static struct rpc_wait_queue nfs_delete_queue = RPC_INIT_WAITQ("nfs_delete_queue"); + +/** + * nfs_detach_unlinkdata - Remove asynchronous unlink from global list + * @data: pointer to descriptor + */ +static inline void +nfs_detach_unlinkdata(struct nfs_unlinkdata *data) +{ + struct nfs_unlinkdata **q; + + for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) { + if (*q == data) { + *q = data->next; + break; + } + } +} + +/** + * nfs_put_unlinkdata - release data from a sillydelete operation. + * @data: pointer to unlink structure. + */ +static void +nfs_put_unlinkdata(struct nfs_unlinkdata *data) +{ + if (--data->count == 0) { + nfs_detach_unlinkdata(data); + if (data->name.name != NULL) + kfree(data->name.name); + kfree(data); + } +} + +#define NAME_ALLOC_LEN(len) ((len+16) & ~15) +/** + * nfs_copy_dname - copy dentry name to data structure + * @dentry: pointer to dentry + * @data: nfs_unlinkdata + */ +static inline void +nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) +{ + char *str; + int len = dentry->d_name.len; + + str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL); + if (!str) + return; + memcpy(str, dentry->d_name.name, len); + if (!data->name.len) { + data->name.len = len; + data->name.name = str; + } else + kfree(str); +} + +/** + * nfs_async_unlink_init - Initialize the RPC info + * @task: rpc_task of the sillydelete + * + * We delay initializing RPC info until after the call to dentry_iput() + * in order to minimize races against rename(). + */ +static void +nfs_async_unlink_init(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct dentry *dir = data->dir; + struct rpc_message msg; + int status = -ENOENT; + + if (!data->name.len) + goto out_err; + + memset(&msg, 0, sizeof(msg)); + msg.rpc_cred = data->cred; + status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name); + if (status < 0) + goto out_err; + rpc_call_setup(task, &msg, 0); + return; + out_err: + rpc_exit(task, status); +} + +/** + * nfs_async_unlink_done - Sillydelete post-processing + * @task: rpc_task of the sillydelete + * + * Do the directory attribute update. + */ +static void +nfs_async_unlink_done(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct dentry *dir = data->dir; + struct inode *dir_i; + + if (!dir) + return; + dir_i = dir->d_inode; + nfs_zap_caches(dir_i); + NFS_PROTO(dir_i)->unlink_done(dir, &task->tk_msg); + rpcauth_releasecred(task->tk_auth, data->cred); + data->cred = NULL; + dput(dir); +} + +/** + * nfs_async_unlink_release - Release the sillydelete data. + * @task: rpc_task of the sillydelete + * + * We need to call nfs_put_unlinkdata as a 'tk_release' task since the + * rpc_task would be freed too. + */ +static void +nfs_async_unlink_release(struct rpc_task *task) +{ + struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + nfs_put_unlinkdata(data); +} + +/** + * nfs_async_unlink - asynchronous unlinking of a file + * @dir: directory in which the file resides. + * @name: name of the file to unlink. + */ +int +nfs_async_unlink(struct dentry *dentry) +{ + struct dentry *dir = dentry->d_parent; + struct nfs_unlinkdata *data; + struct rpc_task *task; + struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); + int status = -ENOMEM; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) + goto out; + memset(data, 0, sizeof(*data)); + + data->dir = dget(dir); + data->dentry = dentry; + + data->next = nfs_deletes; + nfs_deletes = data; + data->count = 1; + + task = &data->task; + rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC); + task->tk_calldata = data; + task->tk_action = nfs_async_unlink_init; + task->tk_release = nfs_async_unlink_release; + + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + + rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); + status = 0; + out: + return status; +} + +/** + * nfs_complete_remove - Initialize completion of the sillydelete + * @dentry: dentry to delete + * + * 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 nfs_unlinkdata *data; + + for(data = nfs_deletes; data != NULL; data = data->next) { + if (dentry == data->dentry) + break; + } + if (!data) + return; + data->count++; + nfs_copy_dname(dentry, data); + dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; + if (data->task.tk_rpcwait == &nfs_delete_queue) + rpc_wake_up_task(&data->task); + nfs_put_unlinkdata(data); +} |