📄 inode.c
字号:
/* * linux/fs/nfs/inode.c * * Copyright (C) 1992 Rick Sladkey * * nfs inode and superblock handling functions * * Modularised by Alan Cox <Alan.Cox@linux.org>, 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/config.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/stat.h>#include <linux/errno.h>#include <linux/locks.h>#include <linux/unistd.h>#include <linux/sunrpc/clnt.h>#include <linux/sunrpc/stats.h>#include <linux/nfs_fs.h>#include <linux/nfs_mount.h>#include <linux/nfs_flushd.h>#include <linux/lockd/bind.h>#include <linux/smp_lock.h>#include <asm/system.h>#include <asm/uaccess.h>#define CONFIG_NFS_SNAPSHOT 1#define NFSDBG_FACILITY NFSDBG_VFS#define NFS_PARANOIA 1static struct inode * __nfs_fhget(struct super_block *, struct nfs_fh *, struct nfs_fattr *);void nfs_zap_caches(struct inode *);static void nfs_invalidate_inode(struct inode *);static void nfs_read_inode(struct inode *);static void nfs_delete_inode(struct inode *);static void nfs_put_super(struct super_block *);static void nfs_umount_begin(struct super_block *);static int nfs_statfs(struct super_block *, struct statfs *);static struct super_operations nfs_sops = { read_inode: nfs_read_inode, put_inode: force_delete, delete_inode: nfs_delete_inode, put_super: nfs_put_super, statfs: nfs_statfs, umount_begin: nfs_umount_begin,};/* * RPC cruft for NFS */struct rpc_stat nfs_rpcstat = { &nfs_program };static struct rpc_version * nfs_version[] = { NULL, NULL, &nfs_version2,#ifdef CONFIG_NFS_V3 &nfs_version3,#endif};struct rpc_program nfs_program = { "nfs", NFS_PROGRAM, sizeof(nfs_version) / sizeof(nfs_version[0]), nfs_version, &nfs_rpcstat,};static inline unsigned longnfs_fattr_to_ino_t(struct nfs_fattr *fattr){ return nfs_fileid_to_ino_t(fattr->fileid);}/* * 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 voidnfs_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;}static voidnfs_delete_inode(struct inode * inode){ dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); /* * The following can never actually happen... */ if (nfs_have_writebacks(inode) || nfs_have_read(inode)) { printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); } clear_inode(inode);}voidnfs_put_super(struct super_block *sb){ struct nfs_server *server = &sb->u.nfs_sb.s_server; struct rpc_clnt *rpc; /* * First get rid of the request flushing daemon. * Relies on rpc_shutdown_client() waiting on all * client tasks to finish. */ nfs_reqlist_exit(server); if ((rpc = server->client) != NULL) rpc_shutdown_client(rpc); nfs_reqlist_free(server); if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ rpciod_down(); /* release rpciod */ kfree(server->hostname);}voidnfs_umount_begin(struct super_block *sb){ struct nfs_server *server = &sb->u.nfs_sb.s_server; struct rpc_clnt *rpc; /* -EIO all pending I/O */ if ((rpc = server->client) != NULL) rpc_killall_tasks(rpc);}static inline unsigned longnfs_block_bits(unsigned long bsize, unsigned char *nrbitsp){ /* make sure blocksize is a power of two */ if ((bsize & (bsize - 1)) || nrbitsp) { unsigned char nrbits; for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--) ; bsize = 1 << nrbits; if (nrbitsp) *nrbitsp = nrbits; } return bsize;}/* * Calculate the number of 512byte blocks used. */static inline unsigned longnfs_calc_block_size(u64 tsize){ loff_t used = (tsize + 511) >> 9; return (used > ULONG_MAX) ? ULONG_MAX : used;}/* * Compute and set NFS server blocksize */static inline unsigned longnfs_block_size(unsigned long bsize, unsigned char *nrbitsp){ if (bsize < 1024) bsize = NFS_DEF_FILE_IO_BUFFER_SIZE; else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE) bsize = NFS_MAX_FILE_IO_BUFFER_SIZE; return nfs_block_bits(bsize, nrbitsp);}/* * Obtain the root inode of the file system. */static struct inode *nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh){ struct nfs_server *server = &sb->u.nfs_sb.s_server; struct nfs_fattr fattr; struct inode *inode; int error; if ((error = server->rpc_ops->getroot(server, rootfh, &fattr)) < 0) { printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error); return NULL; } inode = __nfs_fhget(sb, rootfh, &fattr); return inode;}/* * The way this works is that the mount process passes a structure * in the data argument which contains the server's IP address * and the root file handle obtained from the server's mount * daemon. We stash these away in the private superblock fields. */struct super_block *nfs_read_super(struct super_block *sb, void *raw_data, int silent){ struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data; struct nfs_server *server; struct rpc_xprt *xprt = NULL; struct rpc_clnt *clnt = NULL; struct nfs_fh *root = &data->root, fh; struct inode *root_inode = NULL; unsigned int authflavor; struct sockaddr_in srvaddr; struct rpc_timeout timeparms; struct nfs_fsinfo fsinfo; int tcp, version, maxlen; memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb)); if (!data) goto out_miss_args; memset(&fh, 0, sizeof(fh)); if (data->version != NFS_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", data->version < NFS_MOUNT_VERSION ? "older" : "newer"); if (data->version < 2) data->namlen = 0; if (data->version < 3) data->bsize = 0; if (data->version < 4) { data->flags &= ~NFS_MOUNT_VER3; root = &fh; root->size = NFS2_FHSIZE; memcpy(root->data, data->old_root.data, NFS2_FHSIZE); } } /* We now require that the mount process passes the remote address */ memcpy(&srvaddr, &data->addr, sizeof(srvaddr)); if (srvaddr.sin_addr.s_addr == INADDR_ANY) goto out_no_remote; sb->s_magic = NFS_SUPER_MAGIC; sb->s_op = &nfs_sops; sb->s_blocksize_bits = 0; sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); server = &sb->u.nfs_sb.s_server; server->rsize = nfs_block_size(data->rsize, NULL); server->wsize = nfs_block_size(data->wsize, NULL); server->flags = data->flags & NFS_MOUNT_FLAGMASK; if (data->flags & NFS_MOUNT_NOAC) { data->acregmin = data->acregmax = 0; data->acdirmin = data->acdirmax = 0; } server->acregmin = data->acregmin*HZ; server->acregmax = data->acregmax*HZ; server->acdirmin = data->acdirmin*HZ; server->acdirmax = data->acdirmax*HZ; server->namelen = data->namlen; server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL); if (!server->hostname) goto out_unlock; strcpy(server->hostname, data->hostname); nfsv3_try_again: /* Check NFS protocol revision and initialize RPC op vector * and file handle pool. */ if (data->flags & NFS_MOUNT_VER3) {#ifdef CONFIG_NFS_V3 server->rpc_ops = &nfs_v3_clientops; version = 3; if (data->version < 4) { printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n"); goto out_unlock; }#else printk(KERN_NOTICE "NFS: NFSv3 not supported.\n"); goto out_unlock;#endif } else { server->rpc_ops = &nfs_v2_clientops; version = 2; } /* Which protocol do we use? */ tcp = (data->flags & NFS_MOUNT_TCP); /* Initialize timeout values */ timeparms.to_initval = data->timeo * HZ / 10; timeparms.to_retries = data->retrans; timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT; timeparms.to_exponential = 1; if (!timeparms.to_initval) timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10; if (!timeparms.to_retries) timeparms.to_retries = 5; /* Now create transport and client */ xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, &srvaddr, &timeparms); if (xprt == NULL) goto out_no_xprt; /* Choose authentication flavor */ authflavor = RPC_AUTH_UNIX; if (data->flags & NFS_MOUNT_SECURE) authflavor = RPC_AUTH_DES; else if (data->flags & NFS_MOUNT_KERBEROS) authflavor = RPC_AUTH_KRB; clnt = rpc_create_client(xprt, server->hostname, &nfs_program, version, authflavor); if (clnt == NULL) goto out_no_client; 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; /* Fire up rpciod if not yet running */ if (rpciod_up() != 0) goto out_no_iod; /* * Keep the super block locked while we try to get * the root fh attributes. */ /* Did getting the root inode fail? */ if (!(root_inode = nfs_get_root(sb, root)) && (data->flags & NFS_MOUNT_VER3)) { data->flags &= ~NFS_MOUNT_VER3; rpciod_down(); rpc_shutdown_client(server->client); goto nfsv3_try_again; } if (!root_inode) goto out_no_root; sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) goto out_no_root; sb->s_root->d_op = &nfs_dentry_operations; /* Get some general file system info */ if (server->rpc_ops->statfs(server, root, &fsinfo) >= 0) { if (server->namelen == 0) server->namelen = fsinfo.namelen; } else { printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n"); goto out_no_root; } /* Work out a lot of parameters */ if (data->rsize == 0) server->rsize = nfs_block_size(fsinfo.rtpref, NULL); if (data->wsize == 0) server->wsize = nfs_block_size(fsinfo.wtpref, NULL); /* NFSv3: we don't have bsize, but rather rtmult and wtmult... */ if (!fsinfo.bsize) fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult; /* Also make sure we don't go below rsize/wsize since * RPC calls are expensive */ if (fsinfo.bsize < server->rsize) fsinfo.bsize = server->rsize; if (fsinfo.bsize < server->wsize) fsinfo.bsize = server->wsize; if (data->bsize == 0) sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits); if (server->rsize > fsinfo.rtmax) server->rsize = fsinfo.rtmax; if (server->wsize > fsinfo.wtmax) server->wsize = fsinfo.wtmax; server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (server->rpages > NFS_READ_MAXIOV) { server->rpages = NFS_READ_MAXIOV; server->rsize = server->rpages << PAGE_CACHE_SHIFT; } server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; if (server->wpages > NFS_WRITE_MAXIOV) { server->wpages = NFS_WRITE_MAXIOV; server->wsize = server->wpages << PAGE_CACHE_SHIFT; } server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); if (server->dtsize > PAGE_CACHE_SIZE) server->dtsize = PAGE_CACHE_SIZE; if (server->dtsize > server->rsize) server->dtsize = server->rsize; maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN; if (server->namelen == 0 || server->namelen > maxlen) server->namelen = maxlen; /* Fire up the writeback cache */ if (nfs_reqlist_alloc(server) < 0) { printk(KERN_NOTICE "NFS: cannot initialize writeback cache.\n"); goto failure_kill_reqlist; } /* We're airborne */ /* Check whether to start the lockd process */ if (!(server->flags & NFS_MOUNT_NONLM)) lockd_up(); return sb; /* Yargs. It didn't work out. */ failure_kill_reqlist: nfs_reqlist_exit(server);out_no_root: printk("nfs_read_super: get root inode failed\n"); iput(root_inode); rpciod_down(); goto out_shutdown;out_no_iod: printk(KERN_WARNING "NFS: couldn't start rpciod!\n");out_shutdown: rpc_shutdown_client(server->client); goto out_free_host;out_no_client: printk(KERN_WARNING "NFS: cannot create RPC client.\n"); xprt_destroy(xprt); goto out_free_host;out_no_xprt: printk(KERN_WARNING "NFS: cannot create RPC transport.\n");out_free_host: nfs_reqlist_free(server); kfree(server->hostname);out_unlock: goto out_fail;out_no_remote: printk("NFS: mount program didn't pass remote address!\n"); goto out_fail;out_miss_args: printk("nfs_read_super: missing data argument\n");out_fail: return NULL;}static intnfs_statfs(struct super_block *sb, struct statfs *buf){ struct nfs_server *server = &sb->u.nfs_sb.s_server; unsigned char blockbits; unsigned long blockres; struct nfs_fsinfo res; int error; error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root->d_inode), &res); buf->f_type = NFS_SUPER_MAGIC; if (error < 0) goto out_err; if (res.bsize == 0) res.bsize = sb->s_blocksize; buf->f_bsize = nfs_block_bits(res.bsize, &blockbits); blockres = (1 << blockbits) - 1; buf->f_blocks = (res.tbytes + blockres) >> blockbits; buf->f_bfree = (res.fbytes + blockres) >> blockbits; buf->f_bavail = (res.abytes + blockres) >> blockbits; buf->f_files = res.tfiles; buf->f_ffree = res.afiles; if (res.namelen == 0 || res.namelen > server->namelen) res.namelen = server->namelen; buf->f_namelen = res.namelen; return 0; out_err: printk("nfs_statfs: statfs error = %d\n", -error); buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; return 0;}/* * Invalidate the local caches */voidnfs_zap_caches(struct inode *inode){ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); NFS_ATTRTIMEO_UPDATE(inode) = jiffies; invalidate_inode_pages(inode); memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); NFS_CACHEINV(inode);}/* * Invalidate, but do not unhash, the inode */static voidnfs_invalidate_inode(struct inode *inode){ umode_t save_mode = inode->i_mode; make_bad_inode(inode); inode->i_mode = save_mode; nfs_zap_caches(inode);}/* * Fill in inode information from the fattr.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -