nfs: Fix nfs_parse_mount_options() kfree() leak
[safe/jmp/linux-2.6] / fs / nfs / inode.c
index c40adc5..faa0918 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/nfs_mount.h>
 #include <linux/nfs4_mount.h>
 #include <linux/lockd/bind.h>
-#include <linux/smp_lock.h>
 #include <linux/seq_file.h>
 #include <linux/mount.h>
 #include <linux/nfs_idmap.h>
@@ -46,6 +45,8 @@
 #include "delegation.h"
 #include "iostat.h"
 #include "internal.h"
+#include "fscache.h"
+#include "dns_resolve.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
@@ -121,6 +122,7 @@ void nfs_clear_inode(struct inode *inode)
        BUG_ON(!list_empty(&NFS_I(inode)->open_files));
        nfs_zap_acl_cache(inode);
        nfs_access_zap_cache(inode);
+       nfs_fscache_release_inode_cookie(inode);
 }
 
 /**
@@ -285,6 +287,11 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                /* We can't support update_atime(), since the server will reset it */
                inode->i_flags |= S_NOATIME|S_NOCMTIME;
                inode->i_mode = fattr->mode;
+               if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
+                               && nfs_server_capable(inode, NFS_CAP_MODE))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                /* Why so? Because we want revalidate for devices/FIFOs, and
                 * that's precisely what we have in nfs_file_inode_operations.
                 */
@@ -329,20 +336,46 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                nfsi->attr_gencount = fattr->gencount;
                if (fattr->valid & NFS_ATTR_FATTR_ATIME)
                        inode->i_atime = fattr->atime;
+               else if (nfs_server_capable(inode, NFS_CAP_ATIME))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_MTIME)
                        inode->i_mtime = fattr->mtime;
+               else if (nfs_server_capable(inode, NFS_CAP_MTIME))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA;
                if (fattr->valid & NFS_ATTR_FATTR_CTIME)
                        inode->i_ctime = fattr->ctime;
+               else if (nfs_server_capable(inode, NFS_CAP_CTIME))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
                        nfsi->change_attr = fattr->change_attr;
+               else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA;
                if (fattr->valid & NFS_ATTR_FATTR_SIZE)
                        inode->i_size = nfs_size_to_loff_t(fattr->size);
+               else
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA
+                               | NFS_INO_REVAL_PAGECACHE;
                if (fattr->valid & NFS_ATTR_FATTR_NLINK)
                        inode->i_nlink = fattr->nlink;
+               else if (nfs_server_capable(inode, NFS_CAP_NLINK))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_OWNER)
                        inode->i_uid = fattr->uid;
+               else if (nfs_server_capable(inode, NFS_CAP_OWNER))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_GROUP)
                        inode->i_gid = fattr->gid;
+               else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
+                       nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
                        inode->i_blocks = fattr->du.nfs2.blocks;
                if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
@@ -355,6 +388,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                nfsi->attrtimeo_timestamp = now;
                nfsi->access_cache = RB_ROOT;
 
+               nfs_fscache_init_inode_cookie(inode);
+
                unlock_new_inode(inode);
        } else
                nfs_refresh_inode(inode, fattr);
@@ -423,49 +458,21 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
  */
 static int nfs_vmtruncate(struct inode * inode, loff_t offset)
 {
-       if (i_size_read(inode) < offset) {
-               unsigned long limit;
-
-               limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
-               if (limit != RLIM_INFINITY && offset > limit)
-                       goto out_sig;
-               if (offset > inode->i_sb->s_maxbytes)
-                       goto out_big;
-               spin_lock(&inode->i_lock);
-               i_size_write(inode, offset);
-               spin_unlock(&inode->i_lock);
-       } else {
-               struct address_space *mapping = inode->i_mapping;
+       loff_t oldsize;
+       int err;
 
-               /*
-                * truncation of in-use swapfiles is disallowed - it would
-                * cause subsequent swapout to scribble on the now-freed
-                * blocks.
-                */
-               if (IS_SWAPFILE(inode))
-                       return -ETXTBSY;
-               spin_lock(&inode->i_lock);
-               i_size_write(inode, offset);
-               spin_unlock(&inode->i_lock);
+       err = inode_newsize_ok(inode, offset);
+       if (err)
+               goto out;
 
-               /*
-                * unmap_mapping_range is called twice, first simply for
-                * efficiency so that truncate_inode_pages does fewer
-                * single-page unmaps.  However after this first call, and
-                * before truncate_inode_pages finishes, it is possible for
-                * private pages to be COWed, which remain after
-                * truncate_inode_pages finishes, hence the second
-                * unmap_mapping_range call must be made for correctness.
-                */
-               unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
-               truncate_inode_pages(mapping, offset);
-               unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
-       }
-       return 0;
-out_sig:
-       send_sig(SIGXFSZ, current, 0);
-out_big:
-       return -EFBIG;
+       spin_lock(&inode->i_lock);
+       oldsize = inode->i_size;
+       i_size_write(inode, offset);
+       spin_unlock(&inode->i_lock);
+
+       truncate_pagecache(inode, oldsize, offset);
+out:
+       return err;
 }
 
 /**
@@ -541,6 +548,32 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
        return err;
 }
 
+/**
+ * nfs_close_context - Common close_context() routine NFSv2/v3
+ * @ctx: pointer to context
+ * @is_sync: is this a synchronous close
+ *
+ * always ensure that the attributes are up to date if we're mounted
+ * with close-to-open semantics
+ */
+void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
+{
+       struct inode *inode;
+       struct nfs_server *server;
+
+       if (!(ctx->mode & FMODE_WRITE))
+               return;
+       if (!is_sync)
+               return;
+       inode = ctx->path.dentry->d_inode;
+       if (!list_empty(&NFS_I(inode)->open_files))
+               return;
+       server = NFS_SERVER(inode);
+       if (server->flags & NFS_MOUNT_NOCTO)
+               return;
+       nfs_revalidate_inode(server, inode);
+}
+
 static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred)
 {
        struct nfs_open_context *ctx;
@@ -567,24 +600,15 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
        return ctx;
 }
 
-static void __put_nfs_open_context(struct nfs_open_context *ctx, int wait)
+static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
 {
-       struct inode *inode;
+       struct inode *inode = ctx->path.dentry->d_inode;
 
-       if (ctx == NULL)
-               return;
-
-       inode = ctx->path.dentry->d_inode;
        if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock))
                return;
        list_del(&ctx->list);
        spin_unlock(&inode->i_lock);
-       if (ctx->state != NULL) {
-               if (wait)
-                       nfs4_close_sync(&ctx->path, ctx->state, ctx->mode);
-               else
-                       nfs4_close_state(&ctx->path, ctx->state, ctx->mode);
-       }
+       NFS_PROTO(inode)->close_context(ctx, is_sync);
        if (ctx->cred != NULL)
                put_rpccred(ctx->cred);
        path_put(&ctx->path);
@@ -669,6 +693,7 @@ int nfs_open(struct inode *inode, struct file *filp)
        ctx->mode = filp->f_mode;
        nfs_file_set_open_context(filp, ctx);
        put_nfs_open_context(ctx);
+       nfs_fscache_set_inode_cookie(inode, filp);
        return 0;
 }
 
@@ -769,6 +794,7 @@ static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_spa
                memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
        spin_unlock(&inode->i_lock);
        nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
+       nfs_fscache_reset_inode_cookie(inode);
        dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
                        inode->i_sb->s_id, (long long)NFS_FILEID(inode));
        return 0;
@@ -1013,6 +1039,7 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
        spin_lock(&inode->i_lock);
        status = nfs_refresh_inode_locked(inode, fattr);
        spin_unlock(&inode->i_lock);
+
        return status;
 }
 
@@ -1122,6 +1149,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        loff_t cur_isize, new_isize;
        unsigned long invalid = 0;
        unsigned long now = jiffies;
+       unsigned long save_cache_validity;
 
        dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
                        __func__, inode->i_sb->s_id, inode->i_ino,
@@ -1148,10 +1176,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
         */
        nfsi->read_cache_jiffies = fattr->time_start;
 
-       if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) || (fattr->valid & (NFS_ATTR_FATTR_MTIME|NFS_ATTR_FATTR_CTIME)))
-           nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
-                   | NFS_INO_INVALID_ATIME
-                   | NFS_INO_REVAL_PAGECACHE);
+       save_cache_validity = nfsi->cache_validity;
+       nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
+                       | NFS_INO_INVALID_ATIME
+                       | NFS_INO_REVAL_FORCED
+                       | NFS_INO_REVAL_PAGECACHE);
 
        /* Do atomic weak cache consistency updates */
        nfs_wcc_update_inode(inode, fattr);
@@ -1166,7 +1195,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                nfs_force_lookup_revalidate(inode);
                        nfsi->change_attr = fattr->change_attr;
                }
-       }
+       } else if (server->caps & NFS_CAP_CHANGE_ATTR)
+               invalid |= save_cache_validity;
 
        if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
                /* NFSv2/v3: Check if the mtime agrees */
@@ -1178,7 +1208,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                nfs_force_lookup_revalidate(inode);
                        memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
                }
-       }
+       } else if (server->caps & NFS_CAP_MTIME)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_DATA
+                               | NFS_INO_REVAL_PAGECACHE
+                               | NFS_INO_REVAL_FORCED);
+
        if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
                /* If ctime has changed we should definitely clear access+acl caches */
                if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
@@ -1192,7 +1227,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        }
                        memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
                }
-       }
+       } else if (server->caps & NFS_CAP_CTIME)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
 
        /* Check if our cached file size is stale */
        if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
@@ -1208,30 +1247,50 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        dprintk("NFS: isize change on server for file %s/%ld\n",
                                        inode->i_sb->s_id, inode->i_ino);
                }
-       }
+       } else
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_REVAL_PAGECACHE
+                               | NFS_INO_REVAL_FORCED);
 
 
        if (fattr->valid & NFS_ATTR_FATTR_ATIME)
                memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
+       else if (server->caps & NFS_CAP_ATIME)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATIME
+                               | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_MODE) {
                if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        inode->i_mode = fattr->mode;
                }
-       }
+       } else if (server->caps & NFS_CAP_MODE)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
+
        if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
                if (inode->i_uid != fattr->uid) {
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        inode->i_uid = fattr->uid;
                }
-       }
+       } else if (server->caps & NFS_CAP_OWNER)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
+
        if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
                if (inode->i_gid != fattr->gid) {
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        inode->i_gid = fattr->gid;
                }
-       }
+       } else if (server->caps & NFS_CAP_OWNER_GROUP)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_INVALID_ACCESS
+                               | NFS_INO_INVALID_ACL
+                               | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
                if (inode->i_nlink != fattr->nlink) {
@@ -1240,7 +1299,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                invalid |= NFS_INO_INVALID_DATA;
                        inode->i_nlink = fattr->nlink;
                }
-       }
+       } else if (server->caps & NFS_CAP_NLINK)
+               invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
+                               | NFS_INO_REVAL_FORCED);
 
        if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
                /*
@@ -1270,9 +1331,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                                || S_ISLNK(inode->i_mode)))
                invalid &= ~NFS_INO_INVALID_DATA;
        if (!nfs_have_delegation(inode, FMODE_READ) ||
-                       (nfsi->cache_validity & NFS_INO_REVAL_FORCED))
+                       (save_cache_validity & NFS_INO_REVAL_FORCED))
                nfsi->cache_validity |= invalid;
-       nfsi->cache_validity &= ~NFS_INO_REVAL_FORCED;
 
        return 0;
  out_changed:
@@ -1419,6 +1479,14 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
+       err = nfs_dns_resolver_init();
+       if (err < 0)
+               goto out8;
+
+       err = nfs_fscache_register();
+       if (err < 0)
+               goto out7;
+
        err = nfsiod_start();
        if (err)
                goto out6;
@@ -1471,6 +1539,10 @@ out4:
 out5:
        nfsiod_stop();
 out6:
+       nfs_fscache_unregister();
+out7:
+       nfs_dns_resolver_destroy();
+out8:
        return err;
 }
 
@@ -1481,6 +1553,8 @@ static void __exit exit_nfs_fs(void)
        nfs_destroy_readpagecache();
        nfs_destroy_inodecache();
        nfs_destroy_nfspagecache();
+       nfs_fscache_unregister();
+       nfs_dns_resolver_destroy();
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister("nfs");
 #endif