X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfs%2Ffile.c;h=21071ddd466277f0bb585f375ad6010d513109eb;hb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;hp=1ea93c580a03f8362a72462d642ecf376f5810f3;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 1ea93c580..21071ddd4 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -31,18 +31,19 @@ #include #include +#include "delegation.h" + #define NFSDBG_FACILITY NFSDBG_FILE -static long nfs_file_fcntl(int fd, unsigned int cmd, - unsigned long arg, struct file *filp); static int nfs_file_open(struct inode *, struct file *); static int nfs_file_release(struct inode *, struct file *); static int nfs_file_mmap(struct file *, struct vm_area_struct *); -static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void __user *); +static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); static ssize_t nfs_file_read(struct kiocb *, char __user *, size_t, loff_t); static ssize_t nfs_file_write(struct kiocb *, const char __user *, size_t, loff_t); static int nfs_file_flush(struct file *); static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); +static int nfs_check_flags(int flags); struct file_operations nfs_file_operations = { .llseek = remote_llseek, @@ -57,7 +58,7 @@ struct file_operations nfs_file_operations = { .fsync = nfs_fsync, .lock = nfs_lock, .sendfile = nfs_file_sendfile, - .fcntl = nfs_file_fcntl, + .check_flags = nfs_check_flags, }; struct inode_operations nfs_file_inode_operations = { @@ -71,26 +72,12 @@ struct inode_operations nfs_file_inode_operations = { # define IS_SWAPFILE(inode) (0) #endif -#define nfs_invalid_flags (O_APPEND | O_DIRECT) - -/* - * Check for special cases that NFS doesn't support, and - * pass the rest to the generic fcntl function. - */ -static long -nfs_file_fcntl(int fd, unsigned int cmd, - unsigned long arg, struct file *filp) +static int nfs_check_flags(int flags) { - switch (cmd) { - case F_SETFL: - if ((filp->f_flags & nfs_invalid_flags) == nfs_invalid_flags) - return -EINVAL; - break; - default: - break; - } + if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT)) + return -EINVAL; - return generic_file_fcntl(fd, cmd, arg, filp); + return 0; } /* @@ -101,10 +88,11 @@ nfs_file_open(struct inode *inode, struct file *filp) { struct nfs_server *server = NFS_SERVER(inode); int (*open)(struct inode *, struct file *); - int res = 0; + int res; - if ((filp->f_flags & nfs_invalid_flags) == nfs_invalid_flags) - return -EINVAL; + res = nfs_check_flags(filp->f_flags); + if (res) + return res; lock_kernel(); /* Do NFSv4 open() call */ @@ -127,6 +115,7 @@ nfs_file_release(struct inode *inode, struct file *filp) static int nfs_file_flush(struct file *file) { + struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = file->f_dentry->d_inode; int status; @@ -138,9 +127,9 @@ nfs_file_flush(struct file *file) /* Ensure that data+attribute caches are up to date after close() */ status = nfs_wb_all(inode); if (!status) { - status = file->f_error; - file->f_error = 0; - if (!status) + status = ctx->error; + ctx->error = 0; + if (!status && !nfs_have_delegation(inode, FMODE_READ)) __nfs_revalidate_inode(NFS_SERVER(inode), inode); } unlock_kernel(); @@ -171,7 +160,7 @@ nfs_file_read(struct kiocb *iocb, char __user * buf, size_t count, loff_t pos) static ssize_t nfs_file_sendfile(struct file *filp, loff_t *ppos, size_t count, - read_actor_t actor, void __user *target) + read_actor_t actor, void *target) { struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; @@ -211,6 +200,7 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) static int nfs_fsync(struct file *file, struct dentry *dentry, int datasync) { + struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = dentry->d_inode; int status; @@ -219,8 +209,8 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync) lock_kernel(); status = nfs_wb_all(inode); if (!status) { - status = file->f_error; - file->f_error = 0; + status = ctx->error; + ctx->error = 0; } unlock_kernel(); return status; @@ -302,6 +292,90 @@ out_swapfile: goto out; } +static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) +{ + struct inode *inode = filp->f_mapping->host; + int status; + + lock_kernel(); + status = NFS_PROTO(inode)->lock(filp, cmd, fl); + unlock_kernel(); + return status; +} + +static int do_unlk(struct file *filp, int cmd, struct file_lock *fl) +{ + struct inode *inode = filp->f_mapping->host; + sigset_t oldset; + int status; + + rpc_clnt_sigmask(NFS_CLIENT(inode), &oldset); + /* + * Flush all pending writes before doing anything + * with locks.. + */ + filemap_fdatawrite(filp->f_mapping); + down(&inode->i_sem); + nfs_wb_all(inode); + up(&inode->i_sem); + filemap_fdatawait(filp->f_mapping); + + /* NOTE: special case + * If we're signalled while cleaning up locks on process exit, we + * still need to complete the unlock. + */ + lock_kernel(); + status = NFS_PROTO(inode)->lock(filp, cmd, fl); + rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset); + return status; +} + +static int do_setlk(struct file *filp, int cmd, struct file_lock *fl) +{ + struct inode *inode = filp->f_mapping->host; + int status; + + /* + * Flush all pending writes before doing anything + * with locks.. + */ + status = filemap_fdatawrite(filp->f_mapping); + if (status == 0) { + down(&inode->i_sem); + status = nfs_wb_all(inode); + up(&inode->i_sem); + if (status == 0) + status = filemap_fdatawait(filp->f_mapping); + } + if (status < 0) + return status; + + lock_kernel(); + status = NFS_PROTO(inode)->lock(filp, cmd, fl); + /* If we were signalled we still need to ensure that + * we clean up any state on the server. We therefore + * record the lock call as having succeeded in order to + * ensure that locks_remove_posix() cleans it out when + * the process exits. + */ + if (status == -EINTR || status == -ERESTARTSYS) + posix_lock_file(filp, fl); + unlock_kernel(); + if (status < 0) + return status; + /* + * Make sure we clear the cache whenever we try to get the lock. + * This makes locking act as a cache coherency point. + */ + filemap_fdatawrite(filp->f_mapping); + down(&inode->i_sem); + nfs_wb_all(inode); /* we may have slept */ + up(&inode->i_sem); + filemap_fdatawait(filp->f_mapping); + nfs_zap_caches(inode); + return 0; +} + /* * Lock a (portion of) a file */ @@ -309,8 +383,6 @@ int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) { struct inode * inode = filp->f_mapping->host; - int status = 0; - int status2; dprintk("NFS: nfs_lock(f=%s/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n", inode->i_sb->s_id, inode->i_ino, @@ -328,8 +400,8 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) /* Fake OK code if mounted without NLM support */ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) { if (IS_GETLK(cmd)) - status = LOCK_USE_CLNT; - goto out_ok; + return LOCK_USE_CLNT; + return 0; } } @@ -343,42 +415,9 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl) if (!fl->fl_owner || !(fl->fl_flags & FL_POSIX)) return -ENOLCK; - /* - * Flush all pending writes before doing anything - * with locks.. - */ - status = filemap_fdatawrite(filp->f_mapping); - down(&inode->i_sem); - status2 = nfs_wb_all(inode); - if (!status) - status = status2; - up(&inode->i_sem); - status2 = filemap_fdatawait(filp->f_mapping); - if (!status) - status = status2; - if (status < 0) - return status; - - lock_kernel(); - status = NFS_PROTO(inode)->lock(filp, cmd, fl); - unlock_kernel(); - if (status < 0) - return status; - - status = 0; - - /* - * Make sure we clear the cache whenever we try to get the lock. - * This makes locking act as a cache coherency point. - */ - out_ok: - if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) { - filemap_fdatawrite(filp->f_mapping); - down(&inode->i_sem); - nfs_wb_all(inode); /* we may have slept */ - up(&inode->i_sem); - filemap_fdatawait(filp->f_mapping); - nfs_zap_caches(inode); - } - return status; + if (IS_GETLK(cmd)) + return do_getlk(filp, cmd, fl); + if (fl->fl_type == F_UNLCK) + return do_unlk(filp, cmd, fl); + return do_setlk(filp, cmd, fl); }