檔案過大!部分文章無法顯示
+
+ /* Check for duplicated default export specification. */
+ if (fe->ae_def != NULL) {
+ logconfx("default export specification already was "
+ "specified for %s", fe->path);
+ goto failed;
+ }
+ ae = Malloc(sizeof(*ae));
+ if (ae == NULL) {
+ logconfe("parse_line: malloc");
+ goto failed;
+ }
+ ae->addr_spec = NULL;
+ setopts_addr_exp(ae, 0);
+ fe->ae_def = ae;
+ fe->nspec++;
+ } else if (!saw_addr_spec) {
+ if (((oflags & AE_OPTS_ALLFLAGS) ||
+ (oflags & (OPT_NO_MNT_DMP|OPT_NO_MNT_EXP))) &&
+ !(oflags & OPT_NOSPEC))
+ logconfw("some options specified at the end of this "
+ "line do not have effect on configuration");
+ }
+
+ fe->oflags |= oflagsall &
+ (FE_OPTS_ALLFLAGS & ~(OPT_NO_MNT_DMP|OPT_NO_MNT_EXP));
+ if (oflagsall & OPT_NOSPEC)
+ fe->oflags |= oflagsall & (OPT_NO_MNT_DMP|OPT_NO_MNT_EXP);
+ return (0);
+
+failed:
+ free(addr_spec);
+ if (syserr_flag || exp_cmd_num > 0)
+ return (-1);
+ if (fs_exp != NULL)
+ fs_exp->oflags |= OPT_ERRCONFIG;
+ return (0);
+}
+
+/*
+ * Forget about configuration for the given address family.
+ */
+static void
+forget_conf(const u_int family)
+{
+ const struct fs_exp_list *fe_list;
+ struct addr_exp_list *ae_list;
+ struct fs_exp *fe;
+
+ fe_list = fs_exp_list;
+ TAILQ_FOREACH(fe, fe_list, link) {
+ ae_list = family == AF_INET ? &fe->ae4_list : &fe->ae6_list;
+ free_addr_exp_list(ae_list);
+ TAILQ_INIT(ae_list);
+ }
+}
+
+/*
+ * Read file's content line-by-line and parse each line.
+ */
+static int
+parse_file(FILE *fp, const char *name)
+{
+ int rv;
+
+ if (test_conf)
+ logmsgx("configure: reading file %s", name);
+ lineno = 0;
+ exp_file_name = name;
+ file_is_parsed = 1;
+ for (;;) {
+ rv = read_line(fp);
+ if (rv == 1) {
+ if (parse_line() < 0)
+ return (-1);
+ } else
+ break;
+ }
+ return (rv);
+}
+
+/*
+ * This function parse single commands that cannot be used with another
+ * commands.
+ */
+static int
+parse_single_cmd(const char *str)
+{
+ u_int cmd;
+
+ if (strcmp(str, "reload") == 0)
+ cmd = CTL_CMD_RELOAD;
+ else if (strcmp(str, "flush") == 0)
+ cmd = CTL_CMD_FLUSH;
+ else
+ return (0);
+ if (exp_cmd_num != 1) {
+ exp_file_name = "-c";
+ log_warn("command \"%s\" cannot be used with other commands",
+ str);
+ return (-1);
+ }
+ ctl_cmd_hdr.command = cmd;
+ return (1);
+}
+
+/*
+ * Parse -c options commands.
+ * Return:
+ * 0 -- all commands were parsed successfully;
+ * -1 -- some error occurred (syserr_flag can be set);
+ * -2 -- some system error occurred (the caller must set syserr_flag).
+ */
+static int
+parse_exp_cmds(void)
+{
+ struct stat statbuf;
+ const char *fname;
+ FILE *fp;
+ char *ptr, *linebuf_bak;
+ u_int i;
+ int rv;
+
+ if (test_conf)
+ printf("configure: parsing -c commands\n");
+
+ ctl_cmd_hdr.version = CTL_API_VERSION;
+
+ /* Check single command. */
+ switch (parse_single_cmd(skip_spaces(exp_cmd_arr[0]))) {
+ case 0:
+ break;
+ case 1:
+ return (0);
+ default:
+ return (-1);
+ }
+
+ /* Parse other commands. */
+ for (i = 0; i < exp_cmd_num; ++i) {
+ ptr = skip_spaces(exp_cmd_arr[i]);
+ ocmd = 0;
+ ocmd_set = 0;
+ if (strncmp(ptr, "file", sizeof("file") - 1) == 0)
+ fname = skip_spaces(ptr + sizeof("file") - 1);
+ else {
+ exp_file_name = "-c";
+ lineno = i + 1;
+ linebuf_bak = linebuf;
+ linebuf = ptr;
+ file_is_parsed = 0;
+ rv = parse_line();
+ linebuf = linebuf_bak;
+ if (rv < 0)
+ return (-1);
+ if (!ocmd_set)
+ continue;
+ /* Apply command to export specifications from file. */
+ fname = exp_file_name;
+ }
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ (void)crit_fs_err(errno);
+ logmsg("configure: fopen(%s, \"r\")", fname);
+ return (-1);
+ }
+ if (Fstat(fileno(fp), &statbuf) < 0) {
+ logmsg("configure: stat(%s)", fname);
+ goto failed;
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ logmsgx("configure: file %s is not a regular "
+ "file", fname);
+ goto failed;
+ }
+ if (parse_file(fp, fname) < 0)
+ goto failed;
+ if (Fclose(fp) != 0) {
+ logmsg("configure: fclose(%s)", fname);
+ return (-1);
+ }
+ }
+ ctl_cmd_hdr.command = CTL_CMD_EXPORT;
+ return (0);
+
+failed:
+ if (Fclose(fp) != 0)
+ logmsg("configure: fclose(%s)", fname);
+ return (-1);
+}
+
+/*
+ * Parse all configuration files.
+ * Return:
+ * 0 -- all files were parsed successfully;
+ * -1 -- some error occurred (syserr_flag can be set);
+ * -2 -- some system error occurred (the caller must set syserr_flag).
+ */
+static int
+parse_exp_files(void)
+{
+ struct stat statbuf;
+ const struct dirent *dp;
+ const char *fname, *dname;
+ FILE *fp;
+ DIR *dirp;
+ char *name;
+ int i, rv;
+
+ dirp = NULL;
+ for (i = 0; i < exp_file_num;) {
+ if (dirp == NULL) {
+ /* Not reading a directory. */
+ if (exp_file_arr[i].isfile) {
+ /* A new file. */
+ fname = exp_file_arr[i].name;
+ } else {
+ /* A new directory. */
+ dname = exp_file_arr[i].name;
+ dirp = opendir(dname);
+ if (dirp == NULL) {
+ if (crit_fs_err(errno)) {
+ logmsg("configure: "
+ "opendir(%s)", dname);
+ return (-1);
+ }
+ logmsgw("configure: skipping "
+ "directory %s: %s", dname,
+ strerror(errno));
+ ++i;
+ continue;
+ }
+ }
+ }
+ if (dirp != NULL) {
+ /* Reading a directory. */
+ errno = 0;
+ dp = readdir(dirp);
+ if (dp == NULL) {
+ if (errno != 0) {
+ logmsg("configure: readdir(%s)",
+ dname);
+ return (-2);
+ }
+ if (closedir(dirp) < 0) {
+ logmsg("configure: closedir(%s)",
+ dname);
+ return (-2);
+ }
+ dirp = NULL;
+ ++i;
+ continue;
+ }
+ /* Ignore "." and ".." directories. */
+ if (dp->d_name[0] == '.')
+ switch (dp->d_name[1]) {
+ case '\0':
+ continue;
+ case '.':
+ if (dp->d_name[2] == '\0')
+ continue;
+ break;
+ }
+ if (Asprintf(&name, "%s%s", dname, dp->d_name) < 0) {
+ logmsg("configure: asprintf");
+ return (-1);
+ }
+ fname = name;
+ }
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ rv = crit_fs_err(errno);
+ if (dirp != NULL && !rv) {
+ logmsgw("configure: skipping file %s: %s",
+ fname, strerror(errno));
+ free(name);
+ continue;
+ }
+ logmsg("configure: fopen(%s, \"r\")", fname);
+ return (-1);
+ }
+ if (Fstat(fileno(fp), &statbuf) < 0) {
+ logmsg("configure: stat(%s)", fname);
+ goto failed;
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ if (dirp != NULL)
+ goto next;
+ logmsgx("configure: %s is not a regular file", fname);
+ goto failed;
+ }
+ if (parse_file(fp, fname) < 0)
+ goto failed;
+next: if (Fclose(fp) != 0) {
+ logmsg("configure: fclose(%s)", fname);
+ return (-1);
+ }
+ if (dirp != NULL)
+ free(name);
+ else
+ ++i;
+ }
+ return (0);
+
+failed:
+ if (Fclose(fp) != 0)
+ logmsg("configure: fclose(%s)", fname);
+ return (-1);
+}
+
+/*
+ * Parse configuration.
+ */
+int
+configure(const int reconf)
+{
+ int rv;
+
+ /* Allocate initial line buffer. */
+ linebuf = Malloc(LINEBUF_SIZE);
+ if (linebuf == NULL) {
+ logmsg("configure: malloc(%u)", LINEBUF_SIZE);
+ return (-1);
+ }
+ linesize = LINEBUF_SIZE;
+
+ if (!reconf) {
+ /* First call. */
+ LIST_INSERT_HEAD(&cred_exp_list, &cred_exp_anon_def, link);
+ LIST_INSERT_HEAD(&secflavors_list, &secflavors_def, link);
+ }
+
+ /* Save old configuration of fs_exp_list. */
+ fs_exp_list_prev = fs_exp_list;
+ fs_exp_list = fs_exp_list == &fs_exp_list1 ?
+ &fs_exp_list2 : &fs_exp_list1;
+ TAILQ_INIT(fs_exp_list);
+
+ opt_public = 0;
+ goflags = 0;
+ free(index_webnfs);
+ index_webnfs = NULL;
+ setnetent(1);
+
+ log_err = logconfe;
+ log_warn = logconfw;
+ if (exp_cmd_num == 0) {
+ if (test_conf)
+ log_to_console = 1;
+ rv = parse_exp_files();
+ } else {
+ log_to_console = 1;
+ rv = parse_exp_cmds();
+ }
+
+ endnetent();
+ free(linebuf);
+
+ if (rv < 0) {
+ if (rv == -2)
+ syserr_flag = 1;
+ logmsgx("parsing failed: %s", syserr_flag ?
+ "some system error occurred" : "wrong configuration");
+ free_fs_exp_list(fs_exp_list);
+ return (-1);
+ }
+
+ if (exp_cmd_num == 0) {
+ no_mntproc_dump = (goflags & OPT_NO_MNT_DMP) ? 1 : 0;
+ no_mntproc_export = (goflags & OPT_NO_MNT_EXP) ? 1 : 0;
+ if (!test_conf) {
+ if (!have_ipv4)
+ forget_conf(AF_INET);
+ if (!have_ipv6)
+ forget_conf(AF_INET6);
+ free_wrong_conf();
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Release memory used by the current fs_exp_list and check
+ * that structures with reference counters were freed.
+ */
+void
+unconfigure(void)
+{
+#ifdef DEBUG_MEMORY_LEAK
+ free_fs_exp_list(fs_exp_list);
+ free_unref_conf();
+ if (!LIST_EMPTY(&addr4_spec_list))
+ syslog(LOG_ERR, "addr4_spec_list is not empty");
+ if (!LIST_EMPTY(&addr6_spec_list))
+ syslog(LOG_ERR, "addr6_spec_list is not empty");
+ if (LIST_NEXT(&secflavors_def, link) != NULL)
+ syslog(LOG_ERR, "secflavors_list is not empty");
+ if (LIST_NEXT(&cred_exp_anon_def, link) != NULL)
+ syslog(LOG_ERR, "cred_exp_list is not empty");
+#endif
+}
+
+/*
+ * Output a string, representing non-printable characters,
+ * white spaces and backslashes as octal numbers.
+ */
+static void
+print_string(const char *str)
+{
+ for (; *str != '\0'; ++str)
+ if (!isprint((u_char)*str) || *str == ' ' || *str == '\\')
+ printf("\\%03o", (u_char)*str);
+ else
+ printf("%c", *str);
+}
+
+/*
+ * Output security flavors.
+ */
+static void
+print_secflavors(const struct secflavors *sf)
+{
+ const char *str;
+ u_int i, n;
+
+ printf(" -sec ");
+ n = sf->nsec;
+ for (i = 0; i < n; ++i) {
+ switch (sf->sec[i]) {
+ case AUTH_SYS:
+ str = "sys";
+ break;
+ case RPCSEC_GSS_KRB5:
+ str = "krb5";
+ break;
+ case RPCSEC_GSS_KRB5I:
+ str = "krb5i";
+ break;
+ default: /* RPCSEC_GSS_KRB5P */
+ str = "krb5p";
+ }
+ printf("%s", str);
+ if (i < n - 1)
+ printf(":");
+ }
+}
+
+/*
+ * Output credentials specified in ce.
+ * Special case is -2:-2 credentials.
+ */
+static void
+print_cred(const uint32_t flags, const struct cred_exp *ce)
+{
+ if (flags & OPT_MAPALL)
+ printf(" -mapall");
+ if (flags & OPT_MAPROOT)
+ printf(" -maproot");
+ if (ce == &cred_exp_anon_def)
+ printf("=-2:-2");
+ else {
+ printf(" %lu", (u_long)ce->uid);
+ if (ce->ngids > 0) {
+ u_int i, ngids;
+
+ ngids = ce->ngids;
+ for (i = 0; i < ngids; ++i)
+ printf(":%lu", (u_long)ce->gids[i]);
+ } else
+ printf(":");
+ }
+}
+
+/*
+ * Output everything related to the given addr_exp.
+ */
+static void
+print_addr_exp(const u_int family, const struct addr_exp *ae)
+{
+ int show_opts;
+ uint32_t flags;
+
+ printf(" ");
+ flags = ae->oflags;
+ show_opts = 1;
+ if (exp_cmd_num > 0) {
+ const char *str;
+
+ switch (_CMD_GET(flags)) {
+ case CMD_ADD:
+ str = " add";
+ break;
+ case CMD_UPDATE:
+ str = "update";
+ break;
+ default: /* CMD_DELETE */
+ str = "delete";
+ show_opts = 0;
+ }
+ printf("-c %s ", str);
+ }
+ if (show_opts) {
+ printf("-r%c", (flags & OPT_RO) ? 'o' : 'w');
+ print_secflavors(ae->secflavors);
+ print_cred(flags, ae->cred_exp);
+ if (flags & OPT_NO_MNT_DMP)
+ printf(" -no_mntproc_dump");
+ if (flags & OPT_NO_MNT_EXP)
+ printf(" -no_mntproc_export");
+ printf(" ");
+ }
+ if (family != AF_UNSPEC)
+ printf("-%s %s%s",
+ (flags & OPT_NETWORK) ? "network" : "host",
+ (flags & OPT_DENY) ? "!" : "",
+ addr_spec_str(family, ae->addr_spec));
+}
+
+/*
+ * Output all addr_exp structures from the given list.
+ */
+static void
+print_addr_exp_list(const u_int family, const struct addr_exp_list *ae_list)
+{
+ const struct addr_exp *ae;
+
+ TAILQ_FOREACH(ae, ae_list, link) {
+ print_addr_exp(family, ae);
+ printf("\n");
+ }
+}
+
+/*
+ * Output all configuration in readable form.
+ */
+void
+show_conf(void)
+{
+ const struct fs_exp_list *fe_list;
+ struct statfs statfsbuf;
+ const struct fs_exp *fe;
+ uint32_t flags;
+
+ flags = goflags;
+ if ((flags & ~OPT_SEC) != 0) {
+ printf("Global options:\n");
+ if (flags & OPT_NO_MNT_DMP)
+ printf(" -no_mntproc_dump\n");
+ if (flags & OPT_NO_MNT_EXP)
+ printf(" -no_mntproc_export\n");
+ }
+
+ fe_list = fs_exp_list;
+ TAILQ_FOREACH(fe, fe_list, link) {
+ printf("\nDirectory ");
+ print_string(fe->path);
+ if (statfs(fe->path, &statfsbuf) == 0)
+ if (strcmp(statfsbuf.f_mntonname, fe->path) == 0)
+ printf(" (mount point)");
+ printf("\n");
+ flags = fe->oflags;
+ if (flags & OPT_ERRCONFIG) {
+ printf(" Wrong configuration\n");
+ continue;
+ }
+ if (flags & FE_OPTS_ALLFLAGS) {
+ printf(" File system options:\n");
+ if (flags & OPT_QUIET)
+ printf(" -quiet\n");
+ if (flags & OPT_NO_MNT_DMP)
+ printf(" -no_mntproc_dump\n");
+ if (flags & OPT_NO_MNT_EXP)
+ printf(" -no_mntproc_export\n");
+ if (flags & OPT_PUBLIC) {
+ printf(" -%s", (flags & OPT_WEBNFS) ?
+ "webnfs" : "public");
+ if (flags & OPT_INDEX) {
+ printf(" -index ");
+ print_string(index_webnfs);
+ }
+ printf("\n");
+ }
+ }
+ if (exp_cmd_num == 0)
+ printf(" Export specifications:\n");
+ else
+ printf(" Commands:\n");
+ if (exp_cmd_num > 0)
+ if (_CMD_OPT(flags) == CMD_OPT_FLUSH)
+ printf(" -c flush\n");
+ print_addr_exp_list(AF_INET, &fe->ae4_list);
+ print_addr_exp_list(AF_INET6, &fe->ae6_list);
+ if (fe->ae_def != NULL) {
+ print_addr_exp(AF_UNSPEC, fe->ae_def);
+ printf("(default)\n");
+ }
+ }
+}
+
+/*
+ * Convert one addr_exp structure to ctl_addr_exp structure and
+ * put it to the buffer.
+ */
+static void
+ctl_addr_exp_pack(char **ptr0, const u_int family, const struct addr_exp *ae)
+{
+ struct ctl_addr_exp cae;
+ u_int i;
+
+ cae.family = family;
+ if (ae->addr_spec != NULL) {
+ if (family == AF_INET) {
+ cae.addr_spec.as4.addr =
+ ADDR4_SPEC_PTR(ae->addr_spec)->addr;
+ cae.addr_spec.as4.maskbits= ae->addr_spec->maskbits;
+ } else {
+ cae.addr_spec.as6.addr =
+ ADDR6_SPEC_PTR(ae->addr_spec)->addr;
+ cae.addr_spec.as6.maskbits = ae->addr_spec->maskbits;
+ }
+ }
+ cae.uid = ae->cred_exp->uid;
+ cae.ngids = ae->cred_exp->ngids;
+ for (i = 0; i < cae.ngids; ++i)
+ cae.gids[i] = ae->cred_exp->gids[i];
+ cae.nsec = ae->secflavors->nsec;
+ for (i = 0; i < cae.nsec; ++i)
+ cae.sec[i] = ae->secflavors->sec[i];
+ cae.oflags = ae->oflags;
+ memcpy(*ptr0, &cae, sizeof(cae));
+ *ptr0 += sizeof(cae);
+}
+
+/*
+ * Convert all addr_exp structures from the given list to ctl_addr_exp
+ * structures and put them to the buffer.
+ */
+static void
+ctl_addr_exp_list_pack(char **ptr0, const u_int family,
+ const struct addr_exp_list *ae_list)
+{
+ const struct addr_exp *ae;
+
+ TAILQ_FOREACH(ae, ae_list, link)
+ ctl_addr_exp_pack(ptr0, family, ae);
+}
+
+/*
+ * Pack CTL_CMD_EXPORT command data, fs_exp_list contains all commands.
+ */
+int
+ctl_export_pack(void)
+{
+ struct ctl_fs_exp cfe;
+ const struct fs_exp_list *fe_list;
+ const struct fs_exp *fe;
+ char *ptr;
+ size_t size;
+ u_int nspec;
+
+ size = 0;
+ nspec = 0;
+ fe_list = fs_exp_list;
+ TAILQ_FOREACH(fe, fe_list, link) {
+ size += sizeof(cfe) + fe->path_len;
+ nspec += fe->nspec;
+ }
+ ctl_cmd_hdr.size = size += nspec * sizeof(struct ctl_addr_exp);
+ ctl_cmd_buf = ptr = malloc(size);
+ if (ptr == NULL) {
+ warn("ctl_export_pack: malloc");
+ return (-1);
+ }
+ TAILQ_FOREACH(fe, fe_list, link) {
+ cfe.path_len = fe->path_len;
+ cfe.oflags = fe->oflags;
+ cfe.nspec = fe->nspec;
+ memcpy(ptr, &cfe, sizeof(cfe));
+ ptr += sizeof(cfe);
+ memcpy(ptr, fe->path, fe->path_len);
+ ptr += fe->path_len;
+ if (fe->ae_def != NULL)
+ ctl_addr_exp_pack(&ptr, AF_UNSPEC, fe->ae_def);
+ ctl_addr_exp_list_pack(&ptr, AF_INET, &fe->ae4_list);
+ ctl_addr_exp_list_pack(&ptr, AF_INET6, &fe->ae6_list);
+ }
+ if ((char *)ctl_cmd_buf + size != ptr) {
+ warnx("ctl_export_pack: size mismatch: %p vs %p",
+ (char *)ctl_cmd_buf + size, ptr);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Convert one ctl_addr_exp structure to addr_exp structure and
+ * add it to fs_exp structure.
+ */
+static int
+ctl_addr_exp_unpack(const char *ptr, struct fs_exp *fe)
+{
+ struct ctl_addr_exp cae;
+ struct addr_spec *as;
+ struct addr_exp *ae;
+ uint32_t cmd, flags;
+
+ memcpy(&cae, ptr, sizeof(cae));
+
+ flags = cae.oflags;
+ if (flags & (OPT_HOST|OPT_NETWORK)) {
+ /* -host or -network, should have correct family. */
+ if ((flags & (OPT_HOST|OPT_NETWORK)) == (OPT_HOST|OPT_NETWORK))
+ return (-2);
+ if (cae.family != AF_INET && cae.family != AF_INET6)
+ return (-2);
+ } else {
+ /* Check duplicated update for the default export. */
+ if (fe->ae_def != NULL)
+ return (-2);
+ }
+
+ cmd = _CMD_GET(flags);
+
+ /* Flushed file system cannot have "delete" or "update" commands. */
+ if ((cmd == CMD_DELETE || cmd == CMD_UPDATE) &&
+ (_CMD_OPT(fe->oflags) == CMD_OPT_FLUSH))
+ return (-2);
+
+ if (cmd == CMD_DELETE) {
+ /* "delete" command can have only -host or -network flag. */
+ if (flags & ~(CMD_OPT_DELETE|OPT_HOST|OPT_NETWORK))
+ return (-2);
+ } else if (cmd == CMD_FLUSH) {
+ /* "flush" command cannot be used for address specification. */
+ return (-2);
+ } else {
+ u_int i, j;
+
+ /* Should have -mapall or -maproot. */
+ if (!(flags & (OPT_MAPALL|OPT_MAPROOT)) ||
+ (flags & (OPT_MAPALL|OPT_MAPROOT)) ==
+ (OPT_MAPALL|OPT_MAPROOT))
+ return (-2);
+ /* Should have -rw or -ro. */
+ if (!(flags & (OPT_RO|OPT_RW)) ||
+ (flags & (OPT_RO|OPT_RW)) == (OPT_RO|OPT_RW))
+ return (-2);
+ /* Validate credentials and security flavors. */
+ if (cae.ngids > NGROUPS || cae.nsec > NFSE_NSECFLAV ||
+ cae.nsec == 0)
+ return (-2);
+ for (i = 0; i < cae.ngids; ++i)
+ for (j = i + 1; j < cae.ngids; ++j)
+ if (cae.gids[i] == cae.gids[j])
+ return (-2);
+ for (i = 0; i < cae.nsec; ++i)
+ switch (cae.sec[i]) {
+ case AUTH_SYS:
+ case RPCSEC_GSS_KRB5:
+ case RPCSEC_GSS_KRB5I:
+ case RPCSEC_GSS_KRB5P:
+ for (j = i + 1; j < cae.nsec; ++j)
+ if (cae.sec[i] == cae.sec[j])
+ return (-2);
+ break;
+ default:
+ return (-2);
+ }
+ }
+
+ ae = malloc(sizeof(*ae));
+ if (ae == NULL) {
+ syslog(LOG_ERR, "ctl_addr_exp_unpack: malloc: %m");
+ return (-1);
+ }
+
+ if (flags & (OPT_HOST|OPT_NETWORK)) {
+ const struct addr_exp *ae0;
+ struct addr_exp_list *ae_list;
+ uint8_t *addr, *mask;
+ u_int len, maskbits;
+
+ /* Command for -host or -network. */
+ as = malloc(cae.family == AF_INET ?
+ ADDR_SPEC_IPV4_SIZE : ADDR_SPEC_IPV6_SIZE);
+ if (as == NULL) {
+ syslog(LOG_ERR, "ctl_addr_exp_unpack: malloc");
+ free(ae);
+ return (-1);
+ }
+ if (cae.family == AF_INET) {
+ struct addr4_spec *as4;
+
+ len = IPV4_ADDR_LEN;
+ as4 = ADDR4_SPEC_PTR(as);
+ as4->addr = cae.addr_spec.as4.addr;
+ addr = (uint8_t *)&as4->addr.s_addr;
+ mask = (uint8_t *)&as4->mask.s_addr;
+ maskbits = cae.addr_spec.as4.maskbits;
+ ae_list = &fe->ae4_list;
+ } else {
+ struct addr6_spec *as6;
+
+ len = IPV6_ADDR_LEN;
+ as6 = ADDR6_SPEC_PTR(as);
+ as6->addr = cae.addr_spec.as6.addr;
+ addr = as6->addr.s6_addr;
+ mask = as6->mask.s6_addr;
+ maskbits = cae.addr_spec.as6.maskbits;
+ ae_list = &fe->ae6_list;
+ }
+ if (!(flags & OPT_NETWORK)) {
+ if (maskbits != 0)
+ goto failed;
+ maskbits = cae.family == AF_INET ?
+ (IPV4_ADDR_LEN * 8) : (IPV6_ADDR_LEN * 8);
+ as->maskbits = 0;
+ } else
+ as->maskbits = maskbits;
+ if (make_mask(mask, len, maskbits) < 0)
+ goto failed;
+ as = find_addr_spec(cae.family, (flags & OPT_NETWORK), as);
+ TAILQ_FOREACH(ae0, ae_list, link)
+ if (ae0->addr_spec == as)
+ goto failed;
+ TAILQ_INSERT_TAIL(ae_list, ae, link);
+ ae->addr_spec = as;
+ as->ref_count++;
+ } else {
+ /* Command for default export. */
+ ae->addr_spec = NULL;
+ fe->ae_def = ae;
+ }
+
+ ae->cred_exp = NULL;
+ ae->secflavors = NULL;
+ ae->oflags = flags;
+ if (cmd != CMD_DELETE) {
+ /* "add" or "update" commands. */
+ ae->cred_exp = find_cred_exp(cae.uid, cae.ngids, cae.gids);
+ if (ae->cred_exp == NULL)
+ return (-1);
+ ae->cred_exp->ref_count++;
+ ae->secflavors = find_secflavors(cae.nsec, cae.sec);
+ if (ae->secflavors == NULL)
+ return (-1);
+ ae->secflavors->ref_count++;
+ }
+
+ return (0);
+
+failed:
+ free(as);
+ free(ae);
+ return (-2);
+}
+
+/*
+ * Unpack CTL_CMD_EXPORT command data and create fs_exp_list.
+ */
+int
+ctl_export_unpack(void)
+{
+ struct ctl_fs_exp cfe;
+ struct fs_exp_list *fe_list;
+ struct fs_exp *fe;
+ char *ptr;
+ size_t size;
+ int rv;
+
+ fe_list = &fs_exp_list_update;
+ TAILQ_INIT(fe_list);
+ size = ctl_cmd_hdr.size;
+ ptr = ctl_cmd_buf;
+ for (; size > 0;) {
+ if (size < sizeof(cfe))
+ return (-2);
+ memcpy(&cfe, ptr, sizeof(cfe));
+ if (cfe.oflags & ~(FE_CMD_OPTS_ALLFLAGS|CMD_OPT_FLUSH))
+ return (-2);
+ if (cfe.path_len == 0 || cfe.path_len > RPCMNT_PATHLEN)
+ return (-2);
+ if ((fe = malloc(sizeof(*fe))) == NULL ||
+ (fe->path = malloc(cfe.path_len + 1)) == NULL) {
+ free(fe);
+ syslog(LOG_ERR, "ctl_export_unpack: malloc: %m");
+ return (-1);
+ }
+ size -= sizeof(cfe);
+ if (size < cfe.path_len)
+ return (-2);
+ ptr += sizeof(cfe);
+ memcpy(fe->path, ptr, cfe.path_len);
+ fe->path[cfe.path_len] = '\0';
+ if (fs_exp_by_path(fe_list, fe->path) != NULL) {
+ free(fe->path);
+ free(fe);
+ return (-2);
+ }
+ init_fs_exp(fe);
+ TAILQ_INSERT_TAIL(fe_list, fe, link);
+ fe->oflags = cfe.oflags;
+ if (strchr(fe->path, '\0') != fe->path + cfe.path_len)
+ return (-2);
+ ptr += cfe.path_len;
+ size -= cfe.path_len;
+ if (size < cfe.nspec * sizeof(struct ctl_addr_exp))
+ return (-2);
+ size -= cfe.nspec * sizeof(struct ctl_addr_exp);
+ fe->nspec = cfe.nspec;
+ for (; cfe.nspec != 0; --cfe.nspec) {
+ rv = ctl_addr_exp_unpack(ptr, fe);
+ if (rv < 0)
+ return (rv);
+ ptr += sizeof(struct ctl_addr_exp);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Pack a command with no data.
+ */
+int
+ctl_nodata_pack(void)
+{
+ ctl_cmd_hdr.size = 0;
+ return (0);
+}
+
+/*
+ * Unpack a command that does not require data.
+ */
+int
+ctl_nodata_unpack(void)
+{
+ if (ctl_cmd_hdr.size != 0)
+ return (-2);
+ return (0);
+}
+
+/*
+ * Apply settings from ae1_list to fe2. This function does not allocate
+ * memory, it can fail only if new settings cannot be applied to existent
+ * configuration.
+ */
+static int
+update_addr_exp_list(const int check, const u_int family,
+ struct addr_exp_list *ae1_list, struct fs_exp *fe2)
+{
+ struct addr_exp_list *ae2_list;
+ struct addr_exp *ae1, *ae2, *ae1_next;
+ uint32_t cmd, flags;
+
+ ae2_list = family == AF_INET ? &fe2->ae4_list : &fe2->ae6_list;
+ flags = (goflags | fe2->oflags) & OPT_NO_MNT_DMP;
+ TAILQ_FOREACH_SAFE(ae1, ae1_list, link, ae1_next) {
+ TAILQ_FOREACH(ae2, ae2_list, link)
+ if (ae1->addr_spec == ae2->addr_spec)
+ break;
+ cmd = _CMD_GET(ae1->oflags);
+ if (!check) {
+ ae1->oflags &= ~_CMD_ALLBITS;
+ ae1->oflags |= flags | CMD_OPT_ADD;
+ }
+ switch (cmd) {
+ case CMD_ADD:
+ if (ae2 != NULL)
+ return (-1);
+ if (!check) {
+ TAILQ_REMOVE(ae1_list, ae1, link);
+ link_addr_exp(ae2_list, ae1);
+ fe2->nspec++;
+ inc_no_mnt_exp(fe2, ae1->oflags);
+ }
+ break;
+ case CMD_UPDATE:
+ if (ae2 == NULL)
+ return (-1);
+ if (!check) {
+ ae2->cred_exp->ref_count--;
+ ae2->cred_exp = ae1->cred_exp;
+ ae1->cred_exp = NULL;
+ ae2->secflavors->ref_count--;
+ ae2->secflavors = ae1->secflavors;
+ ae1->secflavors = NULL;
+ if (!(ae2->oflags & OPT_DENY) &&
+ (ae1->oflags & OPT_DENY))
+ mntlist_recheck(family, fe2);
+ dec_no_mnt_exp(fe2, ae2->oflags);
+ inc_no_mnt_exp(fe2, ae1->oflags);
+ ae2->oflags = ae1->oflags;
+ }
+ break;
+ case CMD_DELETE:
+ if (ae2 == NULL)
+ return (-1);
+ if (!check) {
+ ae2->addr_spec->ref_count--;
+ ae2->cred_exp->ref_count--;
+ ae2->secflavors->ref_count--;
+ TAILQ_REMOVE(ae2_list, ae2, link);
+ dec_no_mnt_exp(fe2, ae2->oflags);
+ free(ae2);
+ fe2->nspec--;
+ mntlist_recheck(family, fe2);
+ }
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Get data from fe1_list and apply it to the current configuration
+ * (that is given in fs_exp_list). If the check flag is on, then just
+ * try to apply. This function does not allocate memory, it can fail
+ * only if new settings cannot be applied to existent configuration.
+ * Always check new settings first, if everything is correct then load
+ * updates to nfsserver and only then apply the same new settings to
+ * the current configuration.
+ */
+int
+update_conf(const int check)
+{
+ struct addr_exp_list ae4_list, ae6_list;
+ struct fs_exp_list *fe1_list, *fe2_list;
+ struct fs_exp *fe1, *fe2, *fe1_next;
+ struct addr_exp *ae1, *ae2;
+ int rv;
+ uint32_t cmd;
+
+ fe1_list = &fs_exp_list_update;
+ fe2_list = fs_exp_list;
+ TAILQ_FOREACH_SAFE(fe1, fe1_list, link, fe1_next) {
+ fe2 = fs_exp_by_path(fe2_list, fe1->path);
+ if (fe2 != NULL) {
+ /* Update existent file system. */
+ if (fe2->oflags & OPT_EXPORTED) {
+ fe1->oflags |= OPT_EXPORTED;
+ fe1->fsid = fe2->fsid;
+ }
+ /* Inherit existent -quiet option. */
+ fe1->oflags |= fe2->oflags & OPT_QUIET;
+ } else {
+ /* New file system. */
+ fe1->oflags |= OPT_NEWEXPORT;
+ }
+ if (_CMD_OPT(fe1->oflags) == CMD_OPT_FLUSH) {
+ /* Commands were check in ctl_addr_exp_unpack(). */
+ if (fe2 == NULL)
+ return (-2);
+ if (check)
+ continue;
+ if (fe2 != NULL) {
+ /* Flush configuration. */
+ free_fs_exp(fe2_list, fe2);
+ fe2 = NULL;
+ }
+ }
+ if (fe2 == NULL) {
+ /* Flushed or new file system. */
+ if (fe1->nspec == 0 &&
+ !(fe1->oflags & FE_CMD_OPTS_ALLFLAGS))
+ continue;
+ if (!check) {
+ /* Use already allocated fs_exp structure. */
+ TAILQ_REMOVE(fe1_list, fe1, link);
+ link_fs_exp(fe2_list, fe1);
+ fe1->nspec = 0;
+ }
+ fe2 = fe1;
+ }
+ /*
+ * Apply settings (have to use separate lists since
+ * fe1 and fe2 can point to the same data).
+ */
+ if (!check) {
+ /* Inherit new file system options. */
+ fe2->oflags |= fe1->oflags & FE_CMD_OPTS_ALLFLAGS;
+ }
+ TAILQ_INIT(&ae4_list);
+ TAILQ_CONCAT(&ae4_list, &fe1->ae4_list, link);
+ rv = update_addr_exp_list(check, AF_INET, &ae4_list, fe2);
+ TAILQ_CONCAT(&fe1->ae4_list, &ae4_list, link);
+ if (rv < 0)
+ return (-1);
+ TAILQ_INIT(&ae6_list);
+ TAILQ_CONCAT(&ae6_list, &fe1->ae6_list, link);
+ rv = update_addr_exp_list(check, AF_INET6, &ae6_list, fe2);
+ TAILQ_CONCAT(&fe1->ae6_list, &ae6_list, link);
+ if (rv < 0)
+ return (-1);
+ if (fe1->ae_def != NULL) {
+ cmd = _CMD_GET(fe1->ae_def->oflags);
+ if (!check) {
+ fe1->ae_def->oflags &= ~_CMD_ALLBITS;
+ fe1->ae_def->oflags |= CMD_OPT_ADD;
+ }
+ switch (cmd) {
+ case CMD_ADD:
+ if (fe1 != fe2) {
+ if (fe2->ae_def != NULL)
+ return (-1);
+ if (!check) {
+ fe2->ae_def = fe1->ae_def;
+ fe1->ae_def = NULL;
+ }
+ }
+ if (!check) {
+ fe2->nspec++;
+ inc_no_mnt_exp(fe2,
+ fe2->ae_def->oflags);
+ }
+ break;
+ case CMD_UPDATE:
+ if (fe1 == fe2 || fe2->ae_def == NULL)
+ return (-1);
+ if (!check) {
+ ae1 = fe1->ae_def;
+ ae2 = fe2->ae_def;
+ ae2->cred_exp->ref_count--;
+ ae2->cred_exp = ae1->cred_exp;
+ ae1->cred_exp = NULL;
+ ae2->secflavors->ref_count--;
+ ae2->secflavors = ae1->secflavors;
+ ae1->secflavors = NULL;
+ dec_no_mnt_exp(fe2, ae2->oflags);
+ inc_no_mnt_exp(fe2, ae1->oflags);
+ ae2->oflags = ae1->oflags;
+ }
+ break;
+ case CMD_DELETE:
+ if (fe1 == fe2 || fe2->ae_def == NULL)
+ return (-1);
+ if (!check) {
+ ae2 = fe2->ae_def;
+ ae2->cred_exp->ref_count--;
+ ae2->secflavors->ref_count--;
+ dec_no_mnt_exp(fe2, ae2->oflags);
+ free(ae2);
+ fe2->ae_def = NULL;
+ fe2->nspec--;
+ mntlist_recheck(AF_INET, fe2);
+ mntlist_recheck(AF_INET6, fe2);
+ }
+ break;
+ }
+ }
+ if (!check) {
+ /* Is a directory still exported? */
+ if (!(fe2->oflags & OPT_EXPORTED))
+ fe1->oflags &= ~OPT_EXPORTED;
+ }
+ }
+ return (0);
+}
diff -ruN empty/mountd_conf.h mountd/mountd_conf.h
--- empty/mountd_conf.h 1970-01-01 03:00:00.000000000 +0300
+++ mountd/mountd_conf.h 2009-06-26 14:12:41.000000000 +0300
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2009 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD:$
+ */
+
+#ifndef MOUNTD_CONF_H
+#define MOUNTD_CONF_H
+
+/*
+ * Options flags, not all of them should be visible outside of
+ * mountd_conf.c, but it looks better to keep them all in one place.
+ */
+#define OPT_QUIET 0x00000001 /* -quiet */
+#define OPT_RO 0x00000002 /* -ro */
+#define OPT_RW 0x00000004 /* -rw */
+#define OPT_ALLDIRS 0x00000008 /* -alldirs */
+#define OPT_PUBLIC 0x00000010 /* -public */
+#define OPT_INDEX 0x00000020 /* -index */
+#define OPT_MAPALL 0x00000040 /* -mapall */
+#define OPT_MAPROOT 0x00000080 /* -maproot */
+#define OPT_MASK 0x00000100 /* -mask or -network=.../yy */
+#define OPT_NETWORK 0x00000200 /* -network */
+#define OPT_HOST 0x00000400 /* -host */
+#define OPT_WEBNFS 0x00000800 /* -webnfs */
+#define OPT_SEC 0x00001000 /* -sec */
+#define OPT_NOSPEC 0x00002000 /* -nospec */
+#define OPT_NO_MNT_DMP 0x00004000 /* -no_mntproc_dump */
+#define OPT_NO_MNT_EXP 0x00008000 /* -no_mntproc_export */
+#define OPT_DENY 0x00010000 /* '!' flag in address spec. */
+#define OPT_ARG 0x01000000 /* Option requires an argument. */
+#define OPT_COMPAT 0x02000000 /* Obsolete option. */
+#define OPT_ERRCONFIG 0x04000000 /* Wrong configuration. */
+#define OPT_EXPORTED 0x10000000 /* File system is exported. */
+#define OPT_NEWEXPORT 0x20000000 /* New file system export. */
+
+/*
+ * Commands are encoded in last two bits of options value.
+ */
+#define _CMD_SHIFT (30)
+#define _CMD_GET(x) ((x) >> _CMD_SHIFT)
+#define _CMD_ALLBITS (~((UINT32_C(1) << _CMD_SHIFT) - 1))
+#define _CMD_OPT(x) ((x) & _CMD_ALLBITS)
+#define CMD_ADD (UINT32_C(0))
+#define CMD_UPDATE (UINT32_C(1))
+#define CMD_DELETE (UINT32_C(2))
+#define CMD_FLUSH (UINT32_C(3))
+#define CMD_OPT_ADD (CMD_ADD << _CMD_SHIFT)
+#define CMD_OPT_UPDATE (CMD_UPDATE << _CMD_SHIFT)
+#define CMD_OPT_DELETE (CMD_DELETE << _CMD_SHIFT)
+#define CMD_OPT_FLUSH (CMD_FLUSH << _CMD_SHIFT)
+
+extern int set_exp_file(int, char **);
+extern int add_exp_cmd(char *);
+
+extern int configure(int);
+extern void unconfigure(void);
+extern void show_conf(void);
+extern int update_conf(const int);
+extern void free_unref_conf(void);
+
+extern void free_fs_exp_list(struct fs_exp_list *);
+
+extern char *addr_spec_str(const u_int, const struct addr_spec *);
+extern int addrcmp(const uint8_t *, const uint8_t *, const u_int);
+
+extern int crit_fs_err(const int);
+
+extern int ctl_export_pack(void);
+extern int ctl_export_unpack(void);
+extern int ctl_nodata_pack(void);
+extern int ctl_nodata_unpack(void);
+
+#define ctl_reload_pack ctl_nodata_pack
+#define ctl_reload_unpack ctl_nodata_unpack
+#define ctl_flush_pack ctl_nodata_pack
+#define ctl_flush_unpack ctl_nodata_unpack
+
+#endif /* !MOUNTD_CONF_H */
diff -ruN empty/mountd_xdr.c mountd/mountd_xdr.c
--- empty/mountd_xdr.c 1970-01-01 03:00:00.000000000 +0300
+++ mountd/mountd_xdr.c 2009-06-26 14:12:42.000000000 +0300
@@ -0,0 +1,308 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California.
+ * Copyright (c) 2009 Andrey Simonenko
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD:$");
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+
+#include <nfs/nfsproto.h>
+#include <nfs/rpcv2.h>
+#include <nfsserver/nfs_export.h>
+
+#include <string.h>
+#include <syslog.h>
+
+#include "mountd.h"
+#include "mountd_conf.h"
+#include "mountd_xdr.h"
+
+/*
+ * NFSv2 and NFSv3 define formats for RPC mount procedures.
+ * Both standards use the same XDR data structures (except file handle).
+ */
+
+/*
+ * XDR routine for generating file handle reply.
+ */
+int
+xdr_fhs(XDR *xdrs, char *cp)
+{
+ struct fhreturn *fhrp;
+ u_int val;
+#ifdef WITH_SEC
+ u_int i;
+#else
+ int secflav;
+#endif
+
+ fhrp = (struct fhreturn *)cp;
+
+ val = NFS_OK;
+ if (!xdr_u_int(xdrs, &val))
+ goto xdr_int_failed;
+
+ switch (fhrp->fhr_vers) {
+ case 1:
+ if (!xdr_opaque(xdrs, (char *)&fhrp->fhr_fh, NFSX_V2FH))
+ goto xdr_opaque_failed;
+ return (TRUE);
+ case 3:
+ val = NFSX_V3FH;
+ if (!xdr_u_int(xdrs, &val))
+ goto xdr_int_failed;
+ if (!xdr_opaque(xdrs, (char *)&fhrp->fhr_fh, val))
+ goto xdr_opaque_failed;
+#ifdef WITH_SEC
+ if (!xdr_u_int(xdrs, &fhrp->fhr_nsec))
+ goto xdr_int_failed;
+ for (i = 0; i < fhrp->fhr_nsec; ++i)
+ if (!xdr_int(xdrs, &fhrp->fhr_sec[i]))
+ goto xdr_int_failed;
+#else
+ val = 1;
+ if (!xdr_u_int(xdrs, &val))
+ goto xdr_int_failed;
+ secflav = RPCAUTH_UNIX;
+ if (!xdr_int(xdrs, &secflav))
+ goto xdr_int_failed;
+#endif
+ return (TRUE);
+ }
+ return (FALSE);
+
+xdr_opaque_failed:
+ syslog(LOG_WARNING, "xdr_fhs: xdr_opaque failed");
+ return (FALSE);
+
+xdr_int_failed:
+ syslog(LOG_WARNING, "xdr_fhs: xdr_int failed");
+ return (FALSE);
+}
+
+/*
+ * XDR routine for generating list of hosts with mounted paths.
+ */
+/* ARGSUSED1 */
+int
+xdr_mntlist(XDR *xdrs, char *cp __unused)
+{
+ const struct mnt_host_list *mh_list;
+ const struct mnt_host *mh;
+ const struct fs_exp_list *fe_list;
+ const struct fs_exp *fe;
+ struct mnt_path *mp;
+ char *pathp, *hostp;
+ size_t path_len;
+ int family;
+ bool_t bool;
+ char path[PATH_MAX];
+ char host[INET6_ADDRSTRLEN];
+
+ if (no_mntproc_dump)
+ goto done;
+ bool = TRUE;
+ pathp = path;
+ hostp = host;
+ fe_list = fs_exp_list;
+ TAILQ_FOREACH(fe, fe_list, link) {
+ if ((fe->oflags & OPT_NO_MNT_DMP) ||
+ !(fe->oflags & OPT_EXPORTED))
+ continue;
+ path_len = fe->path_len;
+ memcpy(path, fe->path, fe->path_len);
+ for (family = AF_INET, mh_list = &fe->mh4_list;;) {
+ LIST_FOREACH(mh, mh_list, link) {
+ if (inet_ntop(family, MNT_HOST_ADDR(mh),
+ host, sizeof(host)) == NULL) {
+ syslog(LOG_ERR, "xdr_mntlist: "
+ "inet_ntop: %m");
+ return (FALSE);
+ }
+ LIST_FOREACH(mp, &mh->mp_list, link) {
+ if (!xdr_bool(xdrs, &bool))
+ goto xdr_bool_failed;
+ if (!xdr_string(xdrs, &hostp,
+ RPCMNT_NAMELEN))
+ goto xdr_string_failed;
+ if (mp->subpath == subpath_empty)
+ path[path_len] = '\0';
+ else {
+ path[path_len] = '/';
+ strncpy(path + path_len + 1,
+ mp->subpath,
+ PATH_MAX - path_len - 2);
+ path[PATH_MAX - 1] = '\0';
+ }
+ if (!xdr_string(xdrs, &pathp,
+ RPCMNT_PATHLEN))
+ goto xdr_string_failed;
+ }
+ }
+ if (mh_list == &fe->mh6_list)
+ break;
+ family = AF_INET6;
+ mh_list = &fe->mh6_list;
+ }
+ }
+done:
+ bool = FALSE;
+ if (xdr_bool(xdrs, &bool))
+ return (TRUE);
+
+xdr_bool_failed:
+ syslog(LOG_WARNING, "xdr_mntlist: xdr_bool failed");
+ return (FALSE);
+
+xdr_string_failed:
+ syslog(LOG_WARNING, "xdr_mntlist: xdr_string failed");
+ return (FALSE);
+}
+
+/*
+ * XDR conversion for a file system path.
+ */
+int
+xdr_path(XDR *xdrs, char *path)
+{
+ if (xdr_string(xdrs, &path, RPCMNT_PATHLEN))
+ return (TRUE);
+ syslog(LOG_WARNING, "xdr_path: xdr_string failed");
+ return (FALSE);
+}
+
+/*
+ * XDR routine for generating brief or complete information
+ * about exported file systems.
+ */
+static int
+xdr_export_common(XDR *xdrs, const int brief)
+{
+ static char addr_brief[] = "(...)";
+
+ const struct fs_exp_list *fe_list;
+ const struct addr_exp_list *ae_list;
+ struct addr_exp *ae;
+ struct fs_exp *fe;
+ char *str;
+ u_int family;
+ bool_t true, false;
+
+ false = FALSE;
+ if (no_mntproc_export)
+ goto done;
+ true = TRUE;
+ fe_list = fs_exp_list;
+ TAILQ_FOREACH(fe, fe_list, link) {
+ if (!(fe->oflags & OPT_EXPORTED) ||
+ (fe->oflags & OPT_NO_MNT_EXP) ||
+ fe->no_mnt_exp == fe->nspec)
+ continue;
+ if (!xdr_bool(xdrs, &true))
+ goto xdr_bool_failed;
+ if (!xdr_string(xdrs, &fe->path, RPCMNT_PATHLEN))
+ goto xdr_string_failed;
+ if (brief) {
+ if (!xdr_bool(xdrs, &true))
+ goto xdr_bool_failed;
+ str = addr_brief;
+ if (!xdr_string(xdrs, &str, RPCMNT_PATHLEN))
+ goto xdr_string_failed;
+ } else if (fe->ae_def == NULL) {
+ for (family = AF_INET, ae_list = &fe->ae4_list;;) {
+ TAILQ_FOREACH_REVERSE(ae, ae_list,
+ addr_exp_list, link) {
+ if (ae->oflags &
+ (OPT_DENY|OPT_NO_MNT_EXP))
+ continue;
+ if (!xdr_bool(xdrs, &true))
+ goto xdr_bool_failed;
+ str = addr_spec_str(family,
+ ae->addr_spec);
+ if (!xdr_string(xdrs, &str,
+ RPCMNT_NAMELEN))
+ goto xdr_string_failed;
+ }
+ if (ae_list == &fe->ae6_list)
+ break;
+ family = AF_INET6;
+ ae_list = &fe->ae6_list;
+ }
+ }
+ if (!xdr_bool(xdrs, &false))
+ goto xdr_bool_failed;
+ }
+done:
+ if (xdr_bool(xdrs, &false))
+ return (TRUE);
+
+xdr_bool_failed:
+ syslog(LOG_WARNING, "xdr_export_common: xdr_bool failed");
+ return (FALSE);
+
+xdr_string_failed:
+ syslog(LOG_WARNING, "xdr_export_common: xdr_string failed");
+ return (FALSE);
+}
+
+/*
+ * XDR routine for generating complete information
+ * about exported file systems.
+ */
+/* ARGSUSED1 */
+int
+xdr_export_compl(XDR *xdrs, char *cp __unused)
+{
+ return (xdr_export_common(xdrs, 0));
+}
+
+/*
+ * XDR routine for generating brief information
+ * about exported file systems.
+ */
+/* ARGSUSED1 */
+int
+xdr_export_brief(XDR *xdrs, char *cp __unused)
+{
+ return (xdr_export_common(xdrs, 1));
+}
diff -ruN empty/mountd_xdr.h mountd/mountd_xdr.h
--- empty/mountd_xdr.h 1970-01-01 03:00:00.000000000 +0300
+++ mountd/mountd_xdr.h 2009-06-26 14:12:42.000000000 +0300
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD:$
+ */
+
+#ifndef MOUNTD_XDR_H
+#define MOUNTD_XDR_H
+
+/*
+ * An argument for xdr_fhs().
+ */
+struct fhreturn {
+ u_int fhr_vers; /* Service protocol version. */
+ nfsfh_t fhr_fh; /* File handle from getfh(). */
+ u_int fhr_nsec; /* Number of security flavors. */
+ int *fhr_sec; /* Security flavors. */
+};
+
+extern int xdr_fhs(XDR *, char *);
+extern int xdr_mntlist(XDR *, char *);
+extern int xdr_path(XDR *, char *);
+extern int xdr_export_compl(XDR *, char *);
+extern int xdr_export_brief(XDR *, char *);
+
+#endif /* !MOUNTD_XDR_H */
diff -ruN empty/netgroup.5 mountd/netgroup.5
--- empty/netgroup.5 1970-01-01 03:00:00.000000000 +0300
+++ mountd/netgroup.5 2009-06-01 12:12:57.000000000 +0300
@@ -0,0 +1,199 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD: src/usr.sbin/mountd/netgroup.5,v 1.14 2005/01/18 20:02:38 ru Exp $
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file
+specifies
+.Dq netgroups ,
+which are sets of
+.Sy (host, user, domain)
+tuples that are to be given similar network access.
+.Pp
+Each line in the file
+consists of a netgroup name followed by a list of the members of the
+netgroup.
+Each member can be either the name of another netgroup or a specification
+of a tuple as follows:
+.Bd -literal -offset indent
+(host, user, domain)
+.Ed
+.Pp
+where the
+.Sy host ,
+.Sy user ,
+and
+.Sy domain
+are character string names for the corresponding component.
+Any of the comma separated fields may be empty to specify a
+.Dq wildcard
+value or may consist of the string
+.Ql -
+to specify
+.Dq no valid value .
+The members of the list may be separated by whitespace and/or commas;
+the
+.Ql \e
+character may be used at the end of a line to specify
+line continuation.
+Lines are limited to 1024 characters.
+The functions specified in
+.Xr getnetgrent 3
+should normally be used to access the
+.Nm
+database.
+.Pp
+Lines that begin with a
+.Ql #
+are treated as comments.
+.Sh NIS/YP INTERACTION
+On most other platforms,
+.Nm Ns s
+are only used in conjunction with
+.Tn NIS
+and local
+.Pa /etc/netgroup
+files are ignored.
+With
+.Fx ,
+.Nm Ns s
+can be used with either
+.Tn NIS
+or local files, but there are certain
+caveats to consider.
+The existing
+.Nm
+system is extremely inefficient where
+.Fn innetgr 3
+lookups are concerned since
+.Nm
+memberships are computed on the fly.
+By contrast, the
+.Tn NIS
+.Nm
+database consists of three separate maps (netgroup, netgroup.byuser
+and netgroup.byhost) that are keyed to allow
+.Fn innetgr 3
+lookups to be done quickly.
+The
+.Fx
+.Nm
+system can interact with the
+.Tn NIS
+.Nm
+maps in the following ways:
+.Bl -bullet -offset indent
+.It
+If the
+.Pa /etc/netgroup
+file does not exist, or it exists and is empty, or
+it exists and contains only a
+.Sq + ,
+and
+.Tn NIS
+is running,
+.Nm
+lookups will be done exclusively through
+.Tn NIS ,
+with
+.Fn innetgr 3
+taking advantage of the netgroup.byuser and
+netgroup.byhost maps to speed up searches.
+(This
+is more or less compatible with the behavior of SunOS and
+similar platforms.)
+.It
+If the
+.Pa /etc/netgroup
+exists and contains only local
+.Nm
+information (with no
+.Tn NIS
+.Sq +
+token), then only the local
+.Nm
+information will be processed (and
+.Tn NIS
+will be ignored).
+.It
+If
+.Pa /etc/netgroup
+exists and contains both local netgroup data
+.Pa and
+the
+.Tn NIS
+.Sq +
+token, the local data and the
+.Tn NIS
+netgroup
+map will be processed as a single combined
+.Nm
+database.
+While this configuration is the most flexible, it
+is also the least efficient: in particular,
+.Fn innetgr 3
+lookups will be especially slow if the
+database is large.
+.El
+.Sh FILES
+.Bl -tag -width /etc/netgroup -compact
+.It Pa /etc/netgroup
+the netgroup database
+.El
+.Sh COMPATIBILITY
+The file format is compatible with that of various vendors, however it
+appears that not all vendors use an identical format.
+.Sh SEE ALSO
+.Xr getnetgrent 3
+.Sh BUGS
+The interpretation of access restrictions based on the member tuples of a
+netgroup is left up to the various network applications.
+Also, it is not obvious how the domain specification
+applies to the
+.Bx
+environment.
+.Pp
+The
+.Nm
+database should be stored in the form of a
+hashed
+.Xr db 3
+database just like the
+.Xr passwd 5
+database to speed up reverse lookups.
diff -ruN empty/pathnames.h mountd/pathnames.h
--- empty/pathnames.h 1970-01-01 03:00:00.000000000 +0300
+++ mountd/pathnames.h 2009-06-26 14:12:43.000000000 +0300
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ * $FreeBSD: src/usr.sbin/mountd/pathnames.h,v 1.2 2004/08/07 04:27:51 imp Exp $
+ */
+
+#ifndef MOUNTD_PATHNAMES_H
+#define MOUNTD_PATHNAMES_H
+
+#define _PATH_EXPORTS "/etc/exports"
+#define _PATH_RMOUNTLIST "/var/db/mountdtab"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
+#define _PATH_MOUNTD_CTLSOCKET "/var/run/mountd.socket"
+
+#endif /* !MOUNTD_PATHNAMES_H */
#########################################################################
>Release-Note:
>Audit-Trail:
>Unformatted:
_______________________________________________
freebsd-bugs@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscribe@freebsd.org"
>Number: 136865
>Category: kern
>Synopsis: NFS exports atomic and on-the-fly atomic updates
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Fri Jul 17 11:20:05 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator: Andrey Simonenko
>Release: FreeBSD 7.2-STABLE amd64
>Organization:
>Environment:
FreeBSD 7.2-STABLE, but possible to modify for 8.0.
>Description:
Here I want to describe changes that allow to make atomic updates of
NFS exports lists, dynamic on-the-fly atomic updates of NFS exports
lists and improve security of NFS exports.
Solved tasks:
-------------
1. NFS export specifications (spec -- for short) updates are atomic. NFS
server's users will not get EACCES for exported file systems or wrong
access rights while exports list is reloaded.
2. The mountd utility has an option for testing configuration, now one can
check and see real configuration not loading it into nfsserver.
3. The mountd utility does not load incomplete settings to nfsserver,
wrong configuration will not allow denied exports.
4. Atomic on-the-fly modifications of NFS export specifications were
implemented, it is possible to change exports settings dynamically.
5. If some file system is mounted or unmounted, then SIGHUP signal sending
to mountd is not required, this change removes several race conditions.
6. NFS exports related code is a part of nfsserver/ code, NFS export related
data can be removed from directories not related to NFS.
Which actions are atomic?
-------------------------
1. Loading export settings from exports(5) file into nfsserver is atomic for
each exported file system and for all exported file systems.
2. Loading updates into nfsserver is atomic for each exported file system and
for all exported file systems.
3. If a file system was mounted, then loading export settings for it into
nfssever is atomic for this file system.
Which actions are not atomic?
-----------------------------
1. Loading export specifications for WebNFS file system and specifying
WebNFS related settings require more than one system call.
2. Since VFS events such as mounting and unmounting are asynchronous,
events for all exported and not exported file systems are checked
as separate system calls.
Since nmount(2) is not used in this implementation and subdirectories exports
are not allowed, it is unlikely that these changes will be accepted.
If absence of insecure subdirectories exports is not a problem, then it
it is possible to support both existent mountd and new API on 7-STABLE
(see nfsserver/nfs_srvsubs.c:nfsrv_fhtovp() function patch).
The mountd utility was completely rewritten, actually the better name
for new utility with new properties would be "nfse". The single source
file mountd.c was split into three .c and three .h files: mountd.c
(previous code was rewritten and new code was added), mountd.h (new code),
mountd_conf.c (new code), mountd_conf.h (new code), mountd_xdr.c (previous
code was updated to support new data structure and options), mountd_xdr.h
(previous code).
The analog of kern/vfs_export.c was written from zero and now it is called
nfssever/nfs_export.c.
This version of mountd can be used on modified 7.2 system.
It was tested (except WebNFS related settings) on amd64 and i386 arch.
Support of 8.0 system is possible, it is necessary to modify the
nfs_export.c:nfse_check() function (~60 lines) and add new NFSv4 related
options, but since there are sys/nfssever/ and sys/fs/nfsserver/ that have
functions that call VFS_CHECKEXP and there is activity in NFS development,
it is unclear which arguments nfse_check() should accept. It is better
to discuss arguments list and semantic of nfse_check() first. I think this
is the only one function that does not allow to use these changes on 8.0
system.
Available patches have only necessary changes to sys/nfsserver/, sys/kern/,
sys/sys/ and sys/conf/. Complete changes require removing all NFS exports
related data and code from directories not related to NFS. Also nfs_pub
structure should be a part of the new nfsserver/nfs_export.c file.
I did not make all these changes (all file systems, all NFS related flags
in mount.h, etc.), just to show the main parts of changes.
The following text contains more or less detail description of changes
(definitely I forgot to mention something here).
Major improvements:
-------------------
* Now all export spec updates are atomic. mountd uses nfssvc(2) for this
(the new nfssvc(NFSSVC_EXPORT) call is used). Now it is safe to reload
exports files.
* New nfs_export.c file was added to nfsserver/, all API details are
located in nfs_export.h. All NFS related flags and structures are part
of nfsserver.
* Now mountd uses kernel event EVFILT_FS to see mount and umount VFS events.
The mount(8) utility should not send sighup signal to mountd any more.
New EVENTHANDLERs were declared: vfs_mount_event and vfs_unmount_event.
Registered function for these handlers are invoked when a file system is
mounted or unmounted respectively. The nfsserver uses these event handlers
to synchronizes own data with available file systems. Memory leak was
removed when an exported file system is unmounted. Now the nfssever
understands covered file systems (file system mounted on mount point of
another file system).
* The mountd utility has a new option -c, that allows to modify export spec
on-the-fly. One can clear, add, update, delete export spec. All updates
are atomic. One commands set works like a transaction with changes,
it is applied completely or is not applied at all.
* Now mountd has the -t switch: parse configuration files or commands and
output all settings to stdout. This option allows to check and see real
configuration.
Incompatible security changes:
------------------------------
* Now subdirectory export is disallowed. Subdirectory export does not
improve security, instead it is the right way for misconfiguration
(export settings for a subdirectory can be completely unrelated to this
subdirectory and does not protect access to another parts of exported
file system).
The nfsserver exports file systems, not directories, looks like that
subdirectory export for NFS is too complex or impossible to implement
completely. Anyway there is nullfs.
Having read RFCs, documentation for another NFS implementations and
thoughts in user groups in Internet I think that this (radical)
modification will improve security. This version of mountd has the same
logic of export rules as nfsserver has.
* Now mountd allows to mount file systems, subdirectories and regular files
(if the -r flag is on) in exported file systems by default. The -alldirs
option became obsolete.
Compatible security changes:
----------------------------
* Ignoring exports files is not safe, since remote users can get wrong access
rights. Alternative compatible solution: all exports file must be present,
a user can specify directory/ and all regular files from the given
directories will be loaded (any directory can be absent).
* Now if mountd cannot correctly parse export specification for some file
system, then it does not load anything to nfsserver for this file system.
Ignoring something in exports file is not safe.
* Now security flavors are per address specification settings in nfsserver
and mountd.
* In rare cases mountd completely ignore settings in exports file, and does
not load anything into nfsserver (this can happen if mistake in
configuration does not allow to finish file parsing).
Updates for mountd:
-------------------
* Now everywhere IPv4 and IPv6 addresses are used, since the kernel knows
nothing about domain names, netgroups, etc. Now mountdtab file contains
only address, MOUNT protocol's procedure EXPORT and DUMP output addresses.
This removes problems with reverse name resolving, but sometimes entries
are not removed on unmount (depends on used address).
* Better output for MOUNT protocol's procedure EXPORT: host is an address,
network is an address with prefix.
* Now mountdtab is parsed more carefully.
* Zone scope index checking was removed for IPv6 addresses, nfsserver does
not check zone scope index.
* Now mountdtab is saved only when mountd exists, no other program in the base
system uses this file. The representation of mountdtab file's content in
memory was optimized.
* Do not leave PID file if some error occurred and mountd exited.
* Allowed to use loopback addresses in the -h option. (I do not like design
idea of -h, -p and similar options.)
* Corrected incorrect binding when -p option is not used (nobody saw this
because this can happen very seldom, but I could reproduce this error).
* Wrong implementation of mask creation when prefix length is given as
/prefixlength was corrected.
Updates for nfsserver:
----------------------
* Previous nfsserver could access released memory returned by VFS_CHECKEXP.
New code does have this problem.
Updates for exports(5):
-----------------------
* Added new option -host to allow to use host names and the same netgroups
names at once.
* Added new option -rw: read-write access.
* Added flag `!' for hosts and networks, this flag means "deny access".
* Added new line "options: ...", right now it is used for global -sec,
-no_mntproc_dump and -no_mntproc_export options, later it can be used for
NFSv4.
* Added new option -nospec, that means "this line does not have any address
specifications".
* Added new option -no_mntproc_dump to disable MOUNT protocol's procedure
DUMP.
* Added new option -no_mntproc_export to disable MOUNT protocol's procedure
EXPORT.
* exports(5) says that -o is the only one compatible option. Actually there
are others: -root and -r for -maproot, -m for -mask and -n for -network.
Now mountd logs a warning message if an obsolete option is used.
* Do not allow to use any option between -network and -mask.
* Now #-comment can be anywhere in a line.
* \xxx octal number can be used in directories names and option's arguments
for representing an arbitrary character.
* Now it is possible to mix hosts and netgroups with networks:
"host1 -network=somenetwork host2".
* Now it is possible to change options for particular host/network in one
line: "-ro -mapall=user1 host1 -mapall=user2 host2" (host2 will inherit
previous option -ro, but will get new -mapall option). Since previously
exports(5) says that options must be given before hosts and networks, this
change is backward compatible and allows to represent all settings for one
file system in one logical line.
* Content of exports(5) was simplified and updated.
Open questions and tasks:
-------------------------
* There must be a global solution to check whether it is possible to unload
a KLD module when no process currently is working with its syscalls.
* Looks like that first argument for nfssvc(2) is not a set of flags any more
(according to STABLE, CURRENT and NFSv4 implementation). May be there is
a sense to make it a value, not flags.
* WebNFS related data in nfsserver is protected when export settings are set
(in vfs_export.c and new nfs_export.c), but when other parts of nfsserver
access WebNFS related data no synchronization is performed.
* If a file system cannot be exported in NFS, then there must be some flag
to indicate this (MNT_NFSEXPORTABLE or something more general, see
fs/msdosfs/msdosfs_vfsops.c for example).
* Should signals be checked more often in mountd? Right now signals are
checked when mountd is waiting for RPC request, or if nfssvc's commands
transaction timeout occurred. Previous code has race conditions with
signals and does too many things disallowed by SUSv3 in signal handlers.
* Should be there any limitation in nfsserver on number of export
specifications and number of command transactions?
* May be mountd should be renamed to something another, eg. nfse. NFSv4
does not use MNT procedure, but still needs utility for configuring
access rights as I understand. Also nfse command name is more obvious
for -c commands, eg. "nfse -c 'add /fs -ro'". As well /etc/exports can
be renamed to /etc/nfs.exports, /var/db/mountdtab -> /var/db/nfse.mounts.
Also such renaming will allow to use mountd and new nfse in 7-STABLE at
the same time and mount(8) from 7-STABLE will not send SIGHUP to nfse,
since its PID will be saved in the nfse.pid file.
* netgroup.5 can be moved to src/lib/libc/gen/ or src/share/man/man5, this
documentation is not part of mountd.
Examples:
---------
1. Correct file:
exports file:
options: -no_mntproc_dump
/fs -host 1.1.1.1 -ro 2.2.2.2
/fs -network 10.20.30.40
/home -mapall nobody -network 10/8 -mapall operator -network 20.1/8
"mountd -t" output:
configure: reading file exports
Global options:
-no_mntproc_dump
Directory /fs
Export specifications:
-ro -sec sys -maproot=-2:-2 -host 2.2.2.2
-rw -sec sys -maproot=-2:-2 -host 1.1.1.1
-rw -sec sys -maproot=-2:-2 -network 10.0.0.0/8
Directory /home (mount point)
Export specifications:
-rw -sec sys -mapall 2:5 -network 20.0.0.0/8
-rw -sec sys -mapall 65534:65534 -network 10.0.0.0/8
2. Wrong file:
exports:
/fs -ro 1.1.1.1
/fs -network 10/8 -host 1.1.1.1
/home -quiet -ro
"mountd -t" output:
configure: reading file exports
parsing error: exports:2: duplicated address specification 1.1.1.1 was found
in this line
parsing error: exports:2: -host option's argument parsing failed
Directory /fs
Wrong configuration
Directory /home (mount point)
File system options:
-quiet
Export specifications:
-ro -sec sys -maproot=-2:-2 (default)
3. Commands testing:
mountd -t -c 'add /fs -ro -mapall nobody -host 1.1.1.1 -network !10/8' \
-c 'flush /home' -c 'update /usr -ro -mapall operator'
configure: parsing -c commands
Directory /fs
Commands:
-c add -ro -sec sys -mapall 65534:65534 -host 1.1.1.1
-c add -ro -sec sys -mapall 65534:65534 -network !10.0.0.0/8
Directory /home (mount point)
Commands:
-c flush
Directory /usr (mount point)
Commands:
-c update -ro -sec sys -mapall 2:5 (default)
4. Specifying exports(5) files
mountd /etc/exports /etc/local.exports /usr/local/nfs-export/
Files /etc/exports and /etc/local.exports must be present.
The nfs-export directory can be absent, if it present or will be
present, then all regular files from it are read.
Related PR
----------
kern/9619: Restarting mountd kills existing mounts
kern/131342: [nfs] mounting/unmounting of disks causes NFS to fail
Sources
-------
http://comsys.ntu-kpi.kiev.ua/~simon/nfse/
>How-To-Repeat:
Try to copy files from some NFS exported directory and send a SIGHUP signal
to mountd(8) several times.
>Fix:
Patch is not a single file, because modified files are located in different
directories and rewritten mountd(8) source code is given as is (without diff).
src/sys/conf/files:
#########################################################################
--- files.orig 2009-03-23 11:53:09.000000000 +0200
+++ files 2009-05-26 18:09:36.000000000 +0300
@@ -2084,6 +2084,7 @@
nfsclient/nfs_vfsops.c optional nfsclient
nfsclient/nfs_vnops.c optional nfsclient
nfsclient/nfs_lock.c optional nfsclient
+nfsserver/nfs_export.c optional nfsserver
nfsserver/nfs_serv.c optional nfsserver
nfsserver/nfs_srvsock.c optional nfsserver
nfsserver/nfs_srvcache.c optional nfsserver
#########################################################################
src/sys/kern/vfs_mount.c:
#########################################################################
--- vfs_mount.c.orig 2009-03-02 11:23:05.000000000 +0200
+++ vfs_mount.c 2009-04-11 14:40:00.000000000 +0300
@@ -1083,6 +1083,7 @@
mtx_lock(&mountlist_mtx);
TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
mtx_unlock(&mountlist_mtx);
+ EVENTHANDLER_INVOKE(vfs_mount_event, mp);
vfs_event_signal(NULL, VQ_MOUNT, 0);
if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp, td))
panic("mount: lost mount");
@@ -1336,6 +1337,7 @@
coveredvp->v_mountedhere = NULL;
vput(coveredvp);
}
+ EVENTHANDLER_INVOKE(vfs_unmount_event, mp);
vfs_event_signal(NULL, VQ_UNMOUNT, 0);
lockmgr(&mp->mnt_lock, LK_RELEASE, NULL, td);
vfs_mount_destroy(mp);
#########################################################################
src/sys/sys/mount.h:
#########################################################################
--- mount.h.orig 2009-03-16 13:28:22.000000000 +0200
+++ mount.h 2009-04-11 14:38:40.000000000 +0300
@@ -727,6 +727,13 @@
vfs_extattrctl_t vfs_stdextattrctl;
vfs_sysctl_t vfs_stdsysctl;
+#include <sys/eventhandler.h>
+
+typedef void (*vfs_mount_event_fn)(void *, const struct mount *);
+typedef void (*vfs_unmount_event_fn)(void *, const struct mount *);
+EVENTHANDLER_DECLARE(vfs_mount_event, vfs_mount_event_fn);
+EVENTHANDLER_DECLARE(vfs_unmount_event, vfs_unmount_event_fn);
+
#else /* !_KERNEL */
#include <sys/cdefs.h>
#########################################################################
src/sys/nfssever/:
#########################################################################
diff -ruN nfsserver.orig/nfs.h nfsserver/nfs.h
--- nfsserver.orig/nfs.h 2007-11-26 09:01:29.000000000 +0200
+++ nfsserver/nfs.h 2009-05-26 18:06:04.000000000 +0300
@@ -105,6 +105,7 @@
/*
* Flags for nfssvc() system call.
*/
+#define NFSSVC_EXPORT 0x001
#define NFSSVC_NFSD 0x004
#define NFSSVC_ADDSOCK 0x008
diff -ruN nfsserver.orig/nfs_export.c nfsserver/nfs_export.c
--- nfsserver.orig/nfs_export.c 1970-01-01 03:00:00.000000000 +0300
+++ nfsserver/nfs_export.c 2009-06-06 13:44:45.000000000 +0300
@@ -0,0 +1,1568 @@
+/*-
+ * Copyright (c) 2009 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD:$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD:$");
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/rwlock.h>
+#include <sys/stddef.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/ucred.h>
+
+#if defined(INET) || defined(INET6)
+#include <net/radix.h>
+#include <netinet/in.h>
+#endif
+
+#include <rpc/types.h>
+#include <rpc/auth.h>
+
+#include <nfs/rpcv2.h>
+#include <nfsserver/nfs_export.h>
+
+#define NFSE_EXFLAG_RDONLY 0x0001 /* Read-only access. */
+#define NFSE_EXFLAG_MAPALL 0x0002 /* Change credentials for everyone. */
+#define NFSE_EXFLAG_DENY 0x0004 /* Deny access. */
+
+#define RPCSEC_GSS_KRB5 100
+#define RPCSEC_GSS_KRB5I 200
+#define RPCSEC_GSS_KRB5P 300
+
+MALLOC_DEFINE(M_NFSE, "nfse", "NFS export specifications data");
+
+#define NFSE_EXPORT_RLOCK() rw_rlock(&nfse_export_rwlock)
+#define NFSE_EXPORT_RUNLOCK() rw_runlock(&nfse_export_rwlock)
+#define NFSE_EXPORT_WLOCK() rw_wlock(&nfse_export_rwlock)
+#define NFSE_EXPORT_WUNLOCK() rw_wunlock(&nfse_export_rwlock)
+
+int nfse_api_used = 0; /* Non-zero if NFSE API is used. */
+
+static eventhandler_tag nfse_mount_event_tag;
+static eventhandler_tag nfse_unmount_event_tag;
+
+/*
+ * Read-write lock protects exports list, mutex protects exports data
+ * that is not changed and cannot disappear while nfse_check() is running.
+ */
+static struct rwlock nfse_export_rwlock;
+static struct mtx nfse_export_mtx;
+
+/*
+ * Credentials specification.
+ */
+struct nfse_cred_exp {
+ LIST_ENTRY(nfse_cred_exp) link; /* For list building. */
+ uid_t uid; /* Credentials UID. */
+ u_int ngids; /* Number of credentials GIDs. */
+ gid_t gids[NGROUPS]; /* Credentials GIDs. */
+ u_int ref_count; /* Reference counter. */
+};
+
+/* Global list of all credential specifications. */
+static LIST_HEAD(, nfse_cred_exp) nfse_cred_exp_list =
+ LIST_HEAD_INITIALIZER(nfse_cred_exp_list);
+
+/*
+ * Security flavors specification.
+ */
+struct nfse_secflav {
+ LIST_ENTRY(nfse_secflav) link; /* For list building. */
+ u_int nsec; /* Number of security flavors. */
+ int sec[NFSE_NSECFLAV]; /* Security flavors. */
+ u_int ref_count; /* Reference counter. */
+};
+
+/* Global list of all security flavors. */
+static LIST_HEAD(, nfse_secflav) nfse_secflav_list =
+ LIST_HEAD_INITIALIZER(&nfse_secflav_list);
+
+/*
+ * Export specification for one host or network (variable-sized structure).
+ */
+struct nfse_addr_exp {
+#if defined(INET) || defined(INET6)
+ struct radix_node rnodes[2]; /* For building radix tree. */
+#endif
+ u_int flags; /* Export flags. */
+ struct nfse_secflav *secflav; /* Security flavors. */
+ struct nfse_cred_exp *cred_exp; /* Export credentials. */
+ struct sockaddr sockaddr; /* Alignment for addresses. */
+};
+
+#define NFSE_ADDR_EXP_SIZE(size) \
+ (offsetof(struct nfse_addr_exp, sockaddr) + size)
+
+#define NFSE_ADDR_EXP_ADDR4(p) \
+ ((struct sockaddr_in *)&(p)->sockaddr)
+
+#define NFSE_ADDR_EXP_MASK4(p) \
+ (NFSE_ADDR_EXP_ADDR4(p) + 1)
+
+#define NFSE_ADDR_EXP_ADDR6(p) \
+ ((struct sockaddr_in6 *)&(p)->sockaddr)
+
+#define NFSE_ADDR_EXP_MASK6(p) \
+ (NFSE_ADDR_EXP_ADDR6(p) + 1)
+
+/*
+ * All information about one exported file system.
+ */
+struct nfse_fs_exp {
+ LIST_ENTRY(nfse_fs_exp) link; /* For list building. */
+ struct mount *mp; /* Exported file system. */
+ struct nfse_addr_exp *ae_def; /* Default export spec. */
+#ifdef INET
+ struct radix_node_head *ae4_rnh; /* Export spec. for IPv4. */
+#endif
+#ifdef INET6
+ struct radix_node_head *ae6_rnh; /* Export spec. for IPv6. */
+#endif
+};
+
+/* Export specification settings for all file systems. */
+static LIST_HEAD(, nfse_fs_exp) nfse_fs_exp_list =
+ LIST_HEAD_INITIALIZER(&nfse_fs_exp_list);
+
+/*
+ * User command descriptor.
+ */
+struct nfse_ucmd {
+ STAILQ_ENTRY(nfse_ucmd) link; /* For list building. */
+ u_int command; /* Command code. */
+ void *udata; /* Command's user data. */
+ void *kdata; /* Command's kernel data. */
+};
+
+/* List of user commands from one transaction. */
+STAILQ_HEAD(nfse_ucmd_list, nfse_ucmd);
+
+/*
+ * User export specification from NFSE_CMD_EXPORT command.
+ */
+struct nfse_ucmd_export_spec {
+ STAILQ_ENTRY(nfse_ucmd_export_spec) link; /* For list building. */
+ uint32_t command; /* Command code. */
+ u_int maskbits; /* Number of bits in mask. */
+ sa_family_t family; /* Address family. */
+ struct nfse_addr_exp *addr_exp; /* Associated export spec. */
+};
+
+/*
+ * User NFSE_CMD_EXPORT command descriptor.
+ */
+struct nfse_ucmd_export {
+ char *path; /* Mount point. */
+ fsid_t fsid; /* File system ID. */
+ uint32_t status; /* Status flags. */
+ struct nfse_fs_exp *fs_exp; /* New exported file system. */
+ STAILQ_HEAD(, nfse_ucmd_export_spec) es_list; /* Spec. list. */
+};
+
+/*
+ * User NFSE_CMD_EVENT command descriptor.
+ */
+struct nfse_ucmd_event {
+ char *path; /* Mount point. */
+ fsid_t fsid; /* File system ID. */
+ uint32_t status; /* Status flags. */
+};
+
+/*
+ * User NFSE_CMD_WEBNFS command descriptor.
+ */
+struct nfse_ucmd_webnfs {
+ char *path; /* Mount point. */
+ char *index; /* WebNFS index file. */
+ fsid_t fsid; /* File system ID. */
+ struct fid fid; /* File ID of root vnode. */
+ uint32_t status; /* Status flags. */
+ fhandle_t handle; /* Filehandle. */
+};
+
+#define NFSE_TR_TIMEOUT 20 /* Transaction timeout in seconds. */
+
+#define NFSE_TR_BUSY 1 /* Transaction is being updated. */
+#define NFSE_TR_ACTIVE 2 /* Transaction was recently updated. */
+#define NFSE_TR_INACTIVE 3 /* Transaction was not updated. */
+
+/*
+ * Transaction with user commands descriptor.
+ */
+struct nfse_tr {
+ LIST_ENTRY(nfse_tr) link; /* For list building. */
+ pid_t pid; /* PID of transaction owner. */
+ uid_t uid; /* UID of transaction owner. */
+ u_int trid; /* Transaction ID. */
+ u_int state; /* Transaction state. */
+ struct nfse_ucmd_list uc_list; /* User commands list. */
+};
+
+/* List of all transactions. */
+static LIST_HEAD(, nfse_tr) nfse_tr_list =
+ LIST_HEAD_INITIALIZER(nfse_tr_list);
+
+static struct mtx nfse_tr_mtx; /* Transactions list mutex. */
+
+static struct callout nfse_tr_callout; /* Callout to monitor transactions. */
+
+/*
+ * Release memory used by nfse_addr_exp structure.
+ */
+static void
+nfse_addr_exp_free(struct nfse_addr_exp *ae)
+{
+ struct nfse_secflav *sf;
+ struct nfse_cred_exp *ce;
+
+ ce = ae->cred_exp;
+ sf = ae->secflav;
+
+ /* Drop references on shared data. */
+ mtx_lock(&nfse_export_mtx);
+ ce->ref_count--;
+ if (ce->ref_count == 0) {
+ LIST_REMOVE(ce, link);
+ free(ce, M_NFSE);
+ }
+ sf->ref_count--;
+ if (sf->ref_count == 0) {
+ LIST_REMOVE(sf, link);
+ free(sf, M_NFSE);
+ }
+ mtx_unlock(&nfse_export_mtx);
+
+ /* Release own memory. */
+ free(ae, M_NFSE);
+}
+
+/*
+ * Release memory related to WebNFS.
+ */
+static void
+nfse_webnfs_free(void)
+{
+ if (nfs_pub.np_valid) {
+ nfs_pub.np_valid = 0;
+ free(nfs_pub.np_index, M_TEMP);
+ nfs_pub.np_index = NULL;
+ }
+}
+
+/*
+ * Create new nfse_fs_exp structure.
+ */
+static struct nfse_fs_exp *
+nfse_fs_exp_new(void)
+{
+ struct nfse_fs_exp *fe;
+
+ fe = malloc(sizeof(*fe), M_NFSE, M_WAITOK);
+ fe->ae_def = NULL;
+#ifdef INET6
+ fe->ae6_rnh = NULL;
+#endif
+#ifdef INET
+ fe->ae4_rnh = NULL;
+ if (!rn_inithead((void **)&fe->ae4_rnh,
+ offsetof(struct sockaddr_in, sin_addr) << 3))
+ return (NULL);
+#endif
+#ifdef INET6
+ if (!rn_inithead((void **)&fe->ae6_rnh,
+ offsetof(struct sockaddr_in6, sin6_addr) << 3))
+ return (NULL);
+#endif
+ return (fe);
+}
+
+#if defined(INET) || defined(INET6)
+/*
+ * Release memory used by one nfse_addr_exp node in the given radix tree.
+ */
+static int
+nfse_addr_exp_rn_free(struct radix_node *rn, void *h)
+{
+ struct radix_node_head *rnh;
+
+ rnh = h;
+ rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
+ nfse_addr_exp_free((struct nfse_addr_exp *)rn);
+ return (0);
+}
+
+/*
+ * Release memory used by the given radix tree of nfse_addr_exp structures.
+ */
+static void
+nfse_addr_exp_rnh_free(struct radix_node_head *rnh, const int clear)
+{
+ rnh->rnh_walktree(rnh, nfse_addr_exp_rn_free, rnh);
+ if (!clear) {
+ RADIX_NODE_HEAD_DESTROY(rnh);
+ free(rnh, M_RTABLE);
+ }
+}
+#endif
+
+/*
+ * Release memory used by one nfse_fs_exp structure.
+ */
+static void
+nfse_fs_exp_free(struct nfse_fs_exp *fe, const int clear)
+{
+ if (fe->ae_def != NULL) {
+ nfse_addr_exp_free(fe->ae_def);
+ fe->ae_def = NULL;
+ }
+#ifdef INET
+ nfse_addr_exp_rnh_free(fe->ae4_rnh, clear);
+#endif
+#ifdef INET6
+ nfse_addr_exp_rnh_free(fe->ae6_rnh, clear);
+#endif
+ if (!clear) {
+ if (nfs_pub.np_valid && nfs_pub.np_mount == fe->mp)
+ nfse_webnfs_free();
+ free(fe, M_NFSE);
+ }
+}
+
+/*
+ * Release one user command.
+ */
+static void
+nfse_ucmd_free(struct nfse_ucmd *uc)
+{
+ struct nfse_ucmd_event *uc_event;
+ struct nfse_ucmd_export *uc_export;
+ struct nfse_ucmd_export_spec *uc_es, *uc_es_next;
+ struct nfse_ucmd_webnfs *uc_webnfs;
+
+ switch (uc->command) {
+ case NFSE_CMD_EXPORT:
+ uc_export = uc->kdata;
+ free(uc_export->path, M_NFSE);
+ if (uc_export->fs_exp != NULL)
+ nfse_fs_exp_free(uc_export->fs_exp, 0);
+ STAILQ_FOREACH_SAFE(uc_es, &uc_export->es_list, link,
+ uc_es_next) {
+ switch (uc_es->command) {
+ case NFSE_CMD_EXPORT_ADD:
+ case NFSE_CMD_EXPORT_UPDATE:
+ if (uc_es->addr_exp != NULL)
+ nfse_addr_exp_free(uc_es->addr_exp);
+ break;
+ case NFSE_CMD_EXPORT_DELETE:
+ free(uc_es->addr_exp, M_NFSE);
+ break;
+ }
+ free(uc_es, M_NFSE);
+ }
+ break;
+ case NFSE_CMD_EVENT:
+ uc_event = uc->kdata;
+ free(uc_event->path, M_NFSE);
+ break;
+ case NFSE_CMD_WEBNFS:
+ uc_webnfs = uc->kdata;
+ free(uc_webnfs->path, M_NFSE);
+ free(uc_webnfs->index, M_TEMP);
+ break;
+ }
+}
+
+/*
+ * Release list of user commands.
+ */
+static void
+nfse_ucmd_list_free(struct nfse_ucmd_list *uc_list)
+{
+ struct nfse_ucmd *uc, *uc_next;
+
+ STAILQ_FOREACH_SAFE(uc, uc_list, link, uc_next) {
+ if (uc->kdata != NULL) {
+ nfse_ucmd_free(uc);
+ free(uc->kdata, M_NFSE);
+ }
+ free(uc, M_NFSE);
+ }
+}
+
+/*
+ * Create a new transaction.
+ */
+static struct nfse_tr *
+nfse_tr_new(const struct thread *td)
+{
+ const struct nfse_tr *tr0;
+ struct nfse_tr *tr;
+
+ tr = malloc(sizeof(*tr), M_NFSE, M_WAITOK);
+ tr->pid = td->td_proc->p_pid;
+ tr->uid = td->td_ucred->cr_uid;
+ tr->trid = 1;
+ tr->state = NFSE_TR_BUSY;
+ STAILQ_INIT(&tr->uc_list);
+
+ mtx_lock(&nfse_tr_mtx);
+ for (;;) {
+ LIST_FOREACH(tr0, &nfse_tr_list, link)
+ if (tr0->pid == tr->pid && tr0->uid == tr->uid &&
+ tr0->trid == tr->trid) {
+ tr->trid++;
+ break;
+ }
+ if (tr0 == NULL)
+ break;
+ }
+ LIST_INSERT_HEAD(&nfse_tr_list, tr, link);
+ mtx_unlock(&nfse_tr_mtx);
+
+ return (tr);
+}
+
+/*
+ * Release memory used by one transaction including memory used
+ * by user commands.
+ */
+static void
+nfse_tr_free(struct nfse_tr *tr)
+{
+ nfse_ucmd_list_free(&tr->uc_list);
+ free(tr, M_NFSE);
+}
+
+/*
+ * Release memory used by all transactions.
+ */
+static void
+nfse_tr_free_all(void)
+{
+ struct nfse_tr *tr, *tr_next;
+
+ LIST_FOREACH_SAFE(tr, &nfse_tr_list, link, tr_next)
+ nfse_tr_free(tr);
+}
+
+/*
+ * Callout function for transactions list.
+ */
+static void
+nfse_tr_timeout(void *arg __unused)
+{
+ struct nfse_tr *tr, *tr_next;
+
+ LIST_FOREACH_SAFE(tr, &nfse_tr_list, link, tr_next)
+ switch (tr->state) {
+ case NFSE_TR_ACTIVE:
+ tr->state = NFSE_TR_INACTIVE;
+ break;
+ case NFSE_TR_INACTIVE:
+ LIST_REMOVE(tr, link);
+ nfse_tr_free(tr);
+ break;
+ }
+ if (!LIST_EMPTY(&nfse_tr_list)) {
+ callout_reset(&nfse_tr_callout, NFSE_TR_TIMEOUT * hz,
+ nfse_tr_timeout, NULL);
+ }
+}
+
+/*
+ * Find transaction with the given ID and context, unlink it if the
+ * trci flag is set or mark it as busy.
+ */
+static struct nfse_tr *
+nfse_tr_find(const struct thread *td, const u_int trid, const int trci)
+{
+ struct nfse_tr *tr;
+ pid_t pid;
+ uid_t uid;
+
+ pid = td->td_proc->p_pid;
+ uid = td->td_ucred->cr_uid;
+ mtx_lock(&nfse_tr_mtx);
+ LIST_FOREACH(tr, &nfse_tr_list, link)
+ if (tr->pid == pid && tr->uid == uid && tr->trid == trid) {
+ if (tr->state == NFSE_TR_BUSY) {
+ tr = NULL;
+ break;
+ }
+ if (trci) {
+ LIST_REMOVE(tr, link);
+ if (LIST_EMPTY(&nfse_tr_list))
+ callout_stop(&nfse_tr_callout);
+ } else
+ tr->state = NFSE_TR_BUSY;
+ break;
+ }
+ mtx_unlock(&nfse_tr_mtx);
+
+ return (tr);
+}
+
+/*
+ * Reset a transaction activity.
+ */
+static void
+nfse_tr_reset(struct nfse_tr *tr)
+{
+ mtx_lock(&nfse_tr_mtx);
+ if (LIST_NEXT(LIST_FIRST(&nfse_tr_list), link) == NULL)
+ callout_reset(&nfse_tr_callout, NFSE_TR_TIMEOUT * hz,
+ nfse_tr_timeout, NULL);
+ tr->state = NFSE_TR_ACTIVE;
+ mtx_unlock(&nfse_tr_mtx);
+}
+
+/*
+ * Cancel transaction and release its memory.
+ */
+static void
+nfse_tr_cancel(struct nfse_tr *tr)
+{
+ mtx_lock(&nfse_tr_mtx);
+ LIST_REMOVE(tr, link);
+ if (LIST_EMPTY(&nfse_tr_list))
+ callout_stop(&nfse_tr_callout);
+ mtx_unlock(&nfse_tr_mtx);
+ nfse_tr_free(tr);
+}
+
+/*
+ * Get one export specification from NFSE_CMD_EXPORT command.
+ */
+static int
+nfse_cmd_export_spec_get(struct nfse_ucmd_export_spec **uc_es_p,
+ struct nfse_cmd_export_spec *cmd_es)
+{
+ struct nfse_ucmd_export_spec *uc_es;
+ struct nfse_addr_exp *ae;
+ struct nfse_cred_exp *ce;
+ struct nfse_secflav *sf;
+ size_t size;
+ u_int i, j, addrlen, maskbits;
+ int error;
+ uint32_t command;
+ sa_family_t family;
+
+ /* Check whether the given address family is supported. */
+ uc_es = *uc_es_p;
+ family = cmd_es->family;
+ switch (family) {
+ case AF_INET:
+#ifdef INET
+ size = sizeof(struct sockaddr_in);
+ addrlen = 4;
+ break;
+#else
+ free(uc_es, M_NFSE);
+ *uc_es_p = NULL;
+ return (0);
+#endif
+ case AF_INET6:
+#ifdef INET6
+ size = sizeof(struct sockaddr_in6);
+ addrlen = 16;
+ break;
+#else
+ free(uc_es, M_NFSE);
+ *uc_es_p = NULL;
+ return (0);
+#endif
+ case AF_UNSPEC:
+ size = 0;
+ addrlen = 0;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* Verify mask length. */
+ maskbits = uc_es->maskbits = cmd_es->maskbits;
+ if (maskbits != 0) {
+ if (maskbits > addrlen * 8)
+ return (EINVAL);
+ size *= 2;
+ }
+
+ /* Verify command and allocate needed data structures. */
+ ce = NULL;
+ sf = NULL;
+ command = uc_es->command = cmd_es->flags & NFSE_CMD_EXPORT_CMDMASK;
+ switch (command) {
+ case NFSE_CMD_EXPORT_ADD:
+ case NFSE_CMD_EXPORT_UPDATE:
+ ce = malloc(sizeof(*ce), M_NFSE, M_WAITOK);
+ sf = malloc(sizeof(*sf), M_NFSE, M_WAITOK);
+ /* FALLTHROUGH */
+ case NFSE_CMD_EXPORT_DELETE:
+ ae = malloc(NFSE_ADDR_EXP_SIZE(size), M_NFSE, M_WAITOK|M_ZERO);
+ break;
+ case NFSE_CMD_EXPORT_CLEAR:
+ return (0);
+ default:
+ return (EINVAL);
+ }
+
+ if (command != NFSE_CMD_EXPORT_DELETE) {
+ /* Copy in and verify credentials and security flavors. */
+ if (cmd_es->nsec == 0 || cmd_es->nsec > NFSE_NSECFLAV ||
+ cmd_es->ngids > NGROUPS) {
+ error = EINVAL;
+ goto failed;
+ }
+ ce->uid = cmd_es->uid;
+ if (cmd_es->ngids > 0) {
+ error = copyin(cmd_es->gids, ce->gids,
+ cmd_es->ngids * sizeof(*cmd_es->gids));
+ if (error != 0)
+ goto failed;
+ }
+ ce->ngids = cmd_es->ngids;
+ for (i = 0; i < ce->ngids; ++i)
+ for (j = i + 1; j < ce->ngids; ++j)
+ if (ce->gids[i] == ce->gids[j]) {
+ error = EINVAL;
+ goto failed;
+ }
+ error = copyin(cmd_es->sec, sf->sec,
+ cmd_es->nsec * sizeof(*cmd_es->sec));
+ if (error != 0)
+ goto failed;
+ sf->nsec = cmd_es->nsec;
+ for (i = 0; i < sf->nsec; ++i) {
+ switch (sf->sec[i]) {
+ case AUTH_SYS:
+ case RPCSEC_GSS_KRB5:
+ case RPCSEC_GSS_KRB5I:
+ case RPCSEC_GSS_KRB5P:
+ break;
+ default:
+ error = EINVAL;
+ goto failed;
+ }
+ for (j = i + 1; j < sf->nsec; ++j)
+ if (sf->sec[i] == sf->sec[j]) {
+ error = EINVAL;
+ goto failed;
+ }
+ }
+
+ /* Translate flags. */
+ if (cmd_es->flags & NFSE_CMD_EXPORT_RDONLY)
+ ae->flags |= NFSE_EXFLAG_RDONLY;
+ if (cmd_es->flags & NFSE_CMD_EXPORT_MAPALL)
+ ae->flags |= NFSE_EXFLAG_MAPALL;
+ if (cmd_es->flags & NFSE_CMD_EXPORT_DENY)
+ ae->flags |= NFSE_EXFLAG_DENY;
+ }
+
+ /*
+ * If this is not a default export, then copy in address
+ * and create mask.
+ */
+ if (addrlen != 0) {
+ uint8_t *mask;
+
+ mask = NULL;
+#ifdef INET
+ if (family == AF_INET) {
+ struct sockaddr_in *sa4;
+
+ sa4 = NFSE_ADDR_EXP_ADDR4(ae);
+ sa4->sin_len = sizeof(*sa4);
+ error = copyin(cmd_es->addr, &sa4->sin_addr.s_addr,
+ sizeof(sa4->sin_addr.s_addr));
+ if (error != 0)
+ goto failed;
+ if (maskbits != 0) {
+ sa4 = NFSE_ADDR_EXP_MASK4(ae);
+ sa4->sin_len = sizeof(*sa4);
+ mask = (uint8_t *)&sa4->sin_addr.s_addr;
+ }
+ }
+#endif
+#ifdef INET6
+ if (family == AF_INET6) {
+ struct sockaddr_in6 *sa6;
+
+ sa6 = NFSE_ADDR_EXP_ADDR6(ae);
+ sa6->sin6_len = sizeof(*sa6);
+ error = copyin(cmd_es->addr, &sa6->sin6_addr,
+ sizeof(sa6->sin6_addr));
+ if (error != 0)
+ goto failed;
+ if (maskbits != 0) {
+ sa6 = NFSE_ADDR_EXP_MASK6(ae);
+ sa6->sin6_len = sizeof(*sa6);
+ mask = (uint8_t *)&sa6->sin6_addr;
+ }
+ }
+#endif
+ if (mask != NULL) {
+ j = maskbits / 8;
+ for (i = 0; i < j; ++i)
+ mask[i] = UINT8_MAX;
+ maskbits %= 8;
+ if (maskbits != 0) {
+ mask[i] = ~((1 << (8 - maskbits)) - 1);
+ ++i;
+ }
+ for (; i < addrlen; ++i)
+ mask[i] = 0;
+ }
+ }
+
+ /* Try to find existent credentials and security flavors. */
+ if (command != NFSE_CMD_EXPORT_DELETE) {
+ struct nfse_cred_exp *ce0;
+ struct nfse_secflav *sf0;
+
+ mtx_lock(&nfse_export_mtx);
+ LIST_FOREACH(ce0, &nfse_cred_exp_list, link) {
+ if (ce->uid != ce0->uid || ce->ngids != ce0->ngids)
+ continue;
+ for (i = 0; i < ce->ngids; ++i)
+ if (ce->gids[i] != ce0->gids[i])
+ break;
+ if (i == ce->ngids) {
+ free(ce, M_NFSE);
+ ce = ce0;
+ ce->ref_count++;
+ break;
+ }
+ }
+ if (ce0 == NULL) {
+ LIST_INSERT_HEAD(&nfse_cred_exp_list, ce, link);
+ ce->ref_count = 1;
+ }
+ LIST_FOREACH(sf0, &nfse_secflav_list, link)
+ if (sf->nsec == sf0->nsec) {
+ for (i = 0; i < sf->nsec; ++i)
+ if (sf->sec[i] != sf0->sec[i])
+ break;
+ if (i == sf->nsec) {
+ free(sf, M_NFSE);
+ sf = sf0;
+ sf->ref_count++;
+ break;
+ }
+ }
+ if (sf0 == NULL) {
+ LIST_INSERT_HEAD(&nfse_secflav_list, sf, link);
+ sf->ref_count = 1;
+ }
+ mtx_unlock(&nfse_export_mtx);
+
+ ae->secflav = sf;
+ ae->cred_exp = ce;
+ }
+
+ /* Everything is ready can finish. */
+ uc_es->family = family;
+ uc_es->addr_exp = ae;
+ return (0);
+
+failed:
+ /* Release all allocated memory (it is not shared). */
+ free(ae, M_NFSE);
+ free(ce, M_NFSE);
+ free(sf, M_NFSE);
+ return (error);
+}
+
+/*
+ * Get one NFSE_CMD_EXPORT command.
+ */
+static int
+nfse_cmd_export_get(struct nfse_ucmd_list *uc_list, const struct nfse_cmd *cmd)
+{
+ struct nfse_cmd_export cmd_export;
+ struct nfse_ucmd_export *uc_export;
+ struct nfse_ucmd_export_spec *uc_es;
+ struct nfse_cmd_export_spec *cmd_es;
+ struct nfse_ucmd *uc;
+ u_int i;
+ int error;
+
+ if (cmd->size != sizeof(cmd_export))
+ return (EINVAL);
+
+ error = copyin(cmd->data, &cmd_export, sizeof(cmd_export));
+ if (error != 0)
+ return (error);
+
+ if (cmd_export.path != NULL) {
+ char *path;
+
+ /* A new file system settings. */
+ if (cmd_export.path_size == 0 ||
+ cmd_export.path_size > RPCMNT_PATHLEN + 1)
+ return (EINVAL);
+ path = malloc(cmd_export.path_size, M_NFSE, M_WAITOK);
+ error = copyin(cmd_export.path, path, cmd_export.path_size);
+ if (error != 0 || path[cmd_export.path_size - 1] != '\0') {
+ free(path, M_NFSE);
+ return (error != 0 ? error : EINVAL);
+ }
+ uc = malloc(sizeof(*uc), M_NFSE, M_WAITOK);
+ STAILQ_INSERT_TAIL(uc_list, uc, link);
+ uc->command = NFSE_CMD_EXPORT;
+ uc->udata = cmd->data;
+ uc_export = malloc(sizeof(*uc_export), M_NFSE, M_WAITOK);
+ uc->kdata = uc_export;
+ uc_export->path = path;
+ STAILQ_INIT(&uc_export->es_list);
+ if (cmd_export.status & NFSE_CMD_EXPORT_EXPORTED) {
+ uc_export->fsid = cmd_export.fsid;
+ uc_export->fs_exp = NULL;
+ } else {
+ uc_export->fs_exp = nfse_fs_exp_new();
+ if (uc_export->fs_exp == NULL)
+ return (ENOMEM);
+ }
+ } else {
+ /* Continue to work with the last file system. */
+ uc = STAILQ_LAST(uc_list, nfse_ucmd, link);
+ if (uc == NULL || uc->command != NFSE_CMD_EXPORT)
+ return (EINVAL);
+ uc_export = uc->kdata;
+ }
+
+ /* Read one-by-one all export specifications. */
+ cmd_es = malloc(sizeof(*cmd_es), M_NFSE, M_WAITOK);
+ error = 0;
+ for (i = 0; i < cmd_export.nspec; ++i) {
+ error = copyin(&cmd_export.spec[i], cmd_es, sizeof(*cmd_es));
+ if (error != 0)
+ break;
+ uc_es = malloc(sizeof(*uc_es), M_NFSE, M_WAITOK);
+ error = nfse_cmd_export_spec_get(&uc_es, cmd_es);
+ if (error != 0) {
+ free(uc_es, M_NFSE);
+ break;
+ }
+ if (uc_es != NULL)
+ STAILQ_INSERT_TAIL(&uc_export->es_list, uc_es, link);
+ }
+ free(cmd_es, M_NFSE);
+
+ return (error);
+}
+
+/*
+ * Get one NFSE_CMD_CLEAR command.
+ */
+static int
+nfse_cmd_clear_get(const struct nfse_ucmd_list *uc_list,
+ struct nfse_ucmd *uc, const struct nfse_cmd *cmd)
+{
+ /* This command accepts no data and must be the first command. */
+ if (cmd->size != 0 || cmd->data != NULL || STAILQ_FIRST(uc_list) != uc)
+ return (EINVAL);
+ return (0);
+}
+
+/*
+ * Get one NFSE_CMD_EVENT command.
+ */
+static int
+nfse_cmd_event_get(struct nfse_ucmd *uc, const struct nfse_cmd *cmd)
+{
+ struct nfse_cmd_event cmd_event;
+ struct nfse_ucmd_event *uc_event;
+ int error;
+
+ if (cmd->size != sizeof(cmd_event))
+ return (EINVAL);
+ error = copyin(cmd->data, &cmd_event, sizeof(cmd_event));
+ if (error != 0)
+ return (error);
+ if (cmd_event.path_size == 0 ||
+ cmd_event.path_size > RPCMNT_PATHLEN + 1)
+ return (EINVAL);
+ uc_event = malloc(sizeof(*uc_event), M_NFSE, M_WAITOK);
+ uc->kdata = uc_event;
+ uc_event->path = malloc(cmd_event.path_size, M_NFSE, M_WAITOK);
+ error = copyin(cmd_event.path, uc_event->path, cmd_event.path_size);
+ if (error != 0)
+ return (error);
+ if (uc_event->path[cmd_event.path_size - 1] != '\0')
+ return (EINVAL);
+ uc_event->fsid = cmd_event.fsid;
+ uc_event->status = cmd_event.status;
+ return (0);
+}
+
+/*
+ * Get one NFSE_CMD_WEBNFS command.
+ */
+static int
+nfse_cmd_webnfs_get(struct nfse_ucmd *uc, const struct nfse_cmd *cmd)
+{
+ struct nfse_cmd_webnfs cmd_webnfs;
+ struct nfse_ucmd_webnfs *uc_webnfs;
+ int error;
+
+ if (cmd->size != sizeof(cmd_webnfs))
+ return (EINVAL);
+ error = copyin(cmd->data, &cmd_webnfs, sizeof(cmd_webnfs));
+ if (error != 0)
+ return (error);
+ if (cmd_webnfs.path_size > RPCMNT_PATHLEN + 1 ||
+ cmd_webnfs.index_size > PATH_MAX)
+ return (EINVAL);
+ if ((cmd_webnfs.path != NULL && cmd_webnfs.path_size == 0) ||
+ (cmd_webnfs.index != NULL && cmd_webnfs.index_size == 0))
+ return (EINVAL);
+ uc_webnfs = malloc(sizeof(*uc_webnfs), M_NFSE, M_WAITOK);
+ uc->kdata = uc_webnfs;
+ if (cmd_webnfs.path == NULL) {
+ uc_webnfs->path = uc_webnfs->index = NULL;
+ return (0);
+ }
+ uc_webnfs->path = malloc(cmd_webnfs.path_size, M_NFSE, M_WAITOK);
+ uc_webnfs->index = cmd_webnfs.index != NULL ?
+ malloc(cmd_webnfs.index_size, M_TEMP, M_WAITOK) : NULL;
+ error = copyin(cmd_webnfs.path, uc_webnfs->path,
+ cmd_webnfs.path_size);
+ if (error != 0)
+ return (error);
+ if (uc_webnfs->path[cmd_webnfs.path_size - 1] != '\0')
+ return (EINVAL);
+ if (uc_webnfs->index != NULL) {
+ error = copyin(cmd_webnfs.index, uc_webnfs->index,
+ cmd_webnfs.index_size);
+ if (error != 0)
+ return (error);
+ if (uc_webnfs->index[cmd_webnfs.index_size - 1] != '\0')
+ return (EINVAL);
+ error = strchr(uc_webnfs->index, '/') != NULL;
+ if (error == 0 && uc_webnfs->index[0] == '.')
+ switch (uc_webnfs->index[1]) {
+ case '\0':
+ error = 1;
+ break;
+ case '.':
+ if (uc_webnfs->index[2] == '\0')
+ error = 1;
+ break;
+ }
+ if (error != 0)
+ return (EINVAL);
+ }
+ if (cmd_webnfs.fid.fid_len <= offsetof(struct fid, fid_data) ||
+ cmd_webnfs.fid.fid_len > sizeof(cmd_webnfs.fid))
+ return (EINVAL);
+ uc_webnfs->fsid = cmd_webnfs.fsid;
+ uc_webnfs->fid = cmd_webnfs.fid;
+ return (0);
+}
+
+/*
+ * Update export specification. A user application must verify that
+ * all export specification updates can be applied to the configuration
+ * it manages. If some command cannot be applied to some file system,
+ * then deny access to this file system completely, since a user application
+ * already incorrectly manages own settings.
+ */
+static int
+nfse_cmd_export_run(struct nfse_ucmd_export *uc_export)
+{
+ const char *path;
+ struct nfse_fs_exp *fe;
+ struct nfse_ucmd_export_spec *uc_es;
+ struct nfse_addr_exp *ae;
+
+#if defined(INET) || defined(INET6)
+ struct radix_node_head *rnh;
+ void *addr, *mask;
+#endif
+
+ /* Get exported or new nfse_fs_exp structure. */
+ path = uc_export->path;
+ LIST_FOREACH(fe, &nfse_fs_exp_list, link)
+ if (strcmp(fe->mp->mnt_stat.f_mntonname, path) == 0)
+ break;
+ if (uc_export->fs_exp == NULL) {
+ /* Update for exported file system. */
+ if (fe == NULL) {
+ /* File system is not exported any more. */
+ uc_export->status = 0;
+ return (0);
+ }
+ if (uc_export->fsid.val[0] != fe->mp->mnt_stat.f_fsid.val[0] ||
+ uc_export->fsid.val[1] != fe->mp->mnt_stat.f_fsid.val[1]) {
+ /* File system is exported, but its ID is different. */
+ uc_export->status = NFSE_CMD_EXPORT_WRONG_ID |
+ NFSE_CMD_EXPORT_MOUNTED | NFSE_CMD_EXPORT_EXPORTED;
+ return (0);
+ }
+ } else {
+ struct mount *mp;
+
+ /* New file system to be exported. */
+ if (fe != NULL) {
+ /* File system is already exported. */
+ uc_export->status = NFSE_CMD_EXPORT_WRONG_ID;
+ return (0);
+ }
+ /*
+ * Since a new file system is linked to the tail of the
+ * mount list, let's do reverse search to skip all matched
+ * but covered file systems.
+ */
+ mtx_lock(&mountlist_mtx);
+ TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list)
+ if (strcmp(mp->mnt_stat.f_mntonname, path) == 0)
+ break;
+ mtx_unlock(&mountlist_mtx);
+ if (mp == NULL) {
+ /* File system is not mounted. */
+ uc_export->status = 0;
+ return (0);
+ }
+ fe = uc_export->fs_exp;
+ fe->mp = mp;
+ uc_export->fsid = mp->mnt_stat.f_fsid;
+ uc_export->fs_exp = NULL;
+ LIST_INSERT_HEAD(&nfse_fs_exp_list, fe, link);
+ }
+
+ /* Apply commands one-by-one. */
+ STAILQ_FOREACH(uc_es, &uc_export->es_list, link) {
+ if (uc_es->command == NFSE_CMD_EXPORT_CLEAR) {
+ if (STAILQ_NEXT(uc_es, link) == NULL) {
+ LIST_REMOVE(fe, link);
+ nfse_fs_exp_free(fe, 0);
+ uc_export->status = 0;
+ return (0);
+ } else
+ nfse_fs_exp_free(fe, 1);
+ continue;
+ }
+#ifdef INET
+ if (uc_es->family == AF_INET) {
+ rnh = fe->ae4_rnh;
+ addr = NFSE_ADDR_EXP_ADDR4(uc_es->addr_exp);
+ mask = uc_es->maskbits != 0 ?
+ NFSE_ADDR_EXP_MASK4(uc_es->addr_exp) : NULL;
+ } else
+#endif
+#ifdef INET6
+ if (uc_es->family == AF_INET6) {
+ rnh = fe->ae6_rnh;
+ addr = NFSE_ADDR_EXP_ADDR6(uc_es->addr_exp);
+ mask = uc_es->maskbits != 0 ?
+ NFSE_ADDR_EXP_MASK6(uc_es->addr_exp) : NULL;
+ } else
+#endif
+ {
+ ae = uc_es->addr_exp;
+ switch (uc_es->command) {
+ case NFSE_CMD_EXPORT_ADD:
+ if (fe->ae_def != NULL)
+ goto failed;
+ fe->ae_def = ae;
+ uc_es->addr_exp = NULL;
+ break;
+ case NFSE_CMD_EXPORT_UPDATE:
+ if (fe->ae_def == NULL)
+ goto failed;
+ fe->ae_def->flags = ae->flags;
+ fe->ae_def->secflav = ae->secflav;
+ fe->ae_def->cred_exp = ae->cred_exp;
+ ae->secflav->ref_count++;
+ ae->cred_exp->ref_count++;
+ break;
+ case NFSE_CMD_EXPORT_DELETE:
+ if (fe->ae_def == NULL)
+ goto failed;
+ nfse_addr_exp_free(fe->ae_def);
+ fe->ae_def = NULL;
+ break;
+ }
+ continue;
+ }
+#if defined(INET) || defined(INET6)
+ switch (uc_es->command) {
+ case NFSE_CMD_EXPORT_ADD:
+ if (rnh->rnh_addaddr(addr, mask, rnh,
+ (struct radix_node *)uc_es->addr_exp) == NULL)
+ goto failed;
+ uc_es->addr_exp = NULL;
+ break;
+ case NFSE_CMD_EXPORT_UPDATE:
+ ae = (struct nfse_addr_exp *)
+ rnh->rnh_lookup(addr, mask, rnh);
+ if (ae == NULL)
+ goto failed;
+ ae->flags = uc_es->addr_exp->flags;
+ ae->secflav = uc_es->addr_exp->secflav;
+ ae->cred_exp = uc_es->addr_exp->cred_exp;
+ ae->secflav->ref_count++;
+ ae->cred_exp->ref_count++;
+ break;
+ case NFSE_CMD_EXPORT_DELETE:
+ ae = (struct nfse_addr_exp *)
+ rnh->rnh_deladdr(addr, mask, rnh);
+ if (ae == NULL)
+ goto failed;
+ nfse_addr_exp_free(ae);
+ break;
+ }
+#endif
+ }
+
+ uc_export->status = NFSE_CMD_EXPORT_MOUNTED | NFSE_CMD_EXPORT_EXPORTED;
+ return (0);
+
+failed:
+ LIST_REMOVE(fe, link);
+ nfse_fs_exp_free(fe, 0);
+ return (EPERM);
+}
+
+/*
+ * Clear all export settings.
+ */
+static void
+nfse_cmd_clear_run(void)
+{
+ struct nfse_fs_exp *fe, *fe_next;
+
+ LIST_FOREACH_SAFE(fe, &nfse_fs_exp_list, link, fe_next) {
+ LIST_REMOVE(fe, link);
+ nfse_fs_exp_free(fe, 0);
+ }
+ nfse_webnfs_free();
+}
+
+/*
+ * Verify VFS event.
+ */
+static void
+nfse_cmd_event_run(struct nfse_ucmd_event *uc_event)
+{
+ const struct nfse_fs_exp *fe;
+ const struct mount *mp;
+ const char *path;
+
+ /*
+ * Try to find file system in the exported list. If it is present
+ * in the exported list, then it is mounted.
+ */
+ path = uc_event->path;
+ LIST_FOREACH(fe, &nfse_fs_exp_list, link)
+ if (strcmp(fe->mp->mnt_stat.f_mntonname, path) == 0)
+ break;
+ if (fe != NULL) {
+ /* File system is exported. */
+ if ((uc_event->status & NFSE_CMD_EVENT_EXPORTED) &&
+ uc_event->fsid.val[0] == fe->mp->mnt_stat.f_fsid.val[0] &&
+ uc_event->fsid.val[1] == fe->mp->mnt_stat.f_fsid.val[1]) {
+ uc_event->status =
+ NFSE_CMD_EVENT_MOUNTED | NFSE_CMD_EVENT_EXPORTED;
+ } else {
+ /*
+ * File system's ID is different or it is not
+ * exported by this process.
+ */
+ uc_event->status = NFSE_CMD_EXPORT_WRONG_ID |
+ NFSE_CMD_EVENT_MOUNTED | NFSE_CMD_EVENT_EXPORTED;
+ }
+ } else {
+ /*
+ * Try to find file system in the mount list. Since a new
+ * file system is linked to the tail of the mount list, let's
+ * do reverse search to speed up finding of just mounted
+ * file systems.
+ */
+ uc_event->status = 0;
+ mtx_lock(&mountlist_mtx);
+ TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list)
+ if (strcmp(mp->mnt_stat.f_mntonname, path) == 0) {
+ uc_event->status = NFSE_CMD_EVENT_MOUNTED;
+ break;
+ }
+ mtx_unlock(&mountlist_mtx);
+ }
+}
+
+/*
+ * Apply WebNFS settings.
+ */
+static void
+nfse_cmd_webnfs_run(struct nfse_ucmd_webnfs *uc_webnfs)
+{
+ const struct nfse_fs_exp *fe;
+ const char *path;
+
+ nfse_webnfs_free();
+
+ path = uc_webnfs->path;
+ if (path == NULL)
+ return;
+
+ LIST_FOREACH(fe, &nfse_fs_exp_list, link)
+ if (strcmp(fe->mp->mnt_stat.f_mntonname, path) == 0)
+ break;
+ if (fe == NULL) {
+ /* File system is not exported. */
+ uc_webnfs->status = 0;
+ return;
+ }
+ if (uc_webnfs->fsid.val[0] != fe->mp->mnt_stat.f_fsid.val[0] ||
+ uc_webnfs->fsid.val[1] != fe->mp->mnt_stat.f_fsid.val[1]) {
+ /* File system is exported, but file system ID is different. */
+ uc_webnfs->status = NFSE_CMD_WEBNFS_EXPORTED |
+ NFSE_CMD_WEBNFS_WRONG_ID;
+ return;
+ }
+
+ /* Apply new WebNFS settings. */
+ nfs_pub.np_handle.fh_fsid = uc_webnfs->fsid;
+ nfs_pub.np_handle.fh_fid = uc_webnfs->fid;
+ nfs_pub.np_mount = fe->mp;
+ nfs_pub.np_index = uc_webnfs->index;
+ nfs_pub.np_valid = 1;
+
+ uc_webnfs->index = NULL;
+ uc_webnfs->status = NFSE_CMD_WEBNFS_EXPORTED;
+}
+
+/*
+ * Send result for the given succeeded command.
+ */
+static int
+nfse_cmd_succeeded(const struct nfse_ucmd *uc)
+{
+ uint32_t *uaddr;
+ int error;
+ uint32_t status;
+
+ switch (uc->command) {
+ case NFSE_CMD_EXPORT:
+ error = copyout(&((struct nfse_ucmd_export *)uc->kdata)->fsid,
+ &((struct nfse_cmd_export *)uc->udata)->fsid,
+ sizeof(((struct nfse_ucmd_export *)uc->kdata)->fsid));
+ if (error != 0)
+ return (error);
+ status = ((struct nfse_ucmd_export *)uc->kdata)->status;
+ uaddr = &((struct nfse_cmd_export *)uc->udata)->status;
+ break;
+ case NFSE_CMD_EVENT:
+ status = ((struct nfse_ucmd_event *)uc->kdata)->status;
+ uaddr = &((struct nfse_cmd_event *)uc->udata)->status;
+ break;
+ case NFSE_CMD_WEBNFS:
+ status = ((struct nfse_ucmd_webnfs *)uc->kdata)->status;
+ uaddr = &((struct nfse_cmd_webnfs *)uc->udata)->status;
+ break;
+ default:
+ return (0);
+ }
+ return (copyout(&status, uaddr, sizeof(status)));
+}
+
+/*
+ * Get all user commands and run them.
+ */
+int
+nfse_cmds(struct thread *td, struct nfse_cmds_hdr *uap)
+{
+ struct nfse_cmds_hdr hdr;
+ struct nfse_cmd cmd;
+ struct nfse_ucmd_list uc_list_s;
+ struct nfse_ucmd_list *uc_list;
+ struct nfse_ucmd *uc;
+ struct nfse_tr *tr;
+ u_int i;
+ int error, trci;
+
+ error = copyin(uap, &hdr, sizeof(hdr));
+ if (error != 0)
+ return (error);
+ if (hdr.version != NFSE_API_VERSION)
+ return (EINVAL);
+ trci = (hdr.flags & NFSE_HDR_TR_COMMIT) ? 1 : 0;
+ if (hdr.flags & NFSE_HDR_TR_START) {
+ if (hdr.trid != 0)
+ return (EINVAL);
+ if (trci) {
+ tr = NULL;
+ uc_list = &uc_list_s;
+ STAILQ_INIT(uc_list);
+ } else {
+ tr = nfse_tr_new(td);
+ uc_list = &tr->uc_list;
+ }
+ } else {
+ if (hdr.trid == 0)
+ return (EINVAL);
+ tr = nfse_tr_find(td, hdr.trid, trci);
+ if (tr == NULL)
+ return (ESRCH);
+ uc_list = &tr->uc_list;
+ }
+
+ error = 0;
+ for (i = 0; i < hdr.ncmds; ++i) {
+ error = copyin(&hdr.cmds[i], &cmd, sizeof(cmd));
+ if (error != 0)
+ return (error);
+ if (cmd.command != NFSE_CMD_EXPORT) {
+ uc = malloc(sizeof(*uc), M_NFSE, M_WAITOK);
+ STAILQ_INSERT_TAIL(uc_list, uc, link);
+ uc->command = cmd.command;
+ uc->udata = cmd.data;
+ uc->kdata = NULL;
+ }
+ switch (cmd.command) {
+ case NFSE_CMD_EXPORT:
+ error = nfse_cmd_export_get(uc_list, &cmd);
+ break;
+ case NFSE_CMD_CLEAR:
+ error = nfse_cmd_clear_get(uc_list, uc, &cmd);
+ break;
+ case NFSE_CMD_EVENT:
+ error = nfse_cmd_event_get(uc, &cmd);
+ break;
+ case NFSE_CMD_WEBNFS:
+ error = nfse_cmd_webnfs_get(uc, &cmd);
+ break;
+ default:
+ error = EINVAL;
+ }
+ if (error != 0)
+ break;
+ }
+
+ if (trci) {
+ if (error == 0) {
+ void *data;
+
+ NFSE_EXPORT_WLOCK();
+ STAILQ_FOREACH(uc, uc_list, link) {
+ data = uc->kdata;
+ switch (uc->command) {
+ case NFSE_CMD_EXPORT:
+ error = nfse_cmd_export_run(data);
+ break;
+ case NFSE_CMD_CLEAR:
+ nfse_cmd_clear_run();
+ break;
+ case NFSE_CMD_EVENT:
+ nfse_cmd_event_run(data);
+ break;
+ case NFSE_CMD_WEBNFS:
+ nfse_cmd_webnfs_run(data);
+ break;
+ }
+ if (error != 0)
+ break;
+ }
+ NFSE_EXPORT_WUNLOCK();
+ if (error == 0)
+ STAILQ_FOREACH(uc, uc_list, link) {
+ error = nfse_cmd_succeeded(uc);
+ if (error != 0)
+ break;
+ }
+ }
+ if (tr != NULL)
+ nfse_tr_free(tr);
+ else
+ nfse_ucmd_list_free(uc_list);
+ } else {
+ if (error == 0 && (hdr.flags & NFSE_HDR_TR_START))
+ error = copyout(&tr->trid, &uap->trid,
+ sizeof(tr->trid));
+ if (error != 0)
+ nfse_tr_cancel(tr);
+ else
+ nfse_tr_reset(tr);
+ }
+
+ return (error);
+}
+
+/*
+ * Check whether a host with the given address is allowed to
+ * access a file with the given handle.
+ */
+int
+nfse_check(const fhandle_t *fhp, struct sockaddr *sa, struct mount **mpp,
+ struct ucred *ucred, int *rdonlyp)
+{
+ const struct nfse_fs_exp *fe;
+ const struct nfse_addr_exp *ae;
+ int rv;
+
+ NFSE_EXPORT_RLOCK();
+ LIST_FOREACH(fe, &nfse_fs_exp_list, link)
+ if (fe->mp->mnt_stat.f_fsid.val[0] == fhp->fh_fsid.val[0] &&
+ fe->mp->mnt_stat.f_fsid.val[1] == fhp->fh_fsid.val[1])
+ break;
+ if (fe != NULL) {
+ if (sa == NULL) {
+ ae = fe->ae_def;
+ } else
+#ifdef INET
+ if (sa->sa_family == AF_INET) {
+ ae = (struct nfse_addr_exp *)
+ fe->ae4_rnh->rnh_matchaddr(sa, fe->ae4_rnh);
+ if (ae == NULL ||
+ (ae->rnodes[0].rn_flags & RNF_ROOT))
+ ae = fe->ae_def;
+ } else
+#endif
+#ifdef INET6
+ if (sa->sa_family == AF_INET6) {
+ ae = (struct nfse_addr_exp *)
+ fe->ae6_rnh->rnh_matchaddr(sa, fe->ae6_rnh);
+ if (ae == NULL ||
+ (ae->rnodes[0].rn_flags & RNF_ROOT))
+ ae = fe->ae_def;
+ } else
+#endif
+ {
+ ae = NULL;
+ }
+ if (ae != NULL && !(ae->flags & NFSE_EXFLAG_DENY)) {
+ if (ucred->cr_uid == 0 ||
+ (ae->flags & NFSE_EXFLAG_MAPALL)) {
+ const struct nfse_cred_exp *ce;
+ u_int i;
+
+ ce = ae->cred_exp;
+ ucred->cr_uid = ce->uid;
+ for (i = 0; i < ce->ngids; ++i)
+ ucred->cr_groups[i] = ce->gids[i];
+ ucred->cr_ngroups = ce->ngids;
+ }
+ vfs_ref(fe->mp);
+ *mpp = fe->mp;
+ *rdonlyp = ae->flags & NFSE_EXFLAG_RDONLY;
+ rv = 0;
+ } else
+ rv = EACCES;
+ } else
+ rv = ESTALE;
+ NFSE_EXPORT_RUNLOCK();
+ return (rv);
+}
+
+/*
+ * Handle VFS mount event: if a file system that is being mounted
+ * covers exported file system, then forget about this export since
+ * such behaviour is logical from a user process point of view.
+ */
+static void
+nfse_event_mount(void *arg1 __unused, void *arg2)
+{
+ const struct mount *mp;
+ struct nfse_fs_exp *fe, *fe_next;
+
+ mp = arg2;
+ NFSE_EXPORT_WLOCK();
+ LIST_FOREACH_SAFE(fe, &nfse_fs_exp_list, link, fe_next)
+ if (strcmp(fe->mp->mnt_stat.f_mntonname,
+ mp->mnt_stat.f_mntonname) == 0) {
+ LIST_REMOVE(fe, link);
+ break;
+ }
+ NFSE_EXPORT_WUNLOCK();
+ if (fe != NULL)
+ nfse_fs_exp_free(fe, 0);
+}
+
+/*
+ * Handle VFS unmount event: if a file system that is being unmounted
+ * is exported, then forget about this export.
+ */
+static void
+nfse_event_unmount(void *arg1 __unused, void *arg2)
+{
+ struct nfse_fs_exp *fe, *fe_next;
+
+ NFSE_EXPORT_WLOCK();
+ LIST_FOREACH_SAFE(fe, &nfse_fs_exp_list, link, fe_next)
+ if (fe->mp == arg2) {
+ LIST_REMOVE(fe, link);
+ break;
+ }
+ NFSE_EXPORT_WUNLOCK();
+ if (fe != NULL)
+ nfse_fs_exp_free(fe, 0);
+}
+
+/*
+ * Initialize NFS export code.
+ */
+void
+nfse_init(void)
+{
+ rw_init(&nfse_export_rwlock, "NFS export list");
+ mtx_init(&nfse_export_mtx, "NFS export data", NULL, MTX_DEF);
+ mtx_init(&nfse_tr_mtx, "NFS export transaction", NULL, MTX_DEF);
+ callout_init_mtx(&nfse_tr_callout, &nfse_tr_mtx, 0);
+ nfse_mount_event_tag = EVENTHANDLER_REGISTER(vfs_mount_event,
+ nfse_event_mount, NULL, EVENTHANDLER_PRI_FIRST);
+ nfse_unmount_event_tag = EVENTHANDLER_REGISTER(vfs_unmount_event,
+ nfse_event_unmount, NULL, EVENTHANDLER_PRI_FIRST);
+}
+
+/*
+ * Deinitialize NFS export code.
+ */
+void
+nfse_deinit(void)
+{
+ EVENTHANDLER_DEREGISTER(vfs_unmount_event, nfse_mount_event_tag);
+ EVENTHANDLER_DEREGISTER(vfs_unmount_event, nfse_unmount_event_tag);
+ callout_drain(&nfse_tr_callout);
+ mtx_destroy(&nfse_tr_mtx);
+ mtx_destroy(&nfse_export_mtx);
+ rw_destroy(&nfse_export_rwlock);
+ nfse_tr_free_all();
+ nfse_cmd_clear_run();
+}
diff -ruN nfsserver.orig/nfs_export.h nfsserver/nfs_export.h
--- nfsserver.orig/nfs_export.h 1970-01-01 03:00:00.000000000 +0300
+++ nfsserver/nfs_export.h 2009-06-06 13:44:45.000000000 +0300
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2009 Andrey Simonenko
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD:$
+ */
+
+#ifndef _NFSSERVER_NFS_EXPORT_H_
+#define _NFSSERVER_NFS_EXPORT_H_
+
+#define NFSE_API_VERSION 1 /* API version. */
+
+#define NFSE_NSECFLAV 5 /* Number of security flavors. */
+
+/*
+ * Flags for the nfse_cmds_hdr structure.
+ */
+#define NFSE_HDR_TR_START 0x0001 /* Start a new transaction. */
+#define NFSE_HDR_TR_COMMIT 0x0002 /* Commit transaction. */
+
+/*
+ * Argument for nfssvc(2) (do not change it).
+ */
+struct nfse_cmds_hdr {
+ u_int version; /* NFSE_API_VERSION */
+ uint32_t flags; /* Header flags. */
+ u_int trid; /* Transaction ID. */
+ u_int ncmds; /* Number of commands. */
+ struct nfse_cmd *cmds; /* Array of commands. */
+};
+
+/*
+ * Command code for the nfse_cmd structure.
+ */
+#define NFSE_CMD_EXPORT 1 /* Update current export spec. */
+#define NFSE_CMD_CLEAR 2 /* Clear all export spec. */
+#define NFSE_CMD_EVENT 3 /* Check (sync) VFS events. */
+#define NFSE_CMD_WEBNFS 4 /* Update WebNFS settings. */
+
+/*
+ * Command descriptor.
+ */
+struct nfse_cmd {
+ u_int command; /* Command code. */
+ size_t size; /* Size of command's data. */
+ void *data; /* Pointer to command's data. */
+};
+
+/*
+ * The NFSE_CMD_EXPORT command modifies export specifications for
+ * a file system.
+ */
+
+/*
+ * Status flags for the nfse_cmd_export structure.
+ */
+#define NFSE_CMD_EXPORT_MOUNTED 0x0001 /* File system is mounted. */
+#define NFSE_CMD_EXPORT_EXPORTED 0x0002 /* File system is exported. */
+#define NFSE_CMD_EXPORT_WRONG_ID 0x0004 /* File system has another ID. */
+
+/*
+ * NFSE_CMD_EXPORT command descriptor.
+ */
+struct nfse_cmd_export {
+ const char *path; /* Mount point. */
+ size_t path_size; /* Size of path name. */
+ fsid_t fsid; /* File system ID. */
+ uint32_t status; /* Status flags. */
+ u_int nspec; /* Number of export spec. */
+ const struct nfse_cmd_export_spec *spec; /* Export spec. array. */
+};
+
+/*
+ * Command codes and flags for the nfse_cmd_export_spec structure.
+ */
+#define NFSE_CMD_EXPORT_ADD 0x0001 /* Add a new export spec. */
+#define NFSE_CMD_EXPORT_UPDATE 0x0002 /* Update existent export spec. */
+#define NFSE_CMD_EXPORT_DELETE 0x0003 /* Delete existent export spec. */
+#define NFSE_CMD_EXPORT_CLEAR 0x0004 /* Clear all export spec. */
+#define NFSE_CMD_EXPORT_RDONLY 0x0010 /* Read-only access (f). */
+#define NFSE_CMD_EXPORT_MAPALL 0x0020 /* Change cred. for everyone (f). */
+#define NFSE_CMD_EXPORT_DENY 0x0040 /* Deny access (f). */
+
+#define NFSE_CMD_EXPORT_CMDMASK 0x000f /* Mask for command. */
+
+/*
+ * Export specification descriptor.
+ */
+struct nfse_cmd_export_spec {
+ uint32_t flags; /* Command and flags. */
+ uid_t uid; /* Credentials UID. */
+ u_int ngids; /* Number of credentials GIDs. */
+ const gid_t *gids; /* Credentials GIDs. */
+ u_int nsec; /* Number of security flavors. */
+ const int *sec; /* Pointer to security flavors. */
+ sa_family_t family; /* Address family. */
+ u_char maskbits; /* Number of bits in mask. */
+ const uint8_t *addr; /* Pointer to address. */
+};
+
+/*
+ * The NFSE_CMD_CLEAR command clears all export specifications and
+ * it does not require additional data.
+ */
+
+/*
+ * The NFSE_CMD_EVENT command synchronizes userland and kernel vision of
+ * exported file system.
+ */
+
+/*
+ * Status flags for the nfse_cmd_export structure.
+ */
+#define NFSE_CMD_EVENT_MOUNTED 0x0001 /* File system is mounted. */
+#define NFSE_CMD_EVENT_EXPORTED 0x0002 /* File system is exported. */
+#define NFSE_CMD_EVENT_WRONG_ID 0x0004 /* File system has another ID. */
+
+/*
+ * NFSE_CMD_EVENT command descriptor.
+ */
+struct nfse_cmd_event {
+ const char *path; /* Mount point. */
+ size_t path_size; /* Size of path name. */
+ fsid_t fsid; /* File system ID. */
+ uint32_t status; /* Status flags. */
+};
+
+/*
+ * The NFSE_CMD_WEBNFS command changes WebNFS settings.
+ */
+
+/*
+ * Status flags for the nfse_cmd_webnfs structure.
+ */
+#define NFSE_CMD_WEBNFS_EXPORTED 0x0001 /* File system is exported. */
+#define NFSE_CMD_WEBNFS_WRONG_ID 0x0004 /* File system has another ID. */
+
+/*
+ * NFSE_CMD_WEBNFS command descriptor.
+ */
+struct nfse_cmd_webnfs {
+ const char *path; /* Mount point. */
+ const char *index; /* WebNFS index file. */
+ size_t path_size; /* Size of path name. */
+ size_t index_size; /* Size of index name. */
+ fsid_t fsid; /* File system ID. */
+ struct fid fid; /* File ID of root vnode. */
+ uint32_t status; /* Status flags. */
+};
+
+#ifdef _KERNEL
+
+extern int nfse_api_used;
+
+extern int nfse_cmds(struct thread *, struct nfse_cmds_hdr *);
+extern int nfse_check(const fhandle_t *, struct sockaddr *,
+ struct mount **, struct ucred *, int *);
+
+extern void nfse_init(void);
+extern void nfse_deinit(void);
+
+#endif /* _KERNEL */
+
+#endif /* !_NFSSERVER_NFS_EXPORT_H_ */
diff -ruN nfsserver.orig/nfs_srvsubs.c nfsserver/nfs_srvsubs.c
--- nfsserver.orig/nfs_srvsubs.c 2008-02-11 12:46:20.000000000 +0200
+++ nfsserver/nfs_srvsubs.c 2009-05-26 20:10:17.000000000 +0300
@@ -71,6 +71,7 @@
#include <nfs/nfsproto.h>
#include <nfsserver/nfs.h>
#include <nfs/xdr_subs.h>
+#include <nfsserver/nfs_export.h>
#include <nfsserver/nfsm_subs.h>
#include <netinet/in.h>
@@ -551,6 +552,7 @@
callout_init(&nfsrv_callout, CALLOUT_MPSAFE);
NFSD_UNLOCK();
nfsrv_timer(0);
+ nfse_init();
error = syscall_register(&nfssvc_offset, &nfssvc_sysent,
&nfssvc_prev_sysent);
@@ -570,6 +572,7 @@
callout_drain(&nfsrv_callout);
nfsrv_destroycache(); /* Free the server request cache */
mtx_destroy(&nfsd_mtx);
+ nfse_deinit();
break;
default:
error = EOPNOTSUPP;
@@ -1101,13 +1104,20 @@
fhp = &nfs_pub.np_handle;
}
- mp = vfs_getvfs(&fhp->fh_fsid);
- if (!mp)
- return (ESTALE);
- vfslocked = VFS_LOCK_GIANT(mp);
- error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
- if (error)
- goto out;
+ if (nfse_api_used) {
+ error = nfse_check(fhp, nam, &mp, cred, rdonlyp);
+ if (error != 0)
+ return (error);
+ vfslocked = VFS_LOCK_GIANT(mp);
+ } else {
+ mp = vfs_getvfs(&fhp->fh_fsid);
+ if (!mp)
+ return (ESTALE);
+ vfslocked = VFS_LOCK_GIANT(mp);
+ error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
+ if (error)
+ goto out;
+ }
error = VFS_FHTOVP(mp, &fhp->fh_fid, vpp);
if (error)
goto out;
@@ -1124,19 +1134,21 @@
}
}
#endif
- /*
- * Check/setup credentials.
- */
- if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
- cred->cr_uid = credanon->cr_uid;
- for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
- cred->cr_groups[i] = credanon->cr_groups[i];
- cred->cr_ngroups = i;
- }
- if (exflags & MNT_EXRDONLY)
- *rdonlyp = 1;
- else
- *rdonlyp = 0;
+ if (!nfse_api_used) {
+ /*
+ * Check/setup credentials.
+ */
+ if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+ cred->cr_uid = credanon->cr_uid;
+ for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+ cred->cr_groups[i] = credanon->cr_groups[i];
+ cred->cr_ngroups = i;
+ }
+ if (exflags & MNT_EXRDONLY)
+ *rdonlyp = 1;
+ else
+ *rdonlyp = 0;
+ }
if (!lockflag)
VOP_UNLOCK(*vpp, 0, td);
diff -ruN nfsserver.orig/nfs_syscalls.c nfsserver/nfs_syscalls.c
--- nfsserver.orig/nfs_syscalls.c 2008-08-02 12:59:25.000000000 +0300
+++ nfsserver/nfs_syscalls.c 2009-06-01 12:22:43.000000000 +0300
@@ -70,6 +70,7 @@
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfsserver/nfs.h>
+#include <nfsserver/nfs_export.h>
#include <nfsserver/nfsm_subs.h>
#include <nfsserver/nfsrvcache.h>
@@ -143,7 +144,10 @@
"nfsd init", 0);
}
NFSD_UNLOCK();
- if (uap->flag & NFSSVC_ADDSOCK) {
+ if (uap->flag & NFSSVC_EXPORT) {
+ nfse_api_used = 1;
+ error = nfse_cmds(td, (void *)uap->argp);
+ } else if (uap->flag & NFSSVC_ADDSOCK) {
error = copyin(uap->argp, (caddr_t)&nfsdarg, sizeof(nfsdarg));
if (error)
return (error);
#########################################################################
src/usr.sbin/mountd (this is not a diff):
#########################################################################
diff -ruN empty/Makefile mountd/Makefile
--- empty/Makefile 1970-01-01 03:00:00.000000000 +0300
+++ mountd/Makefile 2009-07-17 13:39:38.000000000 +0300
@@ -0,0 +1,15 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $FreeBSD: src/usr.sbin/mountd/Makefile,v 1.17 2006/05/23 17:10:17 rodrigc Exp $
+
+PROG= mountd
+SRCS= mountd.c mountd_conf.c mountd_xdr.c
+MAN= exports.5 netgroup.5 mountd.8
+
+DEBUG_FLAGS= -DDEBUG_MEMORY_LEAK
+
+WARNS?= 4
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff -ruN empty/exports.5 mountd/exports.5
--- empty/exports.5 1970-01-01 03:00:00.000000000 +0300
+++ mountd/exports.5 2009-06-01 12:12:57.000000000 +0300
@@ -0,0 +1,477 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)exports.5 8.3 (Berkeley) 3/29/95
+.\" $FreeBSD: src/usr.sbin/mountd/exports.5,v 1.32 2008/11/03 10:38:00 dfr Exp $
+.\"
+.Dd April 27, 2009
+.Dt EXPORTS 5
+.Os
+.Sh NAME
+.Nm exports
+.Nd configuration file for
+.Xr mountd 8
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file specifies configuration for the
+.Xr mountd 8
+utility.
+See
+.%T "Network File System Protocol Specification" ,
+RFC1094, Appendix A,
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+RFC1813, Appendix I,
+.%T "WebNFS Client Specification" ,
+RFC2054 and
+.%T "WebNFS Server Specification" ,
+RFC2055.
+.Pp
+The
+.Nm
+file consists of lines.
+All characters after a
+.Ql #
+character (including this character) are ignored and considered as a comment.
+A long line may be split over several lines by ending all but the
+last line with a backslash
+.Pq Ql \e .
+Any character can be represented as a backslash
+.Pq Ql \e
+followed by its three digits octal code (this can be used for representing
+a character that is a part of the syntax format).
+.Pp
+There are two types of configuration lines.
+A line of the first type starts with
+.Dq options:
+and specifies global options that must be given before all other lines.
+A line of the second type starts with absolute path name followed by
+export specifications for the given path name.
+This path name will be exported only if this path name is or will be
+a mount point for some file system and there is no mistake in any
+line related to this path name.
+If a file system is exported to some client, then all its subdirectories
+and files (that belong to this file system) are also exported to this client.
+.Pp
+Each option starts with
+.Ql -
+followed by its name
+.No ( Fl opt ) .
+If an option has an argument, then an option's name and its argument
+should be separated by white space
+.Sm off
+.No ( Fl opt No \ Sy arg )
+.Sm on
+or by a
+.Ql =
+character
+.Sm off
+.No ( Fl opt No = Sy arg ) .
+.Sm on
+Options can be grouped using
+.Ql \&,
+(in this case only the first option must have
+.Ql -
+before its name and options' names and their arguments should be separated by a
+.Ql =
+character).
+.Pp
+Available options:
+.Pp
+.Bl -tag -width indent
+.Sm off
+.It Fl sec Li = Sy flavor1 Op Sy :flavor2:flavor3...
+.Sm on
+Specify a colon separated list of acceptable security flavors to be
+used for remote access.
+Supported security flavors are sys, krb5, krb5i and krb5p.
+If multiple flavors are listed, they should be ordered with the most
+preferred flavor first.
+If this option is not present,
+the default security flavor list of just sys is used.
+This option can be used as a global option, in this case its value
+will be used as a default value for all export specifications.
+.It Fl ro
+Specify that the file system should be exported read-only (default read/write).
+.It Fl rw
+Specify that the file system should be exported read-write (this is default).
+.Sm off
+.It Fl maproot No = Sy user Op Sy :group1:group2...
+.Sm on
+The credential of the specified user is used for remote access by root.
+The user may be specified by name or number.
+The colon separated list is used to specify the precise credential
+to be used for remote access by root.
+The elements of the list may be either group names or numbers.
+Note that
+.Sy user:
+should be used to distinguish a credential containing no groups from
+a complete credential for that user.
+.Sm off
+.It Fl mapall No = Sy user Op Sy :group1:group2:...
+.Sm on
+The credential of the specified user is used for remote access by all
+users (including root).
+In the absence of
+.Fl maproot
+and
+.Fl mapall
+options, remote accesses by root will result in using a credential of
+.No -2:-2 ,
+all other users will be mapped to their remote credential.
+This option is mutually exclusive with
+.Fl maproot
+option.
+.It Fl public
+.Tn WebNFS
+exports strictly according to the RFC 2054 and RFC 2055 can be done with
+this option.
+However, this option in itself allows read-write access to all files in
+the file system (default behaviour), not requiring reserved ports and
+not remapping user credentials.
+It is only provided to conform to the specification, and should normally
+not be used.
+.It Fl webnfs
+Use this flag for a
+.Tn WebNFS
+export, which implies
+.Fl public ,
+.Sm off
+.Fl mapall No = Sy -2:-2
+.Sm on
+and
+.Fl ro .
+Note that only one file system can be
+.Tn WebNFS
+exported on a server.
+.Sm off
+.It Fl index No = Pa file
+.Sm on
+Specify a file whose handle will be returned if a directory is looked up
+using the public filehandle
+.Pq Tn WebNFS .
+This is to mimic the behaviour of
+.Tn URL Ns s .
+If this option is not specified, a directory filehandle will be returned
+as usual.
+This option only makes sense for
+.Tn WebNFS .
+.It Fl no_mntproc_dump
+Disable
+.Tn MOUNT
+protocol's procedure
+.Tn DUMP
+(return mount entries)
+for
+.Tn NFSv2/3 .
+This option can be used as a global option, in this case it will be used
+for all file systems.
+If it is used for a file system together with the
+.Fl nospec
+option, then it is used for all mounts for this file system.
+Else it is used for hosts that match particular export specification.
+.It Fl no_mntproc_export
+Disable
+.Tn MOUNT
+protocol's procedure
+.Tn EXPORT
+(return export list).
+This option can be used as a global option, in this case it will be used
+for all file systems.
+If it is used for a file system together with the
+.Fl nospec
+option, then it is used for all export specifications for this file system.
+Else it is used for particular export specification.
+.It Fl nospec
+Specify that this line does not have any address specification (including
+the default one).
+.It Fl quiet
+If a directory path name given at the beginning of a line is not
+a mount point, then all its settings will be ignored and by default
+.Xr mountd 8
+will log message about this.
+This option allows to suppress these messages.
+.Sm off
+.It Fl Oo ! Oc Cm host No = Sy hostname
+.Sm on
+Specify that corresponding file system is exported to this host, that
+can be given by name or by address.
+All associated addresses with the given hostname are added to this line.
+.Sm off
+.It Fl Oo ! Oc Cm network No = Sy netname Op Li / Sy prefixlength
+.Sm on
+Specify that corresponding file system is exported to this network.
+If the prefix length is not specified and
+.Fl mask
+option is not used, then it will default to the mask for that
+.Tn IPv4
+network class (A, B or C).
+Also a network can be specified as a
+.Dq network name
+as defined in the
+.Pa /etc/networks
+file (see
+.Xr networks 5 ) .
+.Sm off
+.It Fl mask No = Sy netmask
+.Sm on
+Specify a network mask for previous
+.Fl network
+option (that should not have a prefix length).
+The given network mask and network must belong to the same address family.
+Also a netmask can be specified as a
+.Dq network name .
+.El
+.Pp
+Options without
+.Ql - :
+.Bl -tag -width indent
+.It Oo ! Oc Ns Sy hostname
+Specify that corresponding file system is exported to this hostname.
+All hostnames are checked to see if they are
+.Dq netgroup
+names as defined in the
+.Pa /etc/netgroup
+file (see
+.Xr netgroup 5 )
+first and are assumed to be hostnames otherwise.
+Using the full domain specification for a hostname can normally
+circumvent the problem of a host that has the same name as a netgroup.
+All associated addresses with this hostname are added to this line.
+.El
+.Pp
+Export specifications for one file system can be written in several lines.
+Options
+.Fl host ,
+.Fl network
+and
+.Sy hostname
+can be used multiple times.
+After any address specification it is possible to use already specified
+option and its value will overwrite previous option's value.
+.Pp
+Before any address specification it is possible to use the
+.Ql \&!
+character, that means
+.Dq deny access
+for all clients that match this address specification.
+.Pp
+If a configuration line for some file system does not have any address
+specification, then this line defines default export for all other hosts
+and should be used only when the file system contains public information.
+.Pp
+The
+.Xr nfsd 8
+and
+.Xr mountd 8
+always choose the most specific address specification (longest prefix match)
+for a client's address and uses options specified for the chosen most
+specific address specification.
+.Pp
+The
+.Xr mountd 8
+utility can be made to re-read the
+.Nm
+file by sending it a hangup signal as follows:
+.Bd -literal -offset indent
+/etc/rc.d/mountd reload
+.Ed
+.Pp
+After sending the sighup signal, check the
+.Xr syslogd 8
+output to see whether
+.Xr mountd 8
+logged any parsing errors in the
+.Nm
+file.
+.Pp
+Alternatively
+the
+.Xr mountd 8
+utility can be made to re-read the
+.Nm
+file by sending it a
+.Ar reload
+command.
+Read the
+.Xr mountd 8
+for detail information.
+.Sh FILES
+.Bl -tag -width /etc/exports -compact
+.It Pa /etc/exports
+default file with the list of exported file systems
+.El
+.Sh EXAMPLES
+Path names
+.Pa /usr , /u , /a , /b
+and
+.Pa /u2
+are local file system mount points.
+.Pp
+.Bd -literal -offset indent
+/usr -maproot 0:10 friends
+/usr -maproot daemon grumpy.cis.uoguelph.ca 131.104.48.16
+/usr -ro -mapall nobody
+.Ed
+.Pp
+The file system rooted at
+.Pa /usr
+is exported read-write to hosts in
+.Dq friends
+where
+.Dq friends
+is specified in the netgroup file with users mapped to their
+remote credentials and root mapped to
+.Tn UID
+0 and
+.Tn GID
+10.
+It is exported read-write to
+.Li 131.104.48.16
+and
+.Li grumpy.cis.uoguelph.ca
+with users mapped to their remote credentials and
+root mapped to the user and groups associated with
+.Dq daemon ;
+it is exported to the rest of the world as read-only with
+all users mapped to the user and groups associated with
+.Dq nobody .
+.Pp
+.Bd -literal -offset indent
+/u -maproot bin: -network 131.104.48 -mask 255.255.255.0
+.Ed
+.Pp
+The file system rooted at
+.Pa /u
+is exported to all hosts on the subnetwork
+.Li 131.104.48
+with root mapped to the
+.Tn UID
+for
+.Dq bin
+and with no group access.
+.Pp
+.Bd -literal -offset indent
+/a -network 192.168.0/24
+/a -network 3ffe:1ce1:1:fe80::/64
+.Ed
+.Pp
+The file system rooted at
+.Pa /a
+is exported read-write to the network
+.Li 192.168.0.0 ,
+with a netmask of
+.Li 255.255.255.0 .
+It is also exported read-write to the
+.Tn IPv6
+network
+.Li 3ffe:1ce1:1:fe80::
+address, using the upper 64 bits as the prefix.
+Note that, unlike with
+.Tn IPv4
+network addresses, the specified network
+address must be complete, and not just contain the upper bits.
+.Pp
+.Bd -literal -offset indent
+/u2 -maproot root friends
+/u2 -network cis-net -mask cis-mask
+.Ed
+.Pp
+The file system rooted at
+.Pa /u2
+is exported read-write to the hosts in
+.Dq friends
+with root mapped to
+.Tn UID
+and groups associated with
+.Dq root ;
+it is exported read-write to all hosts on network
+.Dq cis-net
+with mask
+.Dq cis-mask
+(both are specified as
+.Dq network names ) .
+.Pp
+.Bd -literal -offset indent
+/cdrom -quiet,ro -network 192.168.33.0 -mask 255.255.255.0
+.Ed
+.Pp
+The file system rooted at
+.Pa /cdrom
+will be exported read-only to the entire network 192.168.33.0/24.
+Since
+.Pa /cdrom
+is the conventional mountpoint for a CD-ROM device, this export will
+fail if no CD-ROM medium is currently mounted there since that line
+would then attempt to export a subdirectory of the root file system
+which is not allowed.
+The
+.Fl quiet
+option will then suppress the error message for this condition that
+would normally be syslogged.
+As soon as an actual CD-ROM is going to be mounted, the kernel will notify
+.Xr mountd 8
+about this event, and the
+.Pa /cdrom
+file system will be exported as intended.
+.Pp
+.Bd -literal -offset indent
+/private -sec krb5i
+/secret -sec krb5p
+.Ed
+.Pp
+The file system rooted at
+.Pa /private
+will be exported read-write using Kerberos 5 authentication and will require
+integrity protected messages for all accesses.
+The file system rooted at
+.Pa /secret
+will also be exported read-write using Kerberos 5 authentication and all
+messages used to access it will be encrypted.
+.Pp
+.Bd -literal -offset indent
+/b -ro -mapall nobody -host 10.1.1.1 -rw -host 10.2.2.2
+.Ed
+.Pp
+The file system rooted at
+.Pa /b
+is exported read-only to
+.Li 10.1.1.1
+and with read-write access to
+.Li 10.2.2.2
+with all users mapped to the user and groups associated with
+.Dq nobody .
+The second host inherit the
+.Fl mapall
+option.
+.Sh SEE ALSO
+.Xr netgroup 5 ,
+.Xr mountd 8 ,
+.Xr nfsd 8 ,
+.Xr showmount 8
diff -ruN empty/mountd.8 mountd/mountd.8
--- empty/mountd.8 1970-01-01 03:00:00.000000000 +0300
+++ mountd/mountd.8 2009-06-02 18:01:58.000000000 +0300
@@ -0,0 +1,304 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95
+.\" $FreeBSD: src/usr.sbin/mountd/mountd.8,v 1.31 2007/10/20 11:25:34 matteo Exp $
+.\"
+.Dd April 27, 2009
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd configure export specifications for
+.Xr nfsd 8
+and service
+.Tn NFSv2/3 MOUNT
+requests
+.Sh SYNOPSIS
+.Nm
+.Op Fl 2dlnrt
+.Op Fl c Ar command
+.Op Fl h Ar bindip
+.Op Fl p Ar port
+.Op Ar exportsfile ...
+.Sh DESCRIPTION
+The
+.Nm
+utility configures export specifications for
+.Xr nfsd 8
+and is the server for
+.Tn NFSv2/3 MOUNT
+requests from client machines.
+See
+.%T "Network File System Protocol Specification" ,
+RFC1094, Appendix A,
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+RFC1813, Appendix I and
+.%T "WebNFS Server Specification" ,
+RFC2055.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 2
+Allow the administrator to force clients to use only the
+.Tn NFSv2
+protocol to mount file systems from this server.
+.It Fl d
+.Nm
+will not detach from the controlling terminal and will print
+log and debugging messages to standard error stream.
+.It Fl c Ar command
+Send a command to the running
+.Nm .
+The
+.Ar reload
+command reloads
+.Xr exports 5
+files.
+The
+.Ar flush
+command flushes all export specifications for all file systems.
+Other commands change export specifications for file systems and can be
+specified multiple times:
+.Ar add
+new export specification,
+.Ar update
+existent export specification,
+.Ar delete
+existent export specification,
+.Ar flush
+all export specifications for the given file system,
+.Ar file
+means to read commands from the given file.
+The
+.Ar add ,
+.Ar update
+and
+.Ar delete
+commands accept option
+.Fl f Ar filename ,
+in this case all export specifications from the given file are
+added or deleted respectively.
+.It Fl h Ar bindip
+Specify specific
+.Tn IP
+addresses to bind to for
+.Tn TCP
+and
+.Tn UDP
+requests.
+This option may be specified multiple times.
+If no
+.Fl h
+option is specified,
+.Nm
+will accept connections on any interface with
+.Tn IP
+address.
+Note that when specifying
+.Tn IP
+addresses with
+.Fl h ,
+.Nm
+will automatically add
+.Li 127.0.0.1
+and
+.Li ::1
+to the list.
+.It Fl l
+Cause all succeeded client requests to be logged.
+.It Fl n
+Allow
+.Tn MOUNT
+protocol's procedures
+.Tn MNT ,
+.Tn UMNT
+and
+.Tn UMNTALL
+requests from unprivileged ports to be served.
+This should only be specified if there are clients that require it.
+It will automatically clear the vfs.nfsrv.nfs_privport sysctl flag, which
+controls if the kernel will accept
+.Tn NFS
+requests from reserved ports only.
+.It Fl r
+Allow
+.Tn MOUNT
+protocol's procedure
+.Tn MNT
+requests for regular files to be served.
+Although this seems to violate the
+.Tn MOUNT
+protocol specification, some diskless workstations do
+.Tn MNT
+requests for their swapfiles and expect them to be regular files.
+.It Fl p Ar port
+Force
+.Nm
+to bind to the specified port, for both
+.Dv AF_INET
+and
+.Dv AF_INET6
+address families.
+This is typically done to ensure that the port which
+.Nm
+binds to is a known quantity which can be used in firewall rulesets.
+If
+.Nm
+cannot bind to this port, an appropriate error will be recorded in
+the system log, and the daemon will then exit.
+.It Fl t
+Parse
+.Xr exports 5
+files or commands in
+.Fl c
+options and output configuration to the standard output.
+.It Ar exportsfile
+Specify an alternate location
+for the
+.Xr exports 5
+file.
+More than one file name can be specified.
+All given files must exist in the file system.
+Also its possible to specify directory names, finish their names with slash
+.Pq Ql / .
+In this case all files from the given directories will be used.
+Any given directory can be absent in the file system.
+.El
+.Pp
+When
+.Nm
+is started, it parses
+.Xr exports 5
+files and loads export specifications into the kernel only for file systems
+that have correct configuration.
+After changing the
+.Xr exports 5
+file a hangup signal should be sent to the
+.Nm
+to get it to reload export specifications and forget all changes in
+export specifications made by commands (alternative method is the
+.Ar reload
+command).
+After sending the sighup signal check log messages to see if
+.Nm
+logged any parsing errors.
+.Pp
+.Nm
+loads and changes export specifications atomically, so reloading of
+.Xr exports 5
+files or applying commands to the current configuration will not cause
+temporal failures for clients that currently are using and are allowed
+to use the same exported file systems in the new configuration.
+.Pp
+If some command in the
+.Fl c
+option cannot be applied to the current configuration, then all given
+commands will not be applied to the current configuration.
+A set of commands can be considered as one transaction with changes.
+.Pp
+If
+.Nm
+detects that the running kernel does not include
+.Tn NFS
+support, it will attempt to load a loadable kernel module containing
+.Tn NFS
+code, using
+.Xr kldload 2 .
+If this fails, or no
+.Tn NFS KLD
+was available,
+.Nm
+exits with an error code.
+.Pp
+While servicing requests and parsing
+.Xr exports 5
+files
+.Nm
+can exit with an error code only if some critical error occurs
+(such as not enough memory error or some file cannot be closed).
+.Sh FILES
+.Bl -tag -width /var/run/mountd.pid -compact
+.It Pa /etc/exports
+default file with the list of exported file systems
+.It Pa /var/run/mountd.pid
+the
+.Tn PID
+of the currently running
+.Nm
+.It Pa /var/run/mountd.socket
+local domain
+.Tn TCP
+socket for control messages
+.It Pa /var/db/mountdtab
+the current list of remote mounted file systems when
+.Nm
+is not running
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+Test two files and files from the directory for correctness:
+.Pp
+.Dl "mountd -t export-lan exports-all /usr/local/etc/exports/"
+.Pp
+Test commands (no command is sent):
+.Pp
+.Dl "mountd -t -c 'add /fs -ro host1' -c 'file commands.txt'
+.Pp
+Add export specification:
+.Pp
+.Dl "mountd -c 'add /fs -rw host1 host2'"
+.Pp
+Change export specifications:
+.Pp
+.Dl "mountd -c 'delete /fs host1' -c 'update /fs -ro host2'"
+.Pp
+Flush export specifications and add new ones from the file:
+.Pp
+.Dl "mountd -c 'flush /fs' -c 'add -f /etc/fs-exports'
+.Pp
+Reload
+.Xr exports 5
+files (unlike sending the sighup signal this command is synchronous,
+the sender always knows when a new settings were loaded into the kernel):
+.Pp
+.Dl "mountd -c reload"
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr kldload 2 ,
+.Xr exports 5 ,
+.Xr nfsd 8 ,
+.Xr rpcbind 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff -ruN empty/mountd.c mountd/mountd.c
--- empty/mountd.c 1970-01-01 03:00:00.000000000 +0300
+++ mountd/mountd.c 2009-06-26 14:12:38.000000000 +0300
@@ -0,0 +1,3084 @@
+/*-
+ * Copyright (c) 2009 Andrey Simonenko
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /*not lint*/
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
+#endif /*not lint*/
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/usr.sbin/mountd/mountd.c,v 1.98 2008/11/03 10:38:00 dfr Exp $");
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/ucred.h>
+#include <sys/un.h>
+
+#include <arpa/inet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+#include <nfsserver/nfs_export.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libutil.h>
+#include <limits.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "mountd.h"
+#include "mountd_conf.h"
+#include "mountd_xdr.h"
+#include "pathnames.h"
+
+#define KLD_NFSSERVER "nfsserver"
+
+#define MNTLISTFILE _PATH_RMOUNTLIST
+
+#define CTL_TIMEOUT 30 /* Timeout in seconds for command. */
+
+char have_ipv4 = 0; /* Set if have IPv4 bound socket. */
+char have_ipv6 = 0; /* Set if have IPv6 bound socket. */
+
+char no_mntproc_dump; /* Global -no_mntproc_dump option. */
+char no_mntproc_export; /* Global -no_mntproc_export option. */
+
+char syserr_flag = 0; /* Set if a system error occurred. */
+
+struct fs_exp_list fs_exp_list1; /* These two lists keep new and old */
+struct fs_exp_list fs_exp_list2; /* configuration. */
+struct fs_exp_list *fs_exp_list; /* Current configuration. */
+struct fs_exp_list *fs_exp_list_prev; /* Previous configuration. */
+struct fs_exp_list fs_exp_list_update; /* Updates to configuration. */
+
+char *index_webnfs = NULL; /* Index file for WebNFS. */
+
+struct ctl_cmd_hdr ctl_cmd_hdr; /* Control command header. */
+void *ctl_cmd_buf; /* Control command buffer. */
+
+char subpath_empty[] = ""; /* No subpath (to simplify code). */
+
+char test_conf = 0; /* -t */
+
+static char force_nfsv2 = 0; /* -2 */
+static char log_cli_reqs = 0; /* -l */
+static char dir_only = 1; /* -r */
+
+static int resvport_only = 1; /* -n */
+static in_port_t svcport = 0; /* -p port */
+
+static int ctl_listen_fd = -1; /* Control listen socket descriptor. */
+static char ctl_socket_bound = 0; /* Non-zero if socket file is bound. */
+
+/*
+ * netconfig(5) file has network configuration for RPC.
+ * This structure has configuration for one network ID.
+ */
+struct netconf {
+ const char *netid; /* Network ID. */
+ const struct netconfig *nc; /* Corresponding netconfig. */
+ int visible; /* NC_VISIBLE flag. */
+};
+
+#define NETID_UDP 0
+#define NETID_TCP 1
+#define NETID_UDP6 2
+#define NETID_TCP6 3
+
+/*
+ * Table of all supported network configurations.
+ */
+static struct netconf netconf_tbl[] = {
+ [NETID_UDP] = { .netid = "udp", .nc = NULL },
+ [NETID_TCP] = { .netid = "tcp", .nc = NULL },
+ [NETID_UDP6] = { .netid = "udp6", .nc = NULL },
+ [NETID_TCP6] = { .netid = "tcp6", .nc = NULL }
+};
+
+#define NC_TBL_SIZE (sizeof(netconf_tbl) / sizeof(netconf_tbl[0]))
+
+/*
+ * Union for remembering registered services.
+ */
+static union {
+ uint8_t net[4];
+ uint32_t any;
+} rpcmnt_reg[2];
+
+/*
+ * Requirements of RPC mount procedure.
+ */
+struct mntproc_req {
+ int priv; /* Requires privilege port. */
+ const char *name; /* Name of this procedure. */
+};
+
+/*
+ * Table of RPC mount procedures.
+ * NFSv2 and NFSv3 define RPC mount procedures numbers (both standards
+ * use the same numbers for the same procedures, NULLPROC has 0 number,
+ * RPCMNT_* have numbers 1, 2, 3, 4, 5).
+ */
+static const struct mntproc_req mntproc_req[] = {
+ [NULLPROC] = { .priv = 0, .name = "null" },
+ [RPCMNT_MOUNT] = { .priv = 1, .name = "mnt" },
+ [RPCMNT_DUMP] = { .priv = 0, .name = "dump" },
+ [RPCMNT_UMOUNT] = { .priv = 1, .name = "umnt" },
+ [RPCMNT_UMNTALL] = { .priv = 1, .name = "umntall" },
+ [RPCMNT_EXPORT] = { .priv = 0, .name = "export" }
+};
+
+#define RPCMNT_MAX_NUMBER RPCMNT_EXPORT
+
+/* Pipe for IPC from asynchronous signals handlers. */
+static int sig_pipe[2];
+
+/* Flags for signal handles. */
+static volatile sig_atomic_t signal_flag = 0;
+static volatile sig_atomic_t reconf_flag = 0;
+static volatile sig_atomic_t shutdown_flag = 0;
+
+#define EXPSPEC_ES_NUM 20 /* Number of export specifications
+ per one nfssvc() system call. */
+
+#define EXPSPEC_TO_SLEEP 3 /* Sleep this number of seconds
+ after transaction timeout. */
+
+#define EXPSPEC_RET_OK 0 /* Function succeeded. */
+#define EXPSPEC_RET_FAILED 1 /* Function failed. */
+#define EXPSPEC_RET_TIMEOUT 2 /* Commands transaction timeout. */
+
+#define EXPSPEC_FN_CLEAR 0 /* Clear all export specifications. */
+#define EXPSPEC_FN_RELOAD 1 /* Load new configuration. */
+#define EXPSPEC_FN_UPDATE 2 /* Update current configuration. */
+#define EXPSPEC_FN_EVENT 3 /* Handle VFS events. */
+
+static int expspec_clear(void);
+static int expspec_reload(void);
+static int expspec_update(void);
+static int expspec_event(void);
+
+/*
+ * Table of functions that work with export specifications in nfsserver.
+ * Do not call expspec_*() functions directly outside of expspec_*()
+ * functions, use the expspec_func() function instead.
+ */
+static struct {
+ int (*func)(void); /* Pointer to function. */
+ const char *name; /* Function name. */
+} expspec_func_tbl[] = {
+ [EXPSPEC_FN_CLEAR] =
+ { .func = expspec_clear, .name = "expspec_clear" },
+ [EXPSPEC_FN_RELOAD] =
+ { .func = expspec_reload, .name = "expspec_reload" },
+ [EXPSPEC_FN_UPDATE] =
+ { .func = expspec_update, .name = "expspec_update" },
+ [EXPSPEC_FN_EVENT] =
+ { .func = expspec_event, .name = "expspec_event" }
+};
+
+/*
+ * SIGHUP handler.
+ */
+/* ARGSUSED */
+static void
+sig_hup(int signo __unused)
+{
+ (void)write(sig_pipe[1], "", 1);
+ signal_flag = 1;
+ reconf_flag = 1;
+}
+
+/*
+ * SIGTERM (and SIGINT) handler.
+ */
+/* ARGSUSED */
+static void
+sig_term(int signo __unused)
+{
+ (void)write(sig_pipe[1], "", 1);
+ signal_flag = 1;
+ shutdown_flag = 1;
+}
+
+/*
+ * Make descriptor non-blockable.
+ */
+static int
+set_nonblock(const int fd)
+{
+ int val;
+
+ val = fcntl(fd, F_GETFL, 0);
+ if (val < 0) {
+ syslog(LOG_ERR, "set_nonblock: fcntl(%d, F_GETFL): %m", fd);
+ return (-1);
+ }
+ if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
+ syslog(LOG_ERR, "set_nonblock: fcntl(%d, F_SETFL): %m", fd);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Make descriptor blockable.
+ */
+static int
+set_block(const int fd)
+{
+ int val;
+
+ val = fcntl(fd, F_GETFL, 0);
+ if (val < 0) {
+ syslog(LOG_ERR, "set_block: fcntl(%d, F_GETFL): %m", fd);
+ return (-1);
+ }
+ if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
+ syslog(LOG_ERR, "set_block: fcntl(%d, F_SETFL): %m", fd);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Find exported fs_exp by a file system ID.
+ */
+static struct fs_exp *
+fs_exp_by_id(const fsid_t *fsid)
+{
+ struct fs_exp *fe;
+
+ TAILQ_FOREACH(fe, fs_exp_list, link)
+ if ((fe->oflags & OPT_EXPORTED) &&
+ fe->fsid.val[0] == fsid->val[0] &&
+ fe->fsid.val[1] == fsid->val[1])
+ break;
+ return (fe);
+}
+
+/*
+ * Find fs_exp by a directory path.
+ */
+struct fs_exp *
+fs_exp_by_path(const struct fs_exp_list *fe_list, const char *path)
+{
+ struct fs_exp *fe;
+
+ TAILQ_FOREACH(fe, fe_list, link)
+ if (strcmp(fe->path, path) == 0)
+ break;
+ return (fe);
+}
+
+/*
+ * Send commands to nfsserver.
+ */
+static int
+nfsserver_call(struct nfse_cmds_hdr *hdr)
+{
+ if (nfssvc(NFSSVC_EXPORT, hdr) == 0)
+ return (EXPSPEC_RET_OK);
+
+ if (errno == ESRCH) {
+ syslog(LOG_WARNING, "nfsserver_call: nfssvc(NFSSVC_EXPORT): "
+ "commands transaction timeout");
+ return (EXPSPEC_RET_TIMEOUT);
+ }
+
+ syslog(LOG_ERR, "nfsserver_call: nfssvc(NFSSVC_EXPORT): %m");
+ return (EXPSPEC_RET_FAILED);
+}
+
+/*
+ * Clear all export specifications.
+ */
+static int
+expspec_clear(void)
+{
+ static struct nfse_cmds_hdr hdr = {
+ .version = NFSE_API_VERSION,