Newer
Older
/*
* linux/fs/nfs/inode.c
*
* Copyright (C) 1992 Rick Sladkey
*
* nfs inode and superblock handling functions
*
* Modularised by Alan Cox <alan@lxorguk.ukuu.org.uk>, while hacking some
* experimental NFS changes. Modularisation taken straight from SYS5 fs.
*
* Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
* J.S.Peatfield@damtp.cam.ac.uk
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/metrics.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_mount.h>
#include <linux/nfs4_mount.h>
#include <linux/lockd/bind.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/vfs.h>
#include <linux/inet.h>
#include <linux/nfs_xdr.h>
#include <linux/slab.h>

Frank Filz
committed
#include <linux/compat.h>
#include <linux/freezer.h>
#include <linux/crc32.h>
#include "callback.h"
#include "iostat.h"
#include "fscache.h"
#include "dns_resolve.h"
#include "netns.h"
#define NFS_64_BIT_INODE_NUMBERS_ENABLED 1
/* Default is to see 64-bit inode numbers */
static bool enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
static int nfs_update_inode(struct inode *, struct nfs_fattr *);
static struct kmem_cache * nfs_inode_cachep;
static inline unsigned long
nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
{
return nfs_fileid_to_ino_t(fattr->fileid);
}
/**
* nfs_wait_bit_killable - helper for functions that are sleeping on bit locks
* @word: long word containing the bit lock
*/
int nfs_wait_bit_killable(void *word)
{
if (fatal_signal_pending(current))
return -ERESTARTSYS;
freezable_schedule();
return 0;
}
EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
/**
* nfs_compat_user_ino64 - returns the user-visible inode number
* @fileid: 64-bit fileid
*
* This function returns a 32-bit inode number if the boot parameter
* nfs.enable_ino64 is zero.
*/
u64 nfs_compat_user_ino64(u64 fileid)
{

Frank Filz
committed
#ifdef CONFIG_COMPAT
compat_ulong_t ino;
#else
unsigned long ino;
#endif
if (enable_ino64)
return fileid;
ino = fileid;
if (sizeof(ino) < sizeof(fileid))
ino ^= fileid >> (sizeof(fileid)-sizeof(ino)) * 8;
return ino;
}
int nfs_drop_inode(struct inode *inode)
{
return NFS_STALE(inode) || generic_drop_inode(inode);
}
EXPORT_SYMBOL_GPL(nfs_drop_inode);
void nfs_clear_inode(struct inode *inode)
/*
* The following should never happen...
*/
WARN_ON_ONCE(nfs_have_writebacks(inode));
WARN_ON_ONCE(!list_empty(&NFS_I(inode)->open_files));

Trond Myklebust
committed
nfs_zap_acl_cache(inode);
nfs_access_zap_cache(inode);
nfs_fscache_release_inode_cookie(inode);
void nfs_evict_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
nfs_clear_inode(inode);
}
/**
* nfs_sync_mapping - helper to flush all mmapped dirty data to disk
*/
int nfs_sync_mapping(struct address_space *mapping)
{
if (mapping->nrpages != 0) {
unmap_mapping_range(mapping, 0, 0, 0);
ret = nfs_wb_all(mapping->host);
}
static void nfs_zap_caches_locked(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);
int mode = inode->i_mode;
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies;
memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
nfs_fscache_invalidate(inode);
} else {
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
void nfs_zap_caches(struct inode *inode)
{
spin_lock(&inode->i_lock);
nfs_zap_caches_locked(inode);
spin_unlock(&inode->i_lock);

Trond Myklebust
committed
}
void nfs_zap_mapping(struct inode *inode, struct address_space *mapping)
{
if (mapping->nrpages != 0) {
spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
spin_unlock(&inode->i_lock);
}
}
void nfs_zap_acl_cache(struct inode *inode)

Trond Myklebust
committed
{
void (*clear_acl_cache)(struct inode *);
clear_acl_cache = NFS_PROTO(inode)->clear_acl_cache;
if (clear_acl_cache != NULL)
clear_acl_cache(inode);
spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_ACL;
spin_unlock(&inode->i_lock);
void nfs_invalidate_atime(struct inode *inode)
{
spin_lock(&inode->i_lock);
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_invalidate_atime);
* Invalidate, but do not unhash, the inode.
* NB: must be called with inode->i_lock held!
static void nfs_invalidate_inode(struct inode *inode)
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
nfs_zap_caches_locked(inode);
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
}
struct nfs_find_desc {
struct nfs_fh *fh;
struct nfs_fattr *fattr;
};
/*
* In NFSv3 we can have 64bit inode numbers. In order to support
* this, and re-exported directories (also seen in NFSv2)
* we are forced to allow 2 different inodes to have the same
* i_ino.
*/
static int
nfs_find_actor(struct inode *inode, void *opaque)
{
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
struct nfs_fh *fh = desc->fh;
struct nfs_fattr *fattr = desc->fattr;
if (NFS_FILEID(inode) != fattr->fileid)
return 0;
if (nfs_compare_fh(NFS_FH(inode), fh))
return 0;
if (is_bad_inode(inode) || NFS_STALE(inode))
return 0;
return 1;
}
static int
nfs_init_locked(struct inode *inode, void *opaque)
{
struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
struct nfs_fattr *fattr = desc->fattr;
set_nfs_fileid(inode, fattr->fileid);
nfs_copy_fh(NFS_FH(inode), desc->fh);
return 0;
}
/*
* This is our front-end to iget that looks up inodes by file handle
* instead of inode number.
*/
struct inode *
nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
{
struct nfs_find_desc desc = {
.fh = fh,
.fattr = fattr
};
struct inode *inode = ERR_PTR(-ENOENT);
nfs_attr_check_mountpoint(sb, fattr);
if (((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0) &&
!nfs_attr_use_mounted_on_fileid(fattr))
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0)
goto out_no_inode;
hash = nfs_fattr_to_ino_t(fattr);
inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc);
if (inode == NULL) {
inode = ERR_PTR(-ENOMEM);
if (inode->i_state & I_NEW) {
struct nfs_inode *nfsi = NFS_I(inode);

Trond Myklebust
committed
unsigned long now = jiffies;
/* We set i_ino for the few things that still rely on it,
* such as stat(2) */
inode->i_ino = hash;
/* 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;
/* Why so? Because we want revalidate for devices/FIFOs, and
* that's precisely what we have in nfs_file_inode_operations.
*/
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops;
inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->file_ops;
inode->i_data.a_ops = &nfs_file_aops;
inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info;
} else if (S_ISDIR(inode->i_mode)) {
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
inode->i_data.a_ops = &nfs_dir_aops;
/* Deal with crossing mountpoints */
if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
inode->i_op = &nfs_referral_inode_operations;
else
inode->i_op = &nfs_mountpoint_inode_operations;
inode->i_fop = NULL;
inode->i_flags |= S_AUTOMOUNT;
}
} else if (S_ISLNK(inode->i_mode))
inode->i_op = &nfs_symlink_inode_operations;
else
init_special_inode(inode, inode->i_mode, fattr->rdev);
memset(&inode->i_atime, 0, sizeof(inode->i_atime));
memset(&inode->i_mtime, 0, sizeof(inode->i_mtime));
memset(&inode->i_ctime, 0, sizeof(inode->i_ctime));
inode->i_version = 0;
inode->i_size = 0;
inode->i_uid = -2;
inode->i_gid = -2;
inode->i_blocks = 0;
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
nfsi->write_io = 0;
nfsi->read_io = 0;
nfsi->read_cache_jiffies = fattr->time_start;
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;
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;
if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
inode->i_version = fattr->change_attr;
else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
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_REVAL_PAGECACHE;
if (fattr->valid & NFS_ATTR_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;
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;
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
inode->i_blocks = fattr->du.nfs2.blocks;
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
/*
* report the blocks in 512byte units
*/
inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
}
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);

Trond Myklebust
committed
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);
dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n",
atomic_read(&inode->i_count));
out:
return inode;
out_no_inode:
dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode));
#define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET|ATTR_FILE|ATTR_OPEN)
int
nfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct nfs_fattr *fattr;
int error = -ENOMEM;
nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
/* skip mode change if it's just for clearing setuid/setgid */
if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
attr->ia_valid &= ~ATTR_MODE;
if (attr->ia_valid & ATTR_SIZE) {
if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
attr->ia_valid &= ~ATTR_SIZE;
}
/* Optimization: if the end result is no change, don't RPC */
attr->ia_valid &= NFS_VALID_ATTRS;
if ((attr->ia_valid & ~(ATTR_FILE|ATTR_OPEN)) == 0)
/* Write all dirty data */
if (S_ISREG(inode->i_mode)) {
nfs_inode_dio_wait(inode);
}
fattr = nfs_alloc_fattr();
if (fattr == NULL)
goto out;
/*
* Return any delegations if we're going to change ACLs
*/
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
NFS_PROTO(inode)->return_delegation(inode);
error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);

Trond Myklebust
committed
if (error == 0)
nfs_refresh_inode(inode, fattr);
nfs_free_fattr(fattr);
out:

Trond Myklebust
committed
return error;
}

Trond Myklebust
committed
/**
* nfs_vmtruncate - unmap mappings "freed" by truncate() syscall
* @inode: inode of the file used
* @offset: file offset to start truncating
*
* This is a copy of the common vmtruncate, but with the locking
* corrected to take into account the fact that NFS requires
* inode->i_size to be updated under the inode->i_lock.
*/
static int nfs_vmtruncate(struct inode * inode, loff_t offset)
{
err = inode_newsize_ok(inode, offset);
if (err)
goto out;
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;

Trond Myklebust
committed
/**
* nfs_setattr_update_inode - Update inode metadata after a setattr call.
* @inode: pointer to struct inode
* @attr: pointer to struct iattr
*
* Note: we do this in the *proc.c in order to ensure that
* it works for things like exclusive creates too.
*/
void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
{
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {

Trond Myklebust
committed
int mode = attr->ia_mode & S_IALLUGO;
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_mode = mode;
}
if ((attr->ia_valid & ATTR_UID) != 0)
inode->i_uid = attr->ia_uid;
if ((attr->ia_valid & ATTR_GID) != 0)
inode->i_gid = attr->ia_gid;
NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
spin_unlock(&inode->i_lock);

Trond Myklebust
committed
}
if ((attr->ia_valid & ATTR_SIZE) != 0) {
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size);

Trond Myklebust
committed
}
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
/* Flush out writes to the server in order to update c/mtime. */
if (S_ISREG(inode->i_mode)) {
nfs_inode_dio_wait(inode);
err = filemap_write_and_wait(inode->i_mapping);
if (err)
goto out;
/*
* We may force a getattr if the user cares about atime.
*
* Note that we only have to check the vfsmount flags here:
* - NFS always sets S_NOATIME by so checking it would give a
* bogus result
* - NFS never sets MS_NOATIME or MS_NODIRATIME so there is
* no point in checking those.
*/
if ((mnt->mnt_flags & MNT_NOATIME) ||
((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
if (need_atime)
err = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
else
err = nfs_revalidate_inode(NFS_SERVER(inode), inode);
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
{
atomic_set(&l_ctx->count, 1);
l_ctx->lockowner.l_owner = current->files;
l_ctx->lockowner.l_pid = current->tgid;
INIT_LIST_HEAD(&l_ctx->list);
}
static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
{
struct nfs_lock_context *pos;
list_for_each_entry(pos, &ctx->lock_context.list, list) {
if (pos->lockowner.l_owner != current->files)
continue;
if (pos->lockowner.l_pid != current->tgid)
continue;
atomic_inc(&pos->count);
return pos;
}
return NULL;
}
struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)
{
struct nfs_lock_context *res, *new = NULL;
struct inode *inode = ctx->dentry->d_inode;
spin_lock(&inode->i_lock);
res = __nfs_find_lock_context(ctx);
if (res == NULL) {
spin_unlock(&inode->i_lock);
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (new == NULL)
return ERR_PTR(-ENOMEM);
nfs_init_lock_context(new);
spin_lock(&inode->i_lock);
res = __nfs_find_lock_context(ctx);
if (res == NULL) {
list_add_tail(&new->list, &ctx->lock_context.list);
new->open_context = ctx;
res = new;
new = NULL;
}
}
spin_unlock(&inode->i_lock);
kfree(new);
return res;
}
void nfs_put_lock_context(struct nfs_lock_context *l_ctx)
{
struct nfs_open_context *ctx = l_ctx->open_context;
struct inode *inode = ctx->dentry->d_inode;
if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock))
return;
list_del(&l_ctx->list);
spin_unlock(&inode->i_lock);
kfree(l_ctx);
}
/**
* 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->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);
}
struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode)
struct rpc_cred *cred = rpc_lookup_cred();
if (IS_ERR(cred))
return ERR_CAST(cred);
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
put_rpccred(cred);
return ERR_PTR(-ENOMEM);
nfs_sb_active(dentry->d_sb);
ctx->dentry = dget(dentry);
ctx->cred = cred;
ctx->state = NULL;
ctx->mode = f_mode;
ctx->flags = 0;
ctx->error = 0;
nfs_init_lock_context(&ctx->lock_context);
ctx->lock_context.open_context = ctx;
INIT_LIST_HEAD(&ctx->list);
EXPORT_SYMBOL_GPL(alloc_nfs_open_context);
struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
{
if (ctx != NULL)
atomic_inc(&ctx->lock_context.count);
EXPORT_SYMBOL_GPL(get_nfs_open_context);
static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
struct inode *inode = ctx->dentry->d_inode;
struct super_block *sb = ctx->dentry->d_sb;
if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock))
return;
list_del(&ctx->list);
spin_unlock(&inode->i_lock);
} else if (!atomic_dec_and_test(&ctx->lock_context.count))
if (inode != NULL)
NFS_PROTO(inode)->close_context(ctx, is_sync);
if (ctx->cred != NULL)
put_rpccred(ctx->cred);
nfs_sb_deactive(sb);
kfree(ctx);
}
void put_nfs_open_context(struct nfs_open_context *ctx)
{
__put_nfs_open_context(ctx, 0);
}
EXPORT_SYMBOL_GPL(put_nfs_open_context);
/*
* Ensure that mmap has a recent RPC credential for use when writing out
* shared pages
*/
void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
struct inode *inode = filp->f_path.dentry->d_inode;
struct nfs_inode *nfsi = NFS_I(inode);
filp->private_data = get_nfs_open_context(ctx);
spin_lock(&inode->i_lock);
list_add(&ctx->list, &nfsi->open_files);
spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_file_set_open_context);
/*
* Given an inode, search for an open context with the desired characteristics
*/
struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *pos, *ctx = NULL;
spin_lock(&inode->i_lock);
list_for_each_entry(pos, &nfsi->open_files, list) {
if (cred != NULL && pos->cred != cred)
continue;
if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode)
continue;
ctx = get_nfs_open_context(pos);
break;
}
spin_unlock(&inode->i_lock);
return ctx;
}
static void nfs_file_clear_open_context(struct file *filp)
struct inode *inode = filp->f_path.dentry->d_inode;
struct nfs_open_context *ctx = nfs_file_open_context(filp);
if (ctx) {
filp->private_data = NULL;
spin_lock(&inode->i_lock);
list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
spin_unlock(&inode->i_lock);
__put_nfs_open_context(ctx, filp->f_flags & O_DIRECT ? 0 : 1);
}
}
/*
* These allocate and release file read/write context information.
*/
int nfs_open(struct inode *inode, struct file *filp)
{
struct nfs_open_context *ctx;
ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
if (IS_ERR(ctx))
return PTR_ERR(ctx);
nfs_file_set_open_context(filp, ctx);
put_nfs_open_context(ctx);
nfs_fscache_set_inode_cookie(inode, filp);
return 0;
}
int nfs_release(struct inode *inode, struct file *filp)
{
nfs_file_clear_open_context(filp);
return 0;
}
/*
* This function is called whenever some part of NFS notices that
* the cached attributes have to be refreshed.
*/
int
__nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
int status = -ESTALE;
struct nfs_fattr *fattr = NULL;
struct nfs_inode *nfsi = NFS_I(inode);
dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode));

Chuck Lever
committed
if (is_bad_inode(inode))
goto out;
status = -ENOMEM;
fattr = nfs_alloc_fattr();
if (fattr == NULL)
goto out;
nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr);
if (status != 0) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
if (status == -ESTALE) {
nfs_zap_caches(inode);
if (!S_ISDIR(inode->i_mode))
set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
status = nfs_refresh_inode(inode, fattr);
if (status) {
dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode), status);
goto out;
}
if (nfsi->cache_validity & NFS_INO_INVALID_ACL)

Trond Myklebust
committed
nfs_zap_acl_cache(inode);
dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n",
inode->i_sb->s_id,
(long long)NFS_FILEID(inode));
nfs_free_fattr(fattr);
return status;
}
int nfs_attribute_timeout(struct inode *inode)
{
struct nfs_inode *nfsi = NFS_I(inode);

Trond Myklebust
committed
return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
}
static int nfs_attribute_cache_expired(struct inode *inode)
{

Trond Myklebust
committed
if (nfs_have_delegated_attributes(inode))

Trond Myklebust
committed
return nfs_attribute_timeout(inode);
}
/**
* nfs_revalidate_inode - Revalidate the inode attributes
* @server - pointer to nfs_server struct
* @inode - pointer to inode struct
*
* Updates inode attribute information by retrieving the data from the server.
*/
int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{
if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR)

Trond Myklebust
committed
&& !nfs_attribute_cache_expired(inode))
return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode);
}
EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
{
struct nfs_inode *nfsi = NFS_I(inode);
if (mapping->nrpages != 0) {
int ret = invalidate_inode_pages2(mapping);
if (ret < 0)
return ret;
}
spin_lock(&inode->i_lock);
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
if (S_ISDIR(inode->i_mode))
memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf));
spin_unlock(&inode->i_lock);
nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
inode->i_sb->s_id, (long long)NFS_FILEID(inode));
return 0;
}
static bool nfs_mapping_need_revalidate_inode(struct inode *inode)
{
if (nfs_have_delegated_attributes(inode))
return false;
return (NFS_I(inode)->cache_validity & NFS_INO_REVAL_PAGECACHE)
|| nfs_attribute_timeout(inode)
|| NFS_STALE(inode);
}
/**
* nfs_revalidate_mapping - Revalidate the pagecache
* @inode - pointer to host inode
* @mapping - pointer to mapping
*/
int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
{
struct nfs_inode *nfsi = NFS_I(inode);
int ret = 0;
/* swapfiles are not supposed to be shared. */
if (IS_SWAPFILE(inode))
goto out;
if (nfs_mapping_need_revalidate_inode(inode)) {
ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (ret < 0)
goto out;
}
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
ret = nfs_invalidate_mapping(inode, mapping);
return ret;
}
static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
unsigned long ret = 0;
if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE)
&& (fattr->valid & NFS_ATTR_FATTR_CHANGE)
&& inode->i_version == fattr->pre_change_attr) {
inode->i_version = fattr->change_attr;
if (S_ISDIR(inode->i_mode))
nfsi->cache_validity |= NFS_INO_INVALID_DATA;
ret |= NFS_INO_INVALID_ATTR;
}
/* If we have atomic WCC data, we may update some attributes */
if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
&& (fattr->valid & NFS_ATTR_FATTR_CTIME)
&& timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
ret |= NFS_INO_INVALID_ATTR;
}
if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
&& (fattr->valid & NFS_ATTR_FATTR_MTIME)
&& timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) {
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
if (S_ISDIR(inode->i_mode))
nfsi->cache_validity |= NFS_INO_INVALID_DATA;
ret |= NFS_INO_INVALID_ATTR;
if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE)
&& (fattr->valid & NFS_ATTR_FATTR_SIZE)
&& i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size)
&& nfsi->npages == 0) {
i_size_write(inode, nfs_size_to_loff_t(fattr->size));
ret |= NFS_INO_INVALID_ATTR;
}
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
nfs_fscache_invalidate(inode);
return ret;
* nfs_check_inode_attributes - verify consistency of the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
*
* Verifies the attribute cache. If we have just changed the attributes,
* so that fattr carries weak cache consistency data, then it may
* also update the ctime/mtime/change_attribute.
*/
static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
loff_t cur_size, new_isize;
unsigned long invalid = 0;
if (nfs_have_delegated_attributes(inode))
return 0;

Trond Myklebust
committed
/* Has the inode gone and changed behind our back? */
if ((fattr->valid & NFS_ATTR_FATTR_FILEID) && nfsi->fileid != fattr->fileid)
return -EIO;
if ((fattr->valid & NFS_ATTR_FATTR_TYPE) && (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))

Trond Myklebust
committed
return -EIO;
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
inode->i_version != fattr->change_attr)
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
/* Verify a few of the more important attributes */
if ((fattr->valid & NFS_ATTR_FATTR_MTIME) && !timespec_equal(&inode->i_mtime, &fattr->mtime))
invalid |= NFS_INO_INVALID_ATTR;

Trond Myklebust
committed
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
cur_size = i_size_read(inode);