void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) { int was_empty; if (!inode || !fattr) { printk("nfs_refresh_inode: inode or fattr is NULL\n"); return; } if (inode->i_ino != fattr->fileid) { printk("nfs_refresh_inode: inode number mismatch\n"); return; } was_empty = (inode->i_mode == 0); /* * Some (broken?) NFS servers return 0 as the file type. * We'll assume that 0 means the file type has not changed. */ if(!(fattr->mode & S_IFMT)){ inode->i_mode = (inode->i_mode & S_IFMT) | (fattr->mode & ~S_IFMT); }else{ inode->i_mode = fattr->mode; } inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; inode->i_gid = fattr->gid; /* Size changed from outside: invalidate caches on next read */ if (inode->i_size != fattr->size) NFS_CACHEINV(inode); if (NFS_OLDMTIME(inode) != fattr->mtime.seconds) NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); inode->i_size = fattr->size; if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) inode->i_rdev = to_kdev_t(fattr->rdev); else inode->i_rdev = 0; inode->i_blocks = fattr->blocks; inode->i_atime = fattr->atime.seconds; inode->i_mtime = fattr->mtime.seconds; inode->i_ctime = fattr->ctime.seconds; if (S_ISREG(inode->i_mode)) inode->i_op = &nfs_file_inode_operations; else if (S_ISDIR(inode->i_mode)) inode->i_op = &nfs_dir_inode_operations; else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; else if (S_ISCHR(inode->i_mode)) inode->i_op = &chrdev_inode_operations; else if (S_ISBLK(inode->i_mode)) inode->i_op = &blkdev_inode_operations; else if (S_ISFIFO(inode->i_mode)) { if (was_empty) init_fifo(inode); } else inode->i_op = NULL; nfs_lookup_cache_refresh(inode, fattr); }
static inline void revalidate_dir(struct nfs_server * server, struct inode * dir) { struct nfs_fattr fattr; if (jiffies - NFS_READTIME(dir) < NFS_ATTRTIMEO(dir)) return; NFS_READTIME(dir) = jiffies; if (nfs_proc_getattr(server, NFS_FH(dir), &fattr) == 0) { nfs_refresh_inode(dir, &fattr); if (fattr.mtime.seconds == NFS_OLDMTIME(dir)) { if ((NFS_ATTRTIMEO(dir) <<= 1) > server->acdirmax) NFS_ATTRTIMEO(dir) = server->acdirmax; return; } NFS_OLDMTIME(dir) = fattr.mtime.seconds; } /* invalidate directory cache here when we _really_ start caching */ }
/* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we * just mark the cache times invalid, and zero out i_mode * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ static void nfs_read_inode(struct inode * inode) { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; inode->i_op = NULL; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); }
/* * Invalidate the local caches */ static void nfs_zap_caches(struct inode *inode) { NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_CACHEINV(inode); if (S_ISDIR(inode->i_mode)) nfs_invalidate_dircache(inode); else invalidate_inode_pages(inode); }
/* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we * just mark the cache times invalid, and zero out i_mode * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ static void nfs_read_inode(struct inode * inode) { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; /* We can't support UPDATE_ATIME(), since the server will reset it */ inode->i_flags |= S_NOATIME; INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; }
/* * The "read_inode" function doesn't actually do anything: * the real data is filled in later in nfs_fhget. Here we * just mark the cache times invalid, and zero out i_mode * (the latter makes "nfs_refresh_inode" do the right thing * wrt pipe inodes) */ static void nfs_read_inode(struct inode * inode) { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; inode->i_rdev = 0; NFS_FILEID(inode) = 0; NFS_FSID(inode) = 0; INIT_LIST_HEAD(&inode->u.nfs_i.read); INIT_LIST_HEAD(&inode->u.nfs_i.dirty); INIT_LIST_HEAD(&inode->u.nfs_i.commit); INIT_LIST_HEAD(&inode->u.nfs_i.writeback); inode->u.nfs_i.nread = 0; inode->u.nfs_i.ndirty = 0; inode->u.nfs_i.ncommit = 0; inode->u.nfs_i.npages = 0; NFS_CACHEINV(inode); NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; }
/* * We judge how long we want to trust negative * dentries by looking at the parent inode mtime. * * If parent mtime has changed, we revalidate, else we wait for a * period corresponding to the parent's attribute cache timeout value. */ static inline int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry) { if (!nfs_check_verifier(dir, dentry)) return 1; return time_after(jiffies, dentry->d_time + NFS_ATTRTIMEO(dir)); }
int nfs_permission(struct inode *inode, int mask) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_access_cache *cache = &NFS_I(inode)->cache_access; struct rpc_cred *cred; int mode = inode->i_mode; int error; if (mask & MAY_WRITE) { /* * * Nobody gets write access to a read-only fs. * */ if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; /* * * Nobody gets write access to an immutable file. * */ if (IS_IMMUTABLE(inode)) return -EACCES; } if ((server->flags & NFS_MOUNT_NOACL) || !NFS_PROTO(inode)->access) goto out_notsup; cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); if (cache->cred == cred && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode))) { if (!cache->err) { /* Is the mask a subset of an accepted mask? */ if ((cache->mask & mask) == mask) goto out_cached; } else { /* ...or is it a superset of a rejected mask? */ if ((cache->mask & mask) == cache->mask) goto out_cached; } } error = NFS_PROTO(inode)->access(inode, cred, mask); if (!error || error == -EACCES) { cache->jiffies = jiffies; if (cache->cred) put_rpccred(cache->cred); cache->cred = cred; cache->mask = mask; cache->err = error; return error; } put_rpccred(cred); out_notsup: nfs_revalidate_inode(NFS_SERVER(inode), inode); return vfs_permission(inode, mask); out_cached: put_rpccred(cred); return cache->err; }
int nfs_permission(struct inode *inode, int mask, struct nameidata *nd) { struct nfs_access_cache *cache = &NFS_I(inode)->cache_access; struct rpc_cred *cred; int mode = inode->i_mode; int res; if (mask == 0) return 0; if (mask & MAY_WRITE) { /* * * Nobody gets write access to a read-only fs. * */ if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; /* * * Nobody gets write access to an immutable file. * */ if (IS_IMMUTABLE(inode)) return -EACCES; } /* Are we checking permissions on anything other than lookup/execute? */ if ((mask & MAY_EXEC) == 0) { /* We only need to check permissions on file open() and access() */ if (!nd || !(nd->flags & (LOOKUP_OPEN|LOOKUP_ACCESS))) return 0; /* NFSv4 has atomic_open... */ if (NFS_PROTO(inode)->version > 3 && (nd->flags & LOOKUP_OPEN)) return 0; } lock_kernel(); if (!NFS_PROTO(inode)->access) goto out_notsup; cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); if (cache->cred == cred && time_before(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)) && !(NFS_FLAGS(inode) & NFS_INO_INVALID_ATTR)) { if (!(res = cache->err)) { /* Is the mask a subset of an accepted mask? */ if ((cache->mask & mask) == mask) goto out; } else { /* ...or is it a superset of a rejected mask? */ if ((cache->mask & mask) == cache->mask) goto out; } } res = NFS_PROTO(inode)->access(inode, cred, mask); if (!res || res == -EACCES) goto add_cache; out: put_rpccred(cred); unlock_kernel(); return res; out_notsup: nfs_revalidate_inode(NFS_SERVER(inode), inode); res = vfs_permission(inode, mask); unlock_kernel(); return res; add_cache: cache->jiffies = jiffies; if (cache->cred) put_rpccred(cache->cred); cache->cred = cred; cache->mask = mask; cache->err = res; unlock_kernel(); return res; }