📄 export.c
字号:
*/intexp_unexport(struct nfsctl_export *nxp){ struct auth_domain *dom; svc_export *exp; struct nameidata nd; int err; /* Consistency check */ if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) return -EINVAL; exp_writelock(); err = -EINVAL; dom = auth_domain_find(nxp->ex_client); if (!dom) { dprintk("nfsd: unexport couldn't find %s\n", nxp->ex_client); goto out_unlock; } err = path_lookup(nxp->ex_path, 0, &nd); if (err) goto out_domain; err = -EINVAL; exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); path_release(&nd); if (IS_ERR(exp)) goto out_domain; exp_do_unexport(exp); exp_put(exp); err = 0;out_domain: auth_domain_put(dom); cache_flush();out_unlock: exp_writeunlock(); return err;}/* * Obtain the root fh on behalf of a client. * This could be done in user space, but I feel that it adds some safety * since its harder to fool a kernel module than a user space program. */intexp_rootfh(svc_client *clp, char *path, struct knfsd_fh *f, int maxsize){ struct svc_export *exp; struct nameidata nd; struct inode *inode; struct svc_fh fh; int err; err = -EPERM; /* NB: we probably ought to check that it's NUL-terminated */ if (path_lookup(path, 0, &nd)) { printk("nfsd: exp_rootfh path not found %s", path); return err; } inode = nd.dentry->d_inode; dprintk("nfsd: exp_rootfh(%s [%p] %s:%s/%ld)\n", path, nd.dentry, clp->name, inode->i_sb->s_id, inode->i_ino); exp = exp_parent(clp, nd.mnt, nd.dentry, NULL); if (IS_ERR(exp)) { err = PTR_ERR(exp); goto out; } /* * fh must be initialized before calling fh_compose */ fh_init(&fh, maxsize); if (fh_compose(&fh, exp, nd.dentry, NULL)) err = -EINVAL; else err = 0; memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh)); fh_put(&fh); exp_put(exp);out: path_release(&nd); return err;}static struct svc_export *exp_find(struct auth_domain *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp){ struct svc_export *exp; struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); if (IS_ERR(ek)) return ERR_PTR(PTR_ERR(ek)); exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); cache_put(&ek->h, &svc_expkey_cache); if (IS_ERR(exp)) return ERR_PTR(PTR_ERR(exp)); return exp;}__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp){ struct exp_flavor_info *f; struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; /* legacy gss-only clients are always OK: */ if (exp->ex_client == rqstp->rq_gssclient) return 0; /* ip-address based client; check sec= export option: */ for (f = exp->ex_flavors; f < end; f++) { if (f->pseudoflavor == rqstp->rq_flavor) return 0; } /* defaults in absence of sec= options: */ if (exp->ex_nflavors == 0) { if (rqstp->rq_flavor == RPC_AUTH_NULL || rqstp->rq_flavor == RPC_AUTH_UNIX) return 0; } return nfserr_wrongsec;}/* * Uses rq_client and rq_gssclient to find an export; uses rq_client (an * auth_unix client) if it's available and has secinfo information; * otherwise, will try to use rq_gssclient. * * Called from functions that handle requests; functions that do work on * behalf of mountd are passed a single client name to use, and should * use exp_get_by_name() or exp_find(). */struct svc_export *rqst_exp_get_by_name(struct svc_rqst *rqstp, struct vfsmount *mnt, struct dentry *dentry){ struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT); if (rqstp->rq_client == NULL) goto gss; /* First try the auth_unix client: */ exp = exp_get_by_name(rqstp->rq_client, mnt, dentry, &rqstp->rq_chandle); if (PTR_ERR(exp) == -ENOENT) goto gss; if (IS_ERR(exp)) return exp; /* If it has secinfo, assume there are no gss/... clients */ if (exp->ex_nflavors > 0) return exp;gss: /* Otherwise, try falling back on gss client */ if (rqstp->rq_gssclient == NULL) return exp; gssexp = exp_get_by_name(rqstp->rq_gssclient, mnt, dentry, &rqstp->rq_chandle); if (PTR_ERR(gssexp) == -ENOENT) return exp; if (!IS_ERR(exp)) exp_put(exp); return gssexp;}struct svc_export *rqst_exp_find(struct svc_rqst *rqstp, int fsid_type, u32 *fsidv){ struct svc_export *gssexp, *exp = ERR_PTR(-ENOENT); if (rqstp->rq_client == NULL) goto gss; /* First try the auth_unix client: */ exp = exp_find(rqstp->rq_client, fsid_type, fsidv, &rqstp->rq_chandle); if (PTR_ERR(exp) == -ENOENT) goto gss; if (IS_ERR(exp)) return exp; /* If it has secinfo, assume there are no gss/... clients */ if (exp->ex_nflavors > 0) return exp;gss: /* Otherwise, try falling back on gss client */ if (rqstp->rq_gssclient == NULL) return exp; gssexp = exp_find(rqstp->rq_gssclient, fsid_type, fsidv, &rqstp->rq_chandle); if (PTR_ERR(gssexp) == -ENOENT) return exp; if (!IS_ERR(exp)) exp_put(exp); return gssexp;}struct svc_export *rqst_exp_parent(struct svc_rqst *rqstp, struct vfsmount *mnt, struct dentry *dentry){ struct svc_export *exp; dget(dentry); exp = rqst_exp_get_by_name(rqstp, mnt, dentry); while (PTR_ERR(exp) == -ENOENT && !IS_ROOT(dentry)) { struct dentry *parent; parent = dget_parent(dentry); dput(dentry); dentry = parent; exp = rqst_exp_get_by_name(rqstp, mnt, dentry); } dput(dentry); return exp;}/* * Called when we need the filehandle for the root of the pseudofs, * for a given NFSv4 client. The root is defined to be the * export point with fsid==0 */__be32exp_pseudoroot(struct svc_rqst *rqstp, struct svc_fh *fhp){ struct svc_export *exp; __be32 rv; u32 fsidv[2]; mk_fsid(FSID_NUM, fsidv, 0, 0, 0, NULL); exp = rqst_exp_find(rqstp, FSID_NUM, fsidv); if (PTR_ERR(exp) == -ENOENT) return nfserr_perm; if (IS_ERR(exp)) return nfserrno(PTR_ERR(exp)); rv = fh_compose(fhp, exp, exp->ex_dentry, NULL); if (rv) goto out; rv = check_nfsd_access(exp, rqstp);out: exp_put(exp); return rv;}/* Iterator */static void *e_start(struct seq_file *m, loff_t *pos) __acquires(svc_export_cache.hash_lock){ loff_t n = *pos; unsigned hash, export; struct cache_head *ch; exp_readlock(); read_lock(&svc_export_cache.hash_lock); if (!n--) return SEQ_START_TOKEN; hash = n >> 32; export = n & ((1LL<<32) - 1); for (ch=export_table[hash]; ch; ch=ch->next) if (!export--) return ch; n &= ~((1LL<<32) - 1); do { hash++; n += 1LL<<32; } while(hash < EXPORT_HASHMAX && export_table[hash]==NULL); if (hash >= EXPORT_HASHMAX) return NULL; *pos = n+1; return export_table[hash];}static void *e_next(struct seq_file *m, void *p, loff_t *pos){ struct cache_head *ch = p; int hash = (*pos >> 32); if (p == SEQ_START_TOKEN) hash = 0; else if (ch->next == NULL) { hash++; *pos += 1LL<<32; } else { ++*pos; return ch->next; } *pos &= ~((1LL<<32) - 1); while (hash < EXPORT_HASHMAX && export_table[hash] == NULL) { hash++; *pos += 1LL<<32; } if (hash >= EXPORT_HASHMAX) return NULL; ++*pos; return export_table[hash];}static void e_stop(struct seq_file *m, void *p) __releases(svc_export_cache.hash_lock){ read_unlock(&svc_export_cache.hash_lock); exp_readunlock();}static struct flags { int flag; char *name[2];} expflags[] = { { NFSEXP_READONLY, {"ro", "rw"}}, { NFSEXP_INSECURE_PORT, {"insecure", ""}}, { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}}, { NFSEXP_ALLSQUASH, {"all_squash", ""}}, { NFSEXP_ASYNC, {"async", "sync"}}, { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}}, { NFSEXP_NOHIDE, {"nohide", ""}}, { NFSEXP_CROSSMOUNT, {"crossmnt", ""}}, { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}}, { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},#ifdef MSNFS { NFSEXP_MSNFS, {"msnfs", ""}},#endif { 0, {"", ""}}};static void show_expflags(struct seq_file *m, int flags, int mask){ struct flags *flg; int state, first = 0; for (flg = expflags; flg->flag; flg++) { if (flg->flag & ~mask) continue; state = (flg->flag & flags) ? 0 : 1; if (*flg->name[state]) seq_printf(m, "%s%s", first++?",":"", flg->name[state]); }}static void show_secinfo_flags(struct seq_file *m, int flags){ seq_printf(m, ","); show_expflags(m, flags, NFSEXP_SECINFO_FLAGS);}static void show_secinfo(struct seq_file *m, struct svc_export *exp){ struct exp_flavor_info *f; struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; int lastflags = 0, first = 0; if (exp->ex_nflavors == 0) return; for (f = exp->ex_flavors; f < end; f++) { if (first || f->flags != lastflags) { if (!first) show_secinfo_flags(m, lastflags); seq_printf(m, ",sec=%d", f->pseudoflavor); lastflags = f->flags; } else { seq_printf(m, ":%d", f->pseudoflavor); } } show_secinfo_flags(m, lastflags);}static void exp_flags(struct seq_file *m, int flag, int fsid, uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc){ show_expflags(m, flag, NFSEXP_ALLFLAGS); if (flag & NFSEXP_FSID) seq_printf(m, ",fsid=%d", fsid); if (anonu != (uid_t)-2 && anonu != (0x10000-2)) seq_printf(m, ",anonuid=%u", anonu); if (anong != (gid_t)-2 && anong != (0x10000-2)) seq_printf(m, ",anongid=%u", anong); if (fsloc && fsloc->locations_count > 0) { char *loctype = (fsloc->migrated) ? "refer" : "replicas"; int i; seq_printf(m, ",%s=", loctype); seq_escape(m, fsloc->locations[0].path, ",;@ \t\n\\"); seq_putc(m, '@'); seq_escape(m, fsloc->locations[0].hosts, ",;@ \t\n\\"); for (i = 1; i < fsloc->locations_count; i++) { seq_putc(m, ';'); seq_escape(m, fsloc->locations[i].path, ",;@ \t\n\\"); seq_putc(m, '@'); seq_escape(m, fsloc->locations[i].hosts, ",;@ \t\n\\"); } }}static int e_show(struct seq_file *m, void *p){ struct cache_head *cp = p; struct svc_export *exp = container_of(cp, struct svc_export, h); if (p == SEQ_START_TOKEN) { seq_puts(m, "# Version 1.1\n"); seq_puts(m, "# Path Client(Flags) # IPs\n"); return 0; } cache_get(&exp->h); if (cache_check(&svc_export_cache, &exp->h, NULL)) return 0; cache_put(&exp->h, &svc_export_cache); return svc_export_show(m, &svc_export_cache, cp);}struct seq_operations nfs_exports_op = { .start = e_start, .next = e_next, .stop = e_stop, .show = e_show,};/* * Add or modify a client. * Change requests may involve the list of host addresses. The list of * exports and possibly existing uid maps are left untouched. */intexp_addclient(struct nfsctl_client *ncp){ struct auth_domain *dom; int i, err; /* First, consistency check. */ err = -EINVAL; if (! exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) goto out; if (ncp->cl_naddr > NFSCLNT_ADDRMAX) goto out; /* Lock the hashtable */ exp_writelock(); dom = unix_domain_find(ncp->cl_ident); err = -ENOMEM; if (!dom) goto out_unlock; /* Insert client into hashtable. */ for (i = 0; i < ncp->cl_naddr; i++) auth_unix_add_addr(ncp->cl_addrlist[i], dom); auth_unix_forget_old(dom); auth_domain_put(dom); err = 0;out_unlock: exp_writeunlock();out: return err;}/* * Delete a client given an identifier. */intexp_delclient(struct nfsctl_client *ncp){ int err; struct auth_domain *dom; err = -EINVAL; if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)) goto out; /* Lock the hashtable */ exp_writelock(); dom = auth_domain_find(ncp->cl_ident); /* just make sure that no addresses work * and that it will expire soon */ if (dom) { err = auth_unix_forget_old(dom); auth_domain_put(dom); } exp_writeunlock();out: return err;}/* * Verify that string is non-empty and does not exceed max length. */static intexp_verify_string(char *cp, int max){ int i; for (i = 0; i < max; i++) if (!cp[i]) return i; cp[i] = 0; printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp); return 0;}/* * Initialize the exports module. */voidnfsd_export_init(void){ dprintk("nfsd: initializing export module.\n"); cache_register(&svc_export_cache); cache_register(&svc_expkey_cache);}/* * Flush exports table - called when last nfsd thread is killed */voidnfsd_export_flush(void){ exp_writelock(); cache_purge(&svc_expkey_cache); cache_purge(&svc_export_cache); exp_writeunlock();}/* * Shutdown the exports module. */voidnfsd_export_shutdown(void){ dprintk("nfsd: shutting down export module.\n"); exp_writelock(); if (cache_unregister(&svc_expkey_cache)) printk(KERN_ERR "nfsd: failed to unregister expkey cache\n"); if (cache_unregister(&svc_export_cache)) printk(KERN_ERR "nfsd: failed to unregister export cache\n"); svcauth_unix_purge(); exp_writeunlock(); dprintk("nfsd: export shutdown complete.\n");}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -