📄 export.c
字号:
#define MSNFS /* HACK HACK *//* * linux/fs/nfsd/export.c * * NFS exporting and validation. * * We maintain a list of clients, each of which has a list of * exports. To export an fs to a given client, you first have * to create the client entry with NFSCTL_ADDCLIENT, which * creates a client control block and adds it to the hash * table. Then, you call NFSCTL_EXPORT for each fs. * * * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de> */#include <linux/unistd.h>#include <linux/slab.h>#include <linux/stat.h>#include <linux/in.h>#include <linux/sunrpc/svc.h>#include <linux/nfsd/nfsd.h>#include <linux/nfsd/nfsfh.h>#include <linux/nfsd/syscall.h>#include <linux/lockd/bind.h>#define NFSDDBG_FACILITY NFSDDBG_EXPORT#define NFSD_PARANOIA 1typedef struct svc_client svc_client;typedef struct svc_export svc_export;static svc_export * exp_find(svc_client *clp, kdev_t dev);static svc_export * exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry);static svc_export * exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry);static void exp_unexport_all(svc_client *clp);static void exp_do_unexport(svc_export *unexp);static svc_client * exp_getclientbyname(char *name);static void exp_freeclient(svc_client *clp);static void exp_unhashclient(svc_client *clp);static int exp_verify_string(char *cp, int max);#define CLIENT_HASHBITS 6#define CLIENT_HASHMAX (1 << CLIENT_HASHBITS)#define CLIENT_HASHMASK (CLIENT_HASHMAX - 1)#define CLIENT_HASH(a) \ ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)/* XXX: is this adequate for 32bit kdev_t ? */#define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1))struct svc_clnthash { struct svc_clnthash * h_next; struct in_addr h_addr; struct svc_client * h_client;};static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX];static svc_client * clients;static int initialized;static int hash_lock;static int want_lock;static int hash_count;static DECLARE_WAIT_QUEUE_HEAD( hash_wait );/* * Find a client's export for a device. */static inline svc_export *exp_find(svc_client *clp, kdev_t dev){ svc_export * exp; exp = clp->cl_export[EXPORT_HASH(dev)]; while (exp && exp->ex_dev != dev) exp = exp->ex_next; return exp;}/* * Find the client's export entry matching xdev/xino. */svc_export *exp_get(svc_client *clp, kdev_t dev, ino_t ino){ svc_export * exp; if (!clp) return NULL; exp = clp->cl_export[EXPORT_HASH(dev)]; if (exp) do { if (exp->ex_ino == ino && exp->ex_dev == dev) goto out; } while (NULL != (exp = exp->ex_next)); exp = NULL;out: return exp;}/* * Find the export entry for a given dentry. <gam3@acm.org> */static svc_export *exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry){ svc_export *exp; if (clp == NULL) return NULL; for (exp = clp->cl_export[EXPORT_HASH(dev)]; exp; exp = exp->ex_next) if (is_subdir(dentry, exp->ex_dentry)) break; return exp;}/* * Find the child export entry for a given fs. This function is used * only by the export syscall to keep the export tree consistent. * <gam3@acm.org> */static svc_export *exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry){ svc_export *exp; if (clp == NULL) return NULL; for (exp = clp->cl_export[EXPORT_HASH(dev)]; exp; exp = exp->ex_next) { struct dentry *ndentry = exp->ex_dentry; if (ndentry && is_subdir(ndentry->d_parent, dentry)) break; } return exp;}/* * Export a file system. */intexp_export(struct nfsctl_export *nxp){ svc_client *clp; svc_export *exp, *parent; svc_export **head; struct nameidata nd; struct inode *inode = NULL; int i, err; kdev_t dev; ino_t ino; /* Consistency check */ err = -EINVAL; if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) || !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) goto out; dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", nxp->ex_client, nxp->ex_path, nxp->ex_dev, (long) nxp->ex_ino, nxp->ex_flags); dev = to_kdev_t(nxp->ex_dev); ino = nxp->ex_ino; /* Try to lock the export table for update */ if ((err = exp_writelock()) < 0) goto out; /* Look up client info */ err = -EINVAL; if (!(clp = exp_getclientbyname(nxp->ex_client))) goto out_unlock; /* * If there's already an export for this file, assume this * is just a flag update. */ if ((exp = exp_get(clp, dev, ino)) != NULL) { exp->ex_flags = nxp->ex_flags; exp->ex_anon_uid = nxp->ex_anon_uid; exp->ex_anon_gid = nxp->ex_anon_gid; err = 0; goto out_unlock; } /* Look up the dentry */ err = 0; if (path_init(nxp->ex_path, LOOKUP_POSITIVE, &nd)) err = path_walk(nxp->ex_path, &nd); if (err) goto out_unlock; inode = nd.dentry->d_inode; err = -EINVAL; if (inode->i_dev != dev || inode->i_ino != nxp->ex_ino) { printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %x\n", inode->i_dev, dev); /* I'm just being paranoid... */ goto finish; } /* We currently export only dirs and regular files. * This is what umountd does. */ err = -ENOTDIR; if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)) goto finish; err = -EINVAL; if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) || (inode->i_sb->s_op->read_inode == NULL && inode->i_sb->s_op->fh_to_dentry == NULL)) { dprintk("exp_export: export of invalid fs type.\n"); goto finish; } if ((parent = exp_child(clp, dev, nd.dentry)) != NULL) { dprintk("exp_export: export not valid (Rule 3).\n"); goto finish; } /* Is this is a sub-export, must be a proper subset of FS */ if ((parent = exp_parent(clp, dev, nd.dentry)) != NULL) { dprintk("exp_export: sub-export not valid (Rule 2).\n"); goto finish; } err = -ENOMEM; if (!(exp = kmalloc(sizeof(*exp), GFP_USER))) goto finish; dprintk("nfsd: created export entry %p for client %p\n", exp, clp); strcpy(exp->ex_path, nxp->ex_path); exp->ex_client = clp; exp->ex_parent = parent; exp->ex_dentry = nd.dentry; exp->ex_mnt = nd.mnt; exp->ex_flags = nxp->ex_flags; exp->ex_dev = dev; exp->ex_ino = ino; exp->ex_anon_uid = nxp->ex_anon_uid; exp->ex_anon_gid = nxp->ex_anon_gid; /* Update parent pointers of all exports */ if (parent) { for (i = 0; i < NFSCLNT_EXPMAX; i++) { svc_export *temp = clp->cl_export[i]; while (temp) { if (temp->ex_parent == parent) temp->ex_parent = exp; temp = temp->ex_next; } } } head = clp->cl_export + EXPORT_HASH(dev); exp->ex_next = *head; *head = exp; err = 0; /* Unlock hashtable */out_unlock: exp_unlock();out: return err; /* Release the dentry */finish: path_release(&nd); goto out_unlock;}/* * Unexport a file system. The export entry has already * been removed from the client's list of exported fs's. */static voidexp_do_unexport(svc_export *unexp){ svc_export *exp; svc_client *clp; struct dentry *dentry; struct vfsmount *mnt; struct inode *inode; int i; /* Update parent pointers. */ clp = unexp->ex_client; for (i = 0; i < NFSCLNT_EXPMAX; i++) { for (exp = clp->cl_export[i]; exp; exp = exp->ex_next) if (exp->ex_parent == unexp) exp->ex_parent = unexp->ex_parent; } dentry = unexp->ex_dentry; mnt = unexp->ex_mnt; inode = dentry->d_inode; if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino) printk(KERN_WARNING "nfsd: bad dentry in unexport!\n"); dput(dentry); mntput(mnt); kfree(unexp);}/* * Revoke all exports for a given client. * This may look very awkward, but we have to do it this way in order * to avoid race conditions (aka mind the parent pointer). */static voidexp_unexport_all(svc_client *clp){ svc_export *exp; int i; dprintk("unexporting all fs's for clnt %p\n", clp); for (i = 0; i < NFSCLNT_EXPMAX; i++) { exp = clp->cl_export[i]; clp->cl_export[i] = NULL; while (exp) { svc_export *next = exp->ex_next; exp_do_unexport(exp); exp = next; } }}/* * unexport syscall. */intexp_unexport(struct nfsctl_export *nxp){ svc_client *clp; svc_export **expp, *exp = NULL; int err; /* Consistency check */ if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX)) return -EINVAL; if ((err = exp_writelock()) < 0) goto out; err = -EINVAL; clp = exp_getclientbyname(nxp->ex_client); if (clp) { expp = clp->cl_export + EXPORT_HASH(nxp->ex_dev); while ((exp = *expp) != NULL) { if (exp->ex_dev == nxp->ex_dev) { if (exp->ex_ino == nxp->ex_ino) { *expp = exp->ex_next; exp_do_unexport(exp); err = 0; break; } } expp = &(exp->ex_next); } } exp_unlock();out: 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(struct svc_client *clp, kdev_t dev, ino_t ino, 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; if (path) { if (path_init(path, LOOKUP_POSITIVE, &nd) && path_walk(path, &nd)) { printk("nfsd: exp_rootfh path not found %s", path); return err; } dev = nd.dentry->d_inode->i_dev; ino = nd.dentry->d_inode->i_ino; dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n", path, nd.dentry, clp->cl_ident, dev, (long) ino); exp = exp_parent(clp, dev, nd.dentry); } else { dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n", clp->cl_ident, dev, (long) ino); if ((exp = exp_get(clp, dev, ino))) { nd.mnt = mntget(exp->ex_mnt); nd.dentry = dget(exp->ex_dentry); } } if (!exp) { dprintk("nfsd: exp_rootfh export not found.\n"); goto out; } inode = nd.dentry->d_inode; if (!inode) { printk("exp_rootfh: Aieee, NULL d_inode\n"); goto out; } if (inode->i_dev != dev || inode->i_ino != ino) { printk("exp_rootfh: Aieee, ino/dev mismatch\n"); printk("exp_rootfh: arg[dev(%x):ino(%ld)]" " inode[dev(%x):ino(%ld)]\n", dev, (long) ino, inode->i_dev, (long) inode->i_ino); } /* * fh must be initialized before calling fh_compose */ fh_init(&fh, maxsize); if (fh_compose(&fh, exp, dget(nd.dentry), NULL)) err = -EINVAL; else err = 0; memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh)); fh_put(&fh);out: if (path) path_release(&nd); return err;}/* * Hashtable locking. Write locks are placed only by user processes * wanting to modify export information. */voidexp_readlock(void){ while (hash_lock || want_lock) sleep_on(&hash_wait); hash_count++;}intexp_writelock(void){ /* fast track */ if (!hash_count && !hash_lock) { lock_it: hash_lock = 1; return 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -