/* * Destroy an NFS2/3 superblock */ static void nfs_kill_super(struct super_block *s) { struct nfs_server *server = NFS_SB(s); kill_anon_super(s); nfs_free_server(server); }
/** * nfs_do_submount - set up mountpoint when crossing a filesystem boundary * @mnt_parent - mountpoint of parent directory * @dentry - parent directory * @fh - filehandle for new root dentry * @fattr - attributes for new root inode * */ static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, const struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr) { struct nfs_clone_mount mountdata = { .sb = mnt_parent->mnt_sb, .dentry = dentry, .fh = fh, .fattr = fattr, }; struct vfsmount *mnt = ERR_PTR(-ENOMEM); char *page = (char *) __get_free_page(GFP_USER); char *devname; dprintk("--> nfs_do_submount()\n"); dprintk("%s: submounting on %s/%s\n", __func__, dentry->d_parent->d_name.name, dentry->d_name.name); if (page == NULL) goto out; devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); mnt = (struct vfsmount *)devname; if (IS_ERR(devname)) goto free_page; mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata); free_page: free_page((unsigned long)page); out: dprintk("%s: done\n", __func__); dprintk("<-- nfs_do_submount() = %p\n", mnt); return mnt; }
/* * Get the per-inode cache cookie for an NFS inode. */ static void nfs_fscache_enable_inode_cookie(struct inode *inode) { struct super_block *sb = inode->i_sb; struct nfs_inode *nfsi = NFS_I(inode); if (nfsi->fscache || !NFS_FSCACHE(inode)) return; if ((NFS_SB(sb)->options & NFS_OPTION_FSCACHE)) { nfsi->fscache = fscache_acquire_cookie( NFS_SB(sb)->fscache, &nfs_fscache_inode_object_def, nfsi); // dfprintk(FSCACHE, "NFS: get FH cookie (0x%p/0x%p/0x%p)\n", ; } }
static int nfs_compare_super(struct super_block *sb, void *data) { struct nfs_server *server = data, *old = NFS_SB(sb); if (old->nfs_client != server->nfs_client) return 0; if (memcmp(&old->fsid, &server->fsid, sizeof(old->fsid)) != 0) return 0; return 1; }
static void nfs4_kill_super(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); nfs_return_all_delegations(sb); kill_anon_super(sb); nfs4_renewd_prepare_shutdown(server); nfs_free_server(server); }
/* * Deliver file system statistics to userspace */ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct nfs_server *server = NFS_SB(dentry->d_sb); unsigned char blockbits; unsigned long blockres; struct nfs_fh *fh = NFS_FH(dentry->d_inode); struct nfs_fattr fattr; struct nfs_fsstat res = { .fattr = &fattr, }; int error; lock_kernel(); error = server->nfs_client->rpc_ops->statfs(server, fh, &res); buf->f_type = NFS_SUPER_MAGIC; if (error < 0) goto out_err; /* * Current versions of glibc do not correctly handle the * case where f_frsize != f_bsize. Eventually we want to * report the value of wtmult in this field. */ buf->f_frsize = dentry->d_sb->s_blocksize; /* * On most *nix systems, f_blocks, f_bfree, and f_bavail * are reported in units of f_frsize. Linux hasn't had * an f_frsize field in its statfs struct until recently, * thus historically Linux's sys_statfs reports these * fields in units of f_bsize. */ buf->f_bsize = dentry->d_sb->s_blocksize; blockbits = dentry->d_sb->s_blocksize_bits; 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; buf->f_namelen = server->namelen; out: unlock_kernel(); return 0; out_err: dprintk("%s: statfs error = %d\n", __FUNCTION__, -error); buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; goto out; }
/* * Describe the mount options on this VFS mountpoint */ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) { struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); nfs_show_mount_options(m, nfss, 0); seq_puts(m, ",addr="); seq_escape(m, nfss->nfs_client->cl_hostname, " \t\n\\"); return 0; }
/* * get an NFS4 root dentry from the root filehandle */ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) { struct nfs_server *server = NFS_SB(sb); struct nfs_fattr fattr; struct dentry *mntroot; struct inode *inode; int error; dprintk("--> nfs4_get_root()\n"); /* get the info about the server and filesystem */ error = nfs4_server_capabilities(server, mntfh); if (error < 0) { dprintk("nfs_get_root: getcaps error = %d\n", -error); return ERR_PTR(error); } /* get the actual root for this mount */ error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); if (error < 0) { dprintk("nfs_get_root: getattr error = %d\n", -error); return ERR_PTR(error); } inode = nfs_fhget(sb, mntfh, &fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); return ERR_CAST(inode); } error = nfs_superblock_set_dummy_root(sb, inode); if (error != 0) return ERR_PTR(error); /* root dentries normally start off anonymous and get spliced in later * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ mntroot = d_obtain_alias(inode); if (IS_ERR(mntroot)) { dprintk("nfs_get_root: get root dentry failed\n"); return mntroot; } security_d_instantiate(mntroot, inode); if (!mntroot->d_op) mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; dprintk("<-- nfs4_get_root()\n"); return mntroot; }
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, char *page, char *page2, const struct nfs4_fs_location *location) { const size_t addr_bufsize = sizeof(struct sockaddr_storage); struct vfsmount *mnt = ERR_PTR(-ENOENT); char *mnt_path; unsigned int maxbuflen; unsigned int s; mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); if (IS_ERR(mnt_path)) return ERR_CAST(mnt_path); mountdata->mnt_path = mnt_path; maxbuflen = mnt_path - 1 - page2; mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL); if (mountdata->addr == NULL) return ERR_PTR(-ENOMEM); for (s = 0; s < location->nservers; s++) { const struct nfs4_string *buf = &location->servers[s]; if (buf->len <= 0 || buf->len >= maxbuflen) continue; if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) continue; mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, mountdata->addr, addr_bufsize, NFS_SB(mountdata->sb)); if (mountdata->addrlen == 0) continue; rpc_set_port(mountdata->addr, NFS_PORT); memcpy(page2, buf->data, buf->len); page2[buf->len] = '\0'; mountdata->hostname = page2; snprintf(page, PAGE_SIZE, "%s:%s", mountdata->hostname, mountdata->mnt_path); mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata); if (!IS_ERR(mnt)) break; } kfree(mountdata->addr); return mnt; }
/* * get an NFS2/NFS3 root dentry from the root filehandle */ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) { struct nfs_server *server = NFS_SB(sb); struct nfs_fsinfo fsinfo; struct dentry *ret; struct inode *inode; int error; /* get the actual root for this mount */ fsinfo.fattr = nfs_alloc_fattr(); if (fsinfo.fattr == NULL) return ERR_PTR(-ENOMEM); error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); if (error < 0) { dprintk("nfs_get_root: getattr error = %d\n", -error); ret = ERR_PTR(error); goto out; } inode = nfs_fhget(sb, mntfh, fsinfo.fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); ret = ERR_CAST(inode); goto out; } error = nfs_superblock_set_dummy_root(sb, inode); if (error != 0) { ret = ERR_PTR(error); goto out; } /* root dentries normally start off anonymous and get spliced in later * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ ret = d_obtain_alias(inode); if (IS_ERR(ret)) { dprintk("nfs_get_root: get root dentry failed\n"); goto out; } security_d_instantiate(ret, inode); if (ret->d_op == NULL) ret->d_op = server->nfs_client->rpc_ops->dentry_ops; out: nfs_free_fattr(fsinfo.fattr); return ret; }
/** * nfs_do_submount - set up mountpoint when crossing a filesystem boundary * @dentry - parent directory * @fh - filehandle for new root dentry * @fattr - attributes for new root inode * @authflavor - security flavor to use when performing the mount * */ struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr, rpc_authflavor_t authflavor) { struct nfs_clone_mount mountdata = { .sb = dentry->d_sb, .dentry = dentry, .fh = fh, .fattr = fattr, .authflavor = authflavor, }; struct vfsmount *mnt = ERR_PTR(-ENOMEM); char *page = (char *) __get_free_page(GFP_USER); char *devname; dprintk("--> nfs_do_submount()\n"); dprintk("%s: submounting on %s/%s\n", __func__, dentry->d_parent->d_name.name, dentry->d_name.name); if (page == NULL) goto out; devname = nfs_devname(dentry, page, PAGE_SIZE); mnt = (struct vfsmount *)devname; if (IS_ERR(devname)) goto free_page; mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata); free_page: free_page((unsigned long)page); out: dprintk("%s: done\n", __func__); dprintk("<-- nfs_do_submount() = %p\n", mnt); return mnt; } EXPORT_SYMBOL_GPL(nfs_do_submount); struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr) { int err; struct dentry *parent = dget_parent(dentry); /* Look it up again to get its attributes */ err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr); dput(parent); if (err != 0) return ERR_PTR(err); return nfs_do_submount(dentry, fh, fattr, server->client->cl_auth->au_flavor); }
/* * release a per-superblock cookie */ void nfs_fscache_release_super_cookie(struct super_block *sb) { struct nfs_server *nfss = NFS_SB(sb); // dfprintk(FSCACHE, "NFS: releasing superblock cookie (0x%p/0x%p)\n", ; fscache_relinquish_cookie(nfss->fscache, 0); nfss->fscache = NULL; if (nfss->fscache_key) { spin_lock(&nfs_fscache_keys_lock); rb_erase(&nfss->fscache_key->node, &nfs_fscache_keys); spin_unlock(&nfs_fscache_keys_lock); kfree(nfss->fscache_key); nfss->fscache_key = NULL; } }
/* * Initialise the common bits of the superblock */ static inline void nfs_initialise_sb(struct super_block *sb) { struct nfs_server *server = NFS_SB(sb); sb->s_magic = NFS_SUPER_MAGIC; /* We probably want something more informative here */ snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); if (sb->s_blocksize == 0) sb->s_blocksize = nfs_block_bits(server->wsize, &sb->s_blocksize_bits); if (server->flags & NFS_MOUNT_NOAC) sb->s_flags |= MS_SYNCHRONOUS; nfs_super_set_maxbytes(sb, server->maxfilesize); }
/** * nfs_do_submount - set up mountpoint when crossing a filesystem boundary * @dentry - parent directory * @fh - filehandle for new root dentry * @fattr - attributes for new root inode * @authflavor - security flavor to use when performing the mount * */ struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr, rpc_authflavor_t authflavor) { struct nfs_clone_mount mountdata = { .sb = dentry->d_sb, .dentry = dentry, .fh = fh, .fattr = fattr, .authflavor = authflavor, }; struct vfsmount *mnt; char *page = (char *) __get_free_page(GFP_USER); char *devname; if (page == NULL) return ERR_PTR(-ENOMEM); devname = nfs_devname(dentry, page, PAGE_SIZE); if (IS_ERR(devname)) mnt = (struct vfsmount *)devname; else mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata); free_page((unsigned long)page); return mnt; } EXPORT_SYMBOL_GPL(nfs_do_submount); struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr) { int err; struct dentry *parent = dget_parent(dentry); /* Look it up again to get its attributes */ err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name, fh, fattr, NULL); dput(parent); if (err != 0) return ERR_PTR(err); return nfs_do_submount(dentry, fh, fattr, server->client->cl_auth->au_flavor); }
/* * Finish setting up a cloned NFS2/3 superblock */ static void nfs_clone_super(struct super_block *sb, const struct super_block *old_sb) { struct nfs_server *server = NFS_SB(sb); sb->s_blocksize_bits = old_sb->s_blocksize_bits; sb->s_blocksize = old_sb->s_blocksize; sb->s_maxbytes = old_sb->s_maxbytes; if (server->flags & NFS_MOUNT_VER3) { /* The VFS shouldn't apply the umask to mode bits. We will do * so ourselves when necessary. */ sb->s_flags |= MS_POSIXACL; sb->s_time_gran = 1; } sb->s_op = old_sb->s_op; nfs_initialise_sb(sb); }
/* * Finish setting up an NFS2/3 superblock */ static void nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data) { struct nfs_server *server = NFS_SB(sb); sb->s_blocksize_bits = 0; sb->s_blocksize = 0; if (data->bsize) sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); if (server->flags & NFS_MOUNT_VER3) { /* The VFS shouldn't apply the umask to mode bits. We will do * so ourselves when necessary. */ sb->s_flags |= MS_POSIXACL; sb->s_time_gran = 1; } sb->s_op = &nfs_sops; nfs_initialise_sb(sb); }
/* * Initialise the per-inode cache cookie pointer for an NFS inode. */ void nfs_fscache_init_inode(struct inode *inode) { struct nfs_fscache_inode_auxdata auxdata; struct nfs_inode *nfsi = NFS_I(inode); nfsi->fscache = NULL; if (!S_ISREG(inode->i_mode)) return; memset(&auxdata, 0, sizeof(auxdata)); auxdata.mtime = timespec64_to_timespec(nfsi->vfs_inode.i_mtime); auxdata.ctime = timespec64_to_timespec(nfsi->vfs_inode.i_ctime); if (NFS_SERVER(&nfsi->vfs_inode)->nfs_client->rpc_ops->version == 4) auxdata.change_attr = inode_peek_iversion_raw(&nfsi->vfs_inode); nfsi->fscache = fscache_acquire_cookie(NFS_SB(inode->i_sb)->fscache, &nfs_fscache_inode_object_def, nfsi->fh.data, nfsi->fh.size, &auxdata, sizeof(auxdata), nfsi, nfsi->vfs_inode.i_size, false); }
/** * nfs_follow_referral - set up mountpoint when hitting a referral on moved error * @mnt_parent - mountpoint of parent directory * @dentry - parent directory * @locations - array of NFSv4 server location information * */ static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, const struct dentry *dentry, const struct nfs4_fs_locations *locations) { struct vfsmount *mnt = ERR_PTR(-ENOENT); struct nfs_clone_mount mountdata = { .sb = mnt_parent->mnt_sb, .dentry = dentry, .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, }; char *page = NULL, *page2 = NULL; unsigned int s; int loc, error; if (locations == NULL || locations->nlocations <= 0) goto out; dprintk("%s: referral at %s/%s\n", __func__, dentry->d_parent->d_name.name, dentry->d_name.name); page = (char *) __get_free_page(GFP_USER); if (!page) goto out; page2 = (char *) __get_free_page(GFP_USER); if (!page2) goto out; /* Ensure fs path is a prefix of current dentry path */ error = nfs4_validate_fspath(mnt_parent, dentry, locations, page, page2); if (error < 0) { mnt = ERR_PTR(error); goto out; } loc = 0; while (loc < locations->nlocations && IS_ERR(mnt)) { const struct nfs4_fs_location *location = &locations->locations[loc]; char *mnt_path; if (location == NULL || location->nservers <= 0 || location->rootpath.ncomponents == 0) { loc++; continue; } mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); if (IS_ERR(mnt_path)) { loc++; continue; } mountdata.mnt_path = mnt_path; s = 0; while (s < location->nservers) { struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(NFS_PORT), }; if (location->servers[s].len <= 0 || valid_ipaddr4(location->servers[s].data) < 0) { s++; continue; } mountdata.hostname = location->servers[s].data; addr.sin_addr.s_addr = in_aton(mountdata.hostname), mountdata.addr = (struct sockaddr *)&addr; mountdata.addrlen = sizeof(addr); snprintf(page, PAGE_SIZE, "%s:%s", mountdata.hostname, mountdata.mnt_path); mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, &mountdata); if (!IS_ERR(mnt)) { break; } s++; } loc++; } out: free_page((unsigned long) page); free_page((unsigned long) page2); dprintk("%s: done\n", __func__); return mnt; } /* * nfs_do_refmount - handle crossing a referral on server * @dentry - dentry of referral * @nd - nameidata info * */ struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) { struct vfsmount *mnt = ERR_PTR(-ENOMEM); struct dentry *parent; struct nfs4_fs_locations *fs_locations = NULL; struct page *page; int err; /* BUG_ON(IS_ROOT(dentry)); */ dprintk("%s: enter\n", __func__); page = alloc_page(GFP_KERNEL); if (page == NULL) goto out; fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); if (fs_locations == NULL) goto out_free; /* Get locations */ mnt = ERR_PTR(-ENOENT); parent = dget_parent(dentry); dprintk("%s: getting locations for %s/%s\n", __func__, parent->d_name.name, dentry->d_name.name); err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page); dput(parent); if (err != 0 || fs_locations->nlocations <= 0 || fs_locations->fs_path.ncomponents <= 0) goto out_free; mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations); out_free: __free_page(page); kfree(fs_locations); out: dprintk("%s: done\n", __func__); return mnt; }
/* * Get the cache cookie for an NFS superblock. We have to handle * uniquification here because the cache doesn't do it for us. * * The default uniquifier is just an empty string, but it may be overridden * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent * superblock across an automount point of some nature. */ void nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, struct nfs_clone_mount *mntdata) { struct nfs_fscache_key *key, *xkey; struct nfs_server *nfss = NFS_SB(sb); struct rb_node **p, *parent; int diff, ulen; if (uniq) { ulen = strlen(uniq); } else if (mntdata) { struct nfs_server *mnt_s = NFS_SB(mntdata->sb); if (mnt_s->fscache_key) { uniq = mnt_s->fscache_key->key.uniquifier; ulen = mnt_s->fscache_key->key.uniq_len; } } if (!uniq) { uniq = ""; ulen = 1; } key = kzalloc(sizeof(*key) + ulen, GFP_KERNEL); if (!key) return; key->nfs_client = nfss->nfs_client; key->key.super.s_flags = sb->s_flags & NFS_MS_MASK; key->key.nfs_server.flags = nfss->flags; key->key.nfs_server.rsize = nfss->rsize; key->key.nfs_server.wsize = nfss->wsize; key->key.nfs_server.acregmin = nfss->acregmin; key->key.nfs_server.acregmax = nfss->acregmax; key->key.nfs_server.acdirmin = nfss->acdirmin; key->key.nfs_server.acdirmax = nfss->acdirmax; key->key.nfs_server.fsid = nfss->fsid; key->key.rpc_auth.au_flavor = nfss->client->cl_auth->au_flavor; key->key.uniq_len = ulen; memcpy(key->key.uniquifier, uniq, ulen); spin_lock(&nfs_fscache_keys_lock); p = &nfs_fscache_keys.rb_node; parent = NULL; while (*p) { parent = *p; xkey = rb_entry(parent, struct nfs_fscache_key, node); if (key->nfs_client < xkey->nfs_client) goto go_left; if (key->nfs_client > xkey->nfs_client) goto go_right; diff = memcmp(&key->key, &xkey->key, sizeof(key->key)); if (diff < 0) goto go_left; if (diff > 0) goto go_right; if (key->key.uniq_len == 0) goto non_unique; diff = memcmp(key->key.uniquifier, xkey->key.uniquifier, key->key.uniq_len); if (diff < 0) goto go_left; if (diff > 0) goto go_right; goto non_unique; go_left: p = &(*p)->rb_left; continue; go_right: p = &(*p)->rb_right; } rb_link_node(&key->node, parent, p); rb_insert_color(&key->node, &nfs_fscache_keys); spin_unlock(&nfs_fscache_keys_lock); nfss->fscache_key = key; /* create a cache index for looking up filehandles */ nfss->fscache = fscache_acquire_cookie(nfss->nfs_client->fscache, &nfs_fscache_super_index_def, nfss); // dfprintk(FSCACHE, "NFS: get superblock cookie (0x%p/0x%p)\n", ; return; non_unique: spin_unlock(&nfs_fscache_keys_lock); kfree(key); nfss->fscache_key = NULL; nfss->fscache = NULL; // printk(KERN_WARNING "NFS:" ; }
/* * 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); unsigned long hash; nfs_attr_check_mountpoint(sb, fattr); if (((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0) && !nfs_attr_use_mounted_on_fileid(fattr)) goto out_no_inode; 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); goto out_no_inode; } if (inode->i_state & I_NEW) { struct nfs_inode *nfsi = NFS_I(inode); 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 | 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. */ inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops; if (S_ISREG(inode->i_mode)) { 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_fop = &nfs_dir_operations; inode->i_data.a_ops = &nfs_dir_aops; if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)) set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); /* 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)); nfsi->change_attr = 0; inode->i_size = 0; inode->i_nlink = 0; inode->i_uid = -2; inode->i_gid = -2; inode->i_blocks = 0; memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); 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 | 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) #ifdef CONFIG_ROOT_NFS_UID if ((fattr->uid) && (fattr->uid == (uid_t) (NFS_CLIENT(inode)->cl_rootuid))) inode->i_uid = 0; else inode->i_uid = fattr->uid; #else inode->i_uid = fattr->uid; #endif 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) #ifdef CONFIG_ROOT_NFS_UID if ((fattr->gid) && (fattr->gid == (gid_t) (NFS_CLIENT(inode)->cl_rootgid))) inode->i_gid = 0; else inode->i_gid = fattr->gid; #else inode->i_gid = fattr->gid; #endif else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
/* * get an NFS2/NFS3 root dentry from the root filehandle */ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, const char *devname) { struct nfs_server *server = NFS_SB(sb); struct nfs_fsinfo fsinfo; struct dentry *ret; struct inode *inode; void *name = kstrdup(devname, GFP_KERNEL); int error; if (!name) return ERR_PTR(-ENOMEM); /* get the actual root for this mount */ fsinfo.fattr = nfs_alloc_fattr(); if (fsinfo.fattr == NULL) { kfree(name); return ERR_PTR(-ENOMEM); } error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); if (error < 0) { dprintk("nfs_get_root: getattr error = %d\n", -error); ret = ERR_PTR(error); goto out; } inode = nfs_fhget(sb, mntfh, fsinfo.fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); ret = ERR_CAST(inode); goto out; } error = nfs_superblock_set_dummy_root(sb, inode); if (error != 0) { ret = ERR_PTR(error); goto out; } /* root dentries normally start off anonymous and get spliced in later * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ ret = d_obtain_alias(inode); if (IS_ERR(ret)) { dprintk("nfs_get_root: get root dentry failed\n"); goto out; } security_d_instantiate(ret, inode); spin_lock(&ret->d_lock); if (IS_ROOT(ret) && !(ret->d_flags & DCACHE_NFSFS_RENAMED)) { ret->d_fsdata = name; name = NULL; } spin_unlock(&ret->d_lock); out: if (name) kfree(name); nfs_free_fattr(fsinfo.fattr); return ret; }
/* * Clone an NFS4 server record on xdev traversal (FSID-change) */ static int nfs4_xdev_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) { struct nfs_clone_mount *data = raw_data; struct super_block *s; struct nfs_server *server; struct dentry *mntroot; int error; dprintk("--> nfs4_xdev_get_sb()\n"); /* create a new volume representation */ server = nfs_clone_server(NFS_SB(data->sb), data->fh, data->fattr); if (IS_ERR(server)) { error = PTR_ERR(server); goto out_err_noserver; } /* Get a superblock - note that we may end up sharing one that already exists */ s = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); if (IS_ERR(s)) { error = PTR_ERR(s); goto out_err_nosb; } if (s->s_fs_info != server) { nfs_free_server(server); server = NULL; } if (!s->s_root) { /* initial superblock/root creation */ s->s_flags = flags; nfs4_clone_super(s, data->sb); } mntroot = nfs4_get_root(s, data->fh); if (IS_ERR(mntroot)) { error = PTR_ERR(mntroot); goto error_splat_super; } s->s_flags |= MS_ACTIVE; mnt->mnt_sb = s; mnt->mnt_root = mntroot; dprintk("<-- nfs4_xdev_get_sb() = 0\n"); return 0; out_err_nosb: nfs_free_server(server); out_err_noserver: dprintk("<-- nfs4_xdev_get_sb() = %d [error]\n", error); return error; error_splat_super: up_write(&s->s_umount); deactivate_super(s); dprintk("<-- nfs4_xdev_get_sb() = %d [splat]\n", error); return error; }
/** * nfs_follow_referral - set up mountpoint when hitting a referral on moved error * @dentry - parent directory * @locations - array of NFSv4 server location information * */ static struct vfsmount *nfs_follow_referral(struct dentry *dentry, const struct nfs4_fs_locations *locations) { struct vfsmount *mnt = ERR_PTR(-ENOENT); struct nfs_clone_mount mountdata = { .sb = dentry->d_sb, .dentry = dentry, .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor, }; char *page = NULL, *page2 = NULL; int loc, error; if (locations == NULL || locations->nlocations <= 0) goto out; dprintk("%s: referral at %s/%s\n", __func__, dentry->d_parent->d_name.name, dentry->d_name.name); page = (char *) __get_free_page(GFP_USER); if (!page) goto out; page2 = (char *) __get_free_page(GFP_USER); if (!page2) goto out; /* Ensure fs path is a prefix of current dentry path */ error = nfs4_validate_fspath(dentry, locations, page, page2); if (error < 0) { mnt = ERR_PTR(error); goto out; } for (loc = 0; loc < locations->nlocations; loc++) { const struct nfs4_fs_location *location = &locations->locations[loc]; if (location == NULL || location->nservers <= 0 || location->rootpath.ncomponents == 0) continue; mnt = try_location(&mountdata, page, page2, location); if (!IS_ERR(mnt)) break; } out: free_page((unsigned long) page); free_page((unsigned long) page2); dprintk("%s: done\n", __func__); return mnt; } /* * nfs_do_refmount - handle crossing a referral on server * @dentry - dentry of referral * */ struct vfsmount *nfs_do_refmount(struct dentry *dentry) { struct vfsmount *mnt = ERR_PTR(-ENOMEM); struct dentry *parent; struct nfs4_fs_locations *fs_locations = NULL; struct page *page; int err; /* BUG_ON(IS_ROOT(dentry)); */ dprintk("%s: enter\n", __func__); page = alloc_page(GFP_KERNEL); if (page == NULL) goto out; fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); if (fs_locations == NULL) goto out_free; /* Get locations */ mnt = ERR_PTR(-ENOENT); parent = dget_parent(dentry); dprintk("%s: getting locations for %s/%s\n", __func__, parent->d_name.name, dentry->d_name.name); err = nfs4_proc_fs_locations(parent->d_inode, &dentry->d_name, fs_locations, page); dput(parent); if (err != 0 || fs_locations->nlocations <= 0 || fs_locations->fs_path.ncomponents <= 0) goto out_free; mnt = nfs_follow_referral(dentry, fs_locations); out_free: __free_page(page); kfree(fs_locations); out: dprintk("%s: done\n", __func__); return mnt; }
/* * get an NFS2/NFS3 root dentry from the root filehandle */ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) { struct nfs_server *server = NFS_SB(sb); struct nfs_fsinfo fsinfo; struct nfs_fattr fattr; struct dentry *mntroot; struct inode *inode; int error; /* create a dummy root dentry with dummy inode for this superblock */ if (!sb->s_root) { struct nfs_fh dummyfh; struct dentry *root; struct inode *iroot; memset(&dummyfh, 0, sizeof(dummyfh)); memset(&fattr, 0, sizeof(fattr)); nfs_fattr_init(&fattr); fattr.valid = NFS_ATTR_FATTR; fattr.type = NFDIR; fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; fattr.nlink = 2; iroot = nfs_fhget(sb, &dummyfh, &fattr); if (IS_ERR(iroot)) return ERR_PTR(PTR_ERR(iroot)); root = d_alloc_root(iroot); if (!root) { iput(iroot); return ERR_PTR(-ENOMEM); } sb->s_root = root; } /* get the actual root for this mount */ fsinfo.fattr = &fattr; error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); if (error < 0) { dprintk("nfs_get_root: getattr error = %d\n", -error); return ERR_PTR(error); } inode = nfs_fhget(sb, mntfh, fsinfo.fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); return ERR_PTR(PTR_ERR(inode)); } /* root dentries normally start off anonymous and get spliced in later * if the dentry tree reaches them; however if the dentry already * exists, we'll pick it up at this point and use it as the root */ mntroot = d_alloc_anon(inode); if (!mntroot) { iput(inode); dprintk("nfs_get_root: get root dentry failed\n"); return ERR_PTR(-ENOMEM); } security_d_instantiate(mntroot, inode); if (!mntroot->d_op) mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops; return mntroot; }
/* * Present statistical information for this VFS mountpoint */ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) { int i, cpu; struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); struct rpc_auth *auth = nfss->client->cl_auth; struct nfs_iostats totals = { }; seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS); /* * Display all mount option settings */ seq_printf(m, "\n\topts:\t"); seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw"); seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : ""); seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : ""); seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : ""); nfs_show_mount_options(m, nfss, 1); seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ); seq_printf(m, "\n\tcaps:\t"); seq_printf(m, "caps=0x%x", nfss->caps); seq_printf(m, ",wtmult=%d", nfss->wtmult); seq_printf(m, ",dtsize=%d", nfss->dtsize); seq_printf(m, ",bsize=%d", nfss->bsize); seq_printf(m, ",namelen=%d", nfss->namelen); #ifdef CONFIG_NFS_V4 if (nfss->nfs_client->cl_nfsversion == 4) { seq_printf(m, "\n\tnfsv4:\t"); seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); seq_printf(m, ",acl=0x%x", nfss->acl_bitmask); } #endif /* * Display security flavor in effect for this mount */ seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor); if (auth->au_flavor) seq_printf(m, ",pseudoflavor=%d", auth->au_flavor); /* * Display superblock I/O counters */ for_each_possible_cpu(cpu) { struct nfs_iostats *stats; preempt_disable(); stats = per_cpu_ptr(nfss->io_stats, cpu); for (i = 0; i < __NFSIOS_COUNTSMAX; i++) totals.events[i] += stats->events[i]; for (i = 0; i < __NFSIOS_BYTESMAX; i++) totals.bytes[i] += stats->bytes[i]; preempt_enable(); } seq_printf(m, "\n\tevents:\t"); for (i = 0; i < __NFSIOS_COUNTSMAX; i++) seq_printf(m, "%lu ", totals.events[i]); seq_printf(m, "\n\tbytes:\t"); for (i = 0; i < __NFSIOS_BYTESMAX; i++) seq_printf(m, "%Lu ", totals.bytes[i]); seq_printf(m, "\n"); rpc_print_iostats(m, nfss->client); return 0; }