--- //depot/vendor/freebsd/src/sys/fs/devfs/devfs_vfsops.c 2002/08/13 03:10:42 +++ //depot/user/mux/nmount/sys/fs/devfs/devfs_vfsops.c 2002/08/20 04:09:53 @@ -201,5 +201,13 @@ devfs_nmount, }; -VFS_SET(devfs_vfsops, devfs, VFCF_SYNTHETIC); +static struct vfsconf devfs_vfsconf = { + &devfs_vfsops, + "devfs", + -1, + 0, + VFCF_SYNTHETIC, + NULL +}; +VFS_SET(devfs, devfs_vfsconf); #endif --- //depot/vendor/freebsd/src/sys/fs/fdescfs/fdesc_vfsops.c 2002/08/04 03:32:14 +++ //depot/user/mux/nmount/sys/fs/fdescfs/fdesc_vfsops.c 2002/08/07 13:11:38 @@ -224,4 +224,12 @@ fdesc_mount, }; -VFS_SET(fdesc_vfsops, fdescfs, VFCF_SYNTHETIC); +static struct vfsconf fdesc_vfsconf = { + &fdesc_vfsops, + "fdescfs", + -1, + 0, + VFCF_SYNTHETIC, + NULL +}; +VFS_SET(fdescfs, fdesc_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/hpfs/hpfs_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/fs/hpfs/hpfs_vfsops.c 2002/09/17 11:21:38 @@ -587,4 +587,13 @@ hpfs_uninit, vfs_stdextattrctl, }; -VFS_SET(hpfs_vfsops, hpfs, 0); + +static struct vfsconf hpfs_vfsconf = { + &hpfs_vfsops, + "hpfs", + -1, + 0, + 0, + NULL +}; +VFS_SET(hpfs, hpfs_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/msdosfs/msdosfs_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/fs/msdosfs/msdosfs_vfsops.c 2002/09/17 11:21:38 @@ -840,4 +840,12 @@ vfs_stdextattrctl, }; -VFS_SET(msdosfs_vfsops, msdosfs, 0); +static struct vfsconf msdosfs_vfsconf = { + &msdosfs_vfsops, + "msdosfs", + -1, + 0, + 0, + NULL +}; +VFS_SET(msdosfs, msdosfs_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/ntfs/ntfs_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/fs/ntfs/ntfs_vfsops.c 2002/09/17 11:21:38 @@ -790,4 +790,13 @@ ntfs_uninit, vfs_stdextattrctl, }; -VFS_SET(ntfs_vfsops, ntfs, 0); + +static struct vfsconf ntfs_vfsconf = { + &ntfs_vfsops, + "ntfs", + -1, + 0, + 0, + NULL +}; +VFS_SET(ntfs, ntfs_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/nullfs/null_vfsops.c 2002/08/04 03:32:14 +++ //depot/user/mux/nmount/sys/fs/nullfs/null_vfsops.c 2002/09/19 11:25:05 @@ -424,4 +424,17 @@ nullfs_mount, }; -VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK); +static struct vfsoptdecl null_opts[] = { + { "target", "A", MNTOPT_NOUPDATE }, + { NULL, NULL, 0 } +}; + +static struct vfsconf null_vfsconf = { + &null_vfsops, + "nullfs", + -1, + 0, + VFCF_LOOPBACK, + null_opts +}; +VFS_SET(nullfs, null_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/nwfs/nwfs_vfsops.c 2002/08/04 03:32:14 +++ //depot/user/mux/nmount/sys/fs/nwfs/nwfs_vfsops.c 2002/08/07 13:11:38 @@ -98,8 +98,15 @@ vfs_stdextattrctl, }; - -VFS_SET(nwfs_vfsops, nwfs, VFCF_NETWORK); +static struct vfsconf nwfs_vfsconf = { + &nwfs_vfsops, + "nwfs", + -1, + 0, + VFCF_NETWORK, + NULL +}; +VFS_SET(nwfs, nwfs_vfsconf); int nwfs_pbuf_freecnt = -1; /* start out unlimited */ static int nwfsid = 1; --- //depot/vendor/freebsd/src/sys/fs/portalfs/portal_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/fs/portalfs/portal_vfsops.c 2002/09/17 11:21:38 @@ -259,4 +259,12 @@ vfs_stdextattrctl, }; -VFS_SET(portal_vfsops, portalfs, VFCF_SYNTHETIC); +static struct vfsconf portal_vfsconf = { + &portal_vfsops, + "portalfs", + -1, + 0, + VFCF_SYNTHETIC, + NULL +}; +VFS_SET(portalfs, portal_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/pseudofs/pseudofs.h 2002/07/31 18:35:22 +++ //depot/user/mux/nmount/sys/fs/pseudofs/pseudofs.h 2002/08/01 11:32:50 @@ -276,7 +276,16 @@ vfs_stdextattrctl, \ _##name##_mount, \ }; \ -VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC); \ + \ +static struct vfsconf name##_vfsconf = { \ + & name##_vfsops, \ + #name, \ + -1, \ + 0, \ + VFCF_SYNTHETIC, \ + NULL \ +}; \ +VFS_SET(name, name##_vfsconf); \ MODULE_VERSION(name, version); \ MODULE_DEPEND(name, pseudofs, 1, 1, 1); --- //depot/vendor/freebsd/src/sys/fs/smbfs/smbfs_vfsops.c 2002/08/04 03:32:14 +++ //depot/user/mux/nmount/sys/fs/smbfs/smbfs_vfsops.c 2002/08/07 13:11:38 @@ -105,8 +105,15 @@ vfs_stdextattrctl }; - -VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); +static struct vfsconf smbfs_vfsconf = { + &smbfs_vfsops, + "smbfs", + -1, + 0, + VFCF_NETWORK, + NULL +}; +VFS_SET(smbfs, smbfs_vfsconf); MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); MODULE_DEPEND(smbfs, libiconv, 1, 1, 1); --- //depot/vendor/freebsd/src/sys/fs/udf/udf_vfsops.c 2002/08/14 17:46:15 +++ //depot/user/mux/nmount/sys/fs/udf/udf_vfsops.c 2002/09/19 11:25:05 @@ -129,7 +129,21 @@ vfs_stdextattrctl, udf_mount, }; -VFS_SET(udf_vfsops, udf, VFCF_READONLY); + +static struct vfsoptdecl udf_opts[] = { + { "from", "A", MNTOPT_NOUPDATE }, + { NULL, NULL, 0 } +}; + +static struct vfsconf udf_vfsconf = { + &udf_vfsops, + "udf", + -1, + 0, + VFCF_READONLY, + udf_opts +}; +VFS_SET(udf, udf_vfsconf); static int udf_mountfs(struct vnode *, struct mount *, struct thread *); --- //depot/vendor/freebsd/src/sys/fs/umapfs/umap_vfsops.c 2002/08/04 03:32:14 +++ //depot/user/mux/nmount/sys/fs/umapfs/umap_vfsops.c 2002/08/07 13:11:38 @@ -459,4 +459,12 @@ umapfs_extattrctl, }; -VFS_SET(umap_vfsops, umapfs, VFCF_LOOPBACK); +static struct vfsconf umap_vfsconf = { + &umap_vfsops, + "umapfs", + -1, + 0, + VFCF_LOOPBACK, + NULL +}; +VFS_SET(umapfs, umap_vfsconf); --- //depot/vendor/freebsd/src/sys/fs/unionfs/union_vfsops.c 2002/06/15 15:50:18 +++ //depot/user/mux/nmount/sys/fs/unionfs/union_vfsops.c 2002/09/19 11:25:05 @@ -500,4 +500,19 @@ union_mount, }; -VFS_SET(union_vfsops, unionfs, VFCF_LOOPBACK); +static struct vfsoptdecl union_opts[] = { + { "target", "A", MNTOPT_NOUPDATE }, + { "below", NULL, MNTOPT_NOUPDATE }, + { "replace", NULL, MNTOPT_NOUPDATE }, + { NULL, NULL, 0 } +}; + +static struct vfsconf union_vfsconf = { + &union_vfsops, + "unionfs", + -1, + 0, + VFCF_LOOPBACK, + union_opts +}; +VFS_SET(unionfs, union_vfsconf); --- //depot/vendor/freebsd/src/sys/gnu/ext2fs/ext2_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/gnu/ext2fs/ext2_vfsops.c 2002/09/20 02:11:08 @@ -100,7 +100,20 @@ ext2_mount, }; -VFS_SET(ext2fs_vfsops, ext2fs, 0); +static struct vfsoptdecl ext2fs_opts[] = { + { "from", "A", MNTOPT_NOUPDATE }, + { NULL, NULL, 0 } +}; + +static struct vfsconf ext2fs_vfsconf = { + &ext2fs_vfsops, + "ext2fs", + -1, + 0, + 0, + ext2fs_opts +}; +VFS_SET(ext2fs, ext2fs_vfsconf); #define bsd_malloc malloc #define bsd_free free --- //depot/vendor/freebsd/src/sys/isofs/cd9660/cd9660_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/isofs/cd9660/cd9660_vfsops.c 2002/09/17 11:21:38 @@ -88,7 +88,16 @@ cd9660_uninit, vfs_stdextattrctl, }; -VFS_SET(cd9660_vfsops, cd9660, VFCF_READONLY); + +static struct vfsconf cd9660_vfsconf = { + &cd9660_vfsops, + "cd9660", + -1, + 0, + VFCF_READONLY, + NULL +}; +VFS_SET(cd9660, cd9660_vfsconf); MODULE_VERSION(cd9660, 1); --- //depot/vendor/freebsd/src/sys/kern/vfs_mount.c 2002/09/19 11:55:17 +++ //depot/user/mux/nmount/sys/kern/vfs_mount.c 2002/09/20 02:22:15 @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include @@ -115,6 +116,25 @@ /* For any iteration/modification of mnt_vnodelist */ struct mtx mntvnode_mtx; +/* Dynamic tree to export mount options */ +SYSCTL_NODE(_vfs, OID_AUTO, mounts, CTLFLAG_RD, NULL, ""); + +TAILQ_HEAD(vfsoptlist, vfsopt); +struct vfsopt { + TAILQ_ENTRY(vfsopt) link; + char *name; + void *value; + int len; + char *fmt; +}; + +static struct vfsoptdecl vfs_genericopts[] = { + { "fstype", "A", MNTOPT_NOUPDATE }, + { "fspath", "A", MNTOPT_NOUPDATE }, + { "export", "S,export_args", 0 }, + { NULL, NULL, 0 }, +}; + /* * The vnode of the system's root (/ in the filesystem, without chroot * active.) @@ -303,6 +323,121 @@ } /* + * Find the struct vfsoptdecl corresponding to a mount option. + * + * If this function isn't called for every mount option we're + * likely to have problems later in vfs_exportopts() since it + * fills in the fmt field, ie the type of the option. + */ +static struct vfsoptdecl * +vfs_findoptdecl(struct vfsopt *opt, struct vfsconf *vfsp) +{ + struct vfsoptdecl *optd; + char *name; + + if (strncmp(opt->name, "no", 2) == 0) + name = opt->name + 2; + else + name = opt->name; + /* First look if the option is fs-specific. */ + optd = vfsp->vfc_opts; + if (optd != NULL) { + while (optd->name != NULL) { + if (strcmp(name, optd->name) == 0) + goto found; + optd++; + } + } + /* Now look if the option is a generic mount option. */ + optd = vfs_genericopts; + while (optd->name != NULL) { + if (strcmp(name, optd->name) == 0) + goto found; + optd++; + } + return (NULL); +found: + opt->fmt = optd->fmt; + return (optd); +} + +/* + * Check that the given options are supported and that + * we can update an option if we've been asked to. + * Returns EINVAL if we find an unknown mount option, + * otherwise returns 0. + */ +static int +vfs_checksupportedopts(struct vfsoptlist *opts, struct vfsconf *vfsp, int flags) +{ + struct vfsopt *opt; + struct vfsoptdecl *optd; + + TAILQ_FOREACH(opt, opts, link) { + optd = vfs_findoptdecl(opt, vfsp); + if (optd == NULL) { + printf("%s: option \"%s\" is unsupported\n", + vfsp->vfc_name, opt->name); + return (EINVAL); + } + if ((flags & MNT_UPDATE) && (optd->flags & MNTOPT_NOUPDATE)) { + printf("%s: option \"%s\" can't be updated\n", + vfsp->vfc_name, opt->name); + return (EINVAL); + } + } + return (0); +} + +/* + * Build the sysctl tree to export the options of this + * mount point. It will only work if vfs_findoptdecl() + * has been called for every mount option. + */ +static int +vfs_exportopts(struct mount *mp) +{ + char name[17]; + struct sysctl_oid *parent; + struct vfsopt *opt; + int (*handler)(SYSCTL_HANDLER_ARGS); + int64_t fsid; + + fsid = mp->mnt_stat.f_fsid.val[1]; + fsid <<= 32; + fsid += mp->mnt_stat.f_fsid.val[0]; + snprintf(name, sizeof(name) - 1, "%jx", (intmax_t)fsid); + name[sizeof(name) - 1] = '\0'; + mp->mnt_ctx = malloc(sizeof(mp->mnt_ctx), M_MOUNT, M_WAITOK); + sysctl_ctx_init(mp->mnt_ctx); + parent = SYSCTL_ADD_NODE(mp->mnt_ctx, + SYSCTL_STATIC_CHILDREN(_vfs_mounts), OID_AUTO, name, CTLFLAG_RD, + NULL, ""); + TAILQ_FOREACH(opt, mp->mnt_opt, link) { + /* + * We need to do that if we want the sysctls + * to be visible with sysctl(8). + */ + if (opt->fmt == NULL) + handler = NULL; + else if (strcmp(opt->fmt, "A") == 0) + handler = sysctl_handle_string; + else if (strcmp(opt->fmt, "I") == 0 || + strcmp(opt->fmt, "IU") == 0) + handler = sysctl_handle_int; + else if (strcmp(opt->fmt, "L") == 0 || + strcmp(opt->fmt, "LU") == 0) + handler = sysctl_handle_long; + else + handler = sysctl_handle_opaque; + sysctl_add_oid(mp->mnt_ctx, SYSCTL_CHILDREN(parent), + OID_AUTO, opt->name, CTLFLAG_RD, opt->value, opt->len, + handler, opt->fmt, ""); + } + return (0); +} + +/* * New mount API. */ int @@ -513,6 +648,13 @@ mp = vp->v_mount; flag = mp->mnt_flag; kern_flag = mp->mnt_kern_flag; + error = vfs_checksupportedopts(optlist, mp->mnt_vfc, fsflags); + if (error) { + vput(vp); + goto bad; + } + mp->mnt_optnew = optlist; + vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt); /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. @@ -553,8 +695,6 @@ mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT); VOP_UNLOCK(vp, 0, td); - mp->mnt_optnew = optlist; - vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt); goto update; } /* @@ -617,6 +757,11 @@ goto bad; } } + error = vfs_checksupportedopts(optlist, vfsp, fsflags); + if (error) { + vput(vp); + goto bad; + } VI_LOCK(vp); if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) { @@ -695,16 +840,6 @@ * get. No freeing of cn_pnbuf. */ error = VFS_NMOUNT(mp, &nd, td); - if (!error) { - if (mp->mnt_opt != NULL) - vfs_freeopts(mp->mnt_opt); - mp->mnt_opt = mp->mnt_optnew; - } - /* - * Prevent external consumers of mount - * options to read mnt_optnew. - */ - mp->mnt_optnew = NULL; if (mp->mnt_flag & MNT_UPDATE) { if (mp->mnt_kern_flag & MNTK_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; @@ -723,6 +858,15 @@ vrele(mp->mnt_syncer); mp->mnt_syncer = NULL; } + if (!error) { + sysctl_ctx_free(mp->mnt_ctx); + vfs_freeopts(mp->mnt_opt); + mp->mnt_opt = mp->mnt_optnew; + vfs_exportopts(mp); + mp->mnt_optnew = NULL; + } else { + vfs_freeopts(mp->mnt_optnew); + } vfs_unbusy(mp, td); VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; @@ -752,11 +896,12 @@ VOP_UNLOCK(vp, 0, td); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); + mp->mnt_opt = mp->mnt_optnew; + mp->mnt_optnew = NULL; + vfs_exportopts(mp); vfs_unbusy(mp, td); - if ((error = VFS_START(mp, 0, td)) != 0) { + if ((error = VFS_START(mp, 0, td)) != 0) vrele(vp); - goto bad; - } } else { VI_LOCK(vp); vp->v_iflag &= ~VI_MOUNT; @@ -770,7 +915,7 @@ vput(vp); goto bad; } - return (0); + return (error); bad: vfs_freeopts(optlist); return (error); @@ -1335,6 +1480,10 @@ #ifdef MAC mac_destroy_mount(mp); #endif + if (mp->mnt_ctx != NULL) { + sysctl_ctx_free(mp->mnt_ctx); + free(mp->mnt_ctx, M_MOUNT); + } if (mp->mnt_op->vfs_mount == NULL) vfs_freeopts(mp->mnt_opt); free(mp, M_MOUNT); @@ -1749,3 +1898,26 @@ } return (ENOENT); } + +/* + * Add a mount option into the options list. + * + * Intended to be used by filesystems which want + * to add a default mount option at VFS_MOUNT time. + */ +void +vfs_addopt(struct mount *mp, const char *name, void *value, int size) +{ + struct vfsoptdecl *optd; + struct vfsopt *opt; + + opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK); + opt->name = malloc(strlen(name) + 1, M_MOUNT, M_WAITOK); + strcpy(opt->name, name); + opt->value = malloc(size, M_MOUNT, M_WAITOK); + bcopy(value, opt->value, size); + optd = vfs_findoptdecl(opt, mp->mnt_vfc); + KASSERT(optd != NULL, ("%s: trying to add an unknown mount option", + __func__)); + TAILQ_INSERT_TAIL(mp->mnt_optnew, opt, link); +} --- //depot/vendor/freebsd/src/sys/nfsclient/nfs_vfsops.c 2002/09/08 08:12:20 +++ //depot/user/mux/nmount/sys/nfsclient/nfs_vfsops.c 2002/09/13 15:03:42 @@ -119,7 +119,16 @@ nfs_uninit, vfs_stdextattrctl, }; -VFS_SET(nfs_vfsops, nfs, VFCF_NETWORK); + +static struct vfsconf nfs_vfsconf = { + &nfs_vfsops, + "nfs", + -1, + 0, + VFCF_NETWORK, + NULL +}; +VFS_SET(nfs, nfs_vfsconf); /* So that loader and kldload(2) can find us, wherever we are.. */ MODULE_VERSION(nfs, 1); --- //depot/vendor/freebsd/src/sys/sys/mount.h 2002/08/18 23:55:23 +++ //depot/user/mux/nmount/sys/sys/mount.h 2002/09/20 02:01:12 @@ -54,6 +54,7 @@ struct iovec; struct netcred; struct netexport; +struct sysctl_ctx_list; typedef struct fsid { int32_t val[2]; } fsid_t; /* filesystem id type */ @@ -122,12 +123,15 @@ TAILQ_HEAD(vnodelst, vnode); #define MMAXOPTIONLEN 65536 /* maximum length of a mount option */ -TAILQ_HEAD(vfsoptlist, vfsopt); -struct vfsopt { - TAILQ_ENTRY(vfsopt) link; + +/* Flags for mount options. */ +#define MNTOPT_NOUPDATE 1 /* can't be changed in MNT_UPDATE */ + +/* Structure for the declaration of mount options. */ +struct vfsoptdecl { char *name; - void *value; - int len; + char *fmt; + int flags; }; struct mount { @@ -143,6 +147,7 @@ int mnt_flag; /* flags shared with user */ struct vfsoptlist *mnt_opt; /* current mount options */ struct vfsoptlist *mnt_optnew; /* new options passed to fs */ + struct sysctl_ctx_list *mnt_ctx; /* sysctl context for options */ int mnt_kern_flag; /* kernel only flags */ int mnt_maxsymlinklen; /* max size of short symlink */ struct statfs mnt_stat; /* cache of filesystem stats */ @@ -442,18 +447,11 @@ #include -#define VFS_SET(vfsops, fsname, flags) \ - static struct vfsconf fsname ## _vfsconf = { \ - &vfsops, \ - #fsname, \ - -1, \ - 0, \ - flags \ - }; \ +#define VFS_SET(fsname, vfsconf) \ static moduledata_t fsname ## _mod = { \ #fsname, \ vfs_modevent, \ - & fsname ## _vfsconf \ + &vfsconf \ }; \ DECLARE_MODULE(fsname, fsname ## _mod, SI_SUB_VFS, SI_ORDER_MIDDLE) @@ -467,6 +465,7 @@ int kernel_vmount(int flags, ...); int vfs_getopt(struct vfsoptlist *, const char *, void **, int *); int vfs_copyopt(struct vfsoptlist *, const char *, void *, int); +void vfs_addopt(struct mount *, const char *, void *, int); int vfs_mount(struct thread *td, const char *type, char *path, int flags, void *data); int vfs_setpublicfs /* set publicly exported fs */ --- //depot/vendor/freebsd/src/sys/ufs/ffs/ffs_vfsops.c 2002/09/14 02:05:35 +++ //depot/user/mux/nmount/sys/ufs/ffs/ffs_vfsops.c 2002/09/17 11:21:38 @@ -93,7 +93,15 @@ ffs_extattrctl, }; -VFS_SET(ufs_vfsops, ufs, 0); +static struct vfsconf ufs_vfsconf = { + &ufs_vfsops, + "ufs", + -1, + 0, + 0, + NULL +}; +VFS_SET(ufs, ufs_vfsconf); /* * ffs_mount