int opt_check_allowed_csv (Opt o, const char *csv) { Opt allow; ListIterator itr; char *item, *cpy, *p; int ret = 0; NP_ASSERT (o->magic == OPT_MAGIC); allow = opt_create (); opt_addf (allow, "%s", csv); if (!(itr = list_iterator_create (o->list))) msg_exit ("out of memory"); while ((item = list_next (itr))) { if (!(cpy = strdup (item))) msg_exit ("out of memory"); if ((p = strchr (cpy, '='))) *p = '\0'; if (!opt_find (allow, cpy)) { ret = 1; free (cpy); break; } free (cpy); } list_iterator_destroy (itr); opt_destroy (allow); return ret; }
static char * _parse_spec (char *spec, Opt o) { char *host, *aname; if (!(host = strdup (spec))) msg_exit ("out of memory"); if ((aname = strchr (host, ':'))) *aname++ = '\0'; if (strlen (host) == 0) msg_exit ("no host specified"); if (!aname || strlen (aname) == 0) ; /* aname = opt_find (o, "aname"); */ else if (!opt_addf (o, "aname=%s", aname)) msg_exit ("you cannot have both -oaname and spec=host:aname"); return host; }
/* Private or public mount? Only allow two modes: * private: -ouname=USER,access=UID (uname could be root) * public (dflt): -ouname=root,access=user */ static void _parse_uname_access (Opt o) { char *uname = opt_find (o, "uname"); int uname_uid = -1; char *access = opt_find (o, "access"); int access_uid = -1; char *access_name = NULL; struct passwd *pw; if (uname) { if (!(pw = getpwnam (uname))) msg_exit ("could not look up uname='%s'", uname); uname_uid = pw->pw_uid; } if (access && opt_scanf (o, "access=%d", &access_uid)) { if (!(pw = getpwuid (access_uid))) msg_exit ("could not look up access='%d'", access_uid); if (!(access_name = strdup (pw->pw_name))) msg_exit ("out of memory"); } if (!uname && !access) { opt_addf (o, "uname=%s", "root"); opt_addf (o, "access=%s", "user"); } else if (uname && !access) { if (uname_uid == 0) opt_addf (o, "access=%s", "user"); else opt_addf (o, "access=%d", uname_uid); } else if (!uname && access) { if (strcmp (access, "user") == 0) opt_addf (o, "uname=%s", "root"); else if (access_name) /* access=<uid> */ opt_addf (o, "uname=%s", access_name); else msg_exit ("unsupported -oaccess=%s", access); } else { /* if (uname && access) */ if (strcmp (access, "user") == 0) { if (uname_uid != 0) msg_exit ("-oaccess=user can only be used with -ouname=root"); } else if (access_name) { /* access=<uid> */ if (uname_uid != access_uid) msg_exit ("-oaccess=<uid> requires matching -ouname=<name>"); } else msg_exit ("unsupported -oaccess=%s", access); } if (access_name) free (access_name); }
/* Attach 9nbd device to remote file. */ static void _nbd_attach (Opt o, int argc, char **argv, int nopt, int vopt) { char *spec; char *host; char *addr; char *dev; char *path; int fd; char *options; int blksize = 4096; int uid; if (argc != 2) usage(); spec = argv[0]; dev = argv[1]; _parse_nbdspec (spec, &host, &path); addr = _name2addr (host); if (!opt_find (o, "aname")) { opt_addf (o, "aname=%s", path); path = NULL; } if (!opt_find (o, "msize")) opt_addf (o, "msize=%d", DIOD_DEFAULT_MSIZE); if (opt_find (o, "trans=fd")) msg_exit ("9nbd doesn't work with trans=fd"); if (!opt_find (o, "trans")) opt_addf (o, "trans=%s", "tcp"); if (opt_find (o, "version") && !opt_find (o, "version=9p2000.L")) msg_exit ("9nbd only works with version=9p2000.L"); if (!opt_find (o, "version")) opt_addf (o, "version=%s", "9p2000.L"); if (!opt_find (o, "port")) opt_addf (o, "port=564"); /* for 9nbd we require uid=<int> instead of uname=<str> */ if (!opt_scanf (o, "uid=%d", &uid)) { char uname[256]; if (opt_scanf (o, "uname=%255s", uname)) { uid = _uname2uid (uname); opt_delete (o, "uname"); } else uid = 0; opt_addf (o, "uid=%d", uid); } if (!opt_find (o, "auth")) opt_addf (o, "auth=%s", "munge"); options = opt_csv (o); if (!nopt) { fd = open (dev, O_RDWR); if (fd < 0 && (errno == ENOENT || errno == ENXIO)) { system ("/sbin/modprobe 9nbd"); fd = open (dev, O_RDWR); } if (fd < 0) err_exit ("open %s", dev); } if (vopt) msg ("set blocksize=%d", blksize); if (!nopt && ioctl (fd, NBD_SET_BLKSIZE, blksize) < 0) err_exit ("ioctl set_blksize"); if (vopt) msg ("set opts=%s", options); if (!nopt && ioctl (fd, NBD_SET_OPTS, options) < 0) err_exit ("ioctl set_opts"); if (vopt) msg ("set addr=%s", addr); if (!nopt && ioctl (fd, NBD_SET_ADDR, addr) < 0) err_exit ("ioctl set_addr"); if (vopt) msg ("set path=%s", path ? path : "null"); if (!nopt && ioctl (fd, NBD_SET_PATH, path) < 0) err_exit ("ioctl set_path"); if (vopt) msg ("start"); if (!nopt && ioctl (fd, NBD_START) < 0) err_exit ("ioctl start"); if (!nopt) close (fd); free (options); free (host); free (addr); }
int main (int argc, char *argv[]) { char *dir = NULL; char *spec, *host; char *nspec = NULL; int c, i; int nopt = 0; int vopt = 0; int fopt = 0; int aopt = 0; int dopt = 0; int rfd = -1, wfd = -1; Opt o; diod_log_init (argv[0]); o = opt_create (); opterr = 0; while ((c = GETOPT (argc, argv, OPTIONS, longopts)) != -1) { switch (c) { case 'f': /* --fake-mount */ fopt = 1; break; case 'n': /* --no-mtab */ nopt = 1; break; case 'v': /* --verbose */ vopt++; break; case 'o': /* --options OPT[,OPT]... */ opt_addf (o, "%s", optarg); break; case 'a': /* --9nbd-attach */ aopt++; break; case 'd': /* --9nbd-detach */ dopt++; break; default: usage (); } } /* Take care of 9nbd operations and exit. */ if (aopt) { _nbd_attach (o, argc - optind, argv + optind, nopt, vopt); exit (0); } if (dopt) { _nbd_detach (o, argc - optind, argv + optind, nopt, vopt); exit (0); } if (optind != argc - 2) usage (); if (geteuid () != 0) msg_exit ("you must be root"); spec = argv[optind++]; dir = argv[optind++]; host = _parse_spec (spec, o); _verify_mountpoint (dir); /* Remount - only pass mount flags into the VFS for an existing mount. * Take care of it here and exit. */ if (opt_find (o, "remount")) { if (opt_check_allowed_csv (o, "ro,rw,aname,remount")) msg_exit ("-oremount can only be used with ro,rw"); _diod_remount (o, spec, dir, vopt, fopt); goto done; } /* Ensure uname and access are set, and to diod-compatible values. * The uname user becomes the euid which will be used by munge auth. */ _parse_uname_access (o); if (seteuid (_uname2uid (opt_find (o, "uname"))) < 0) err_exit ("seteuid"); /* We require -otrans=fd because auth occurs in user space, then live fd * is passed to the kernel via -orfdno,wfdno. */ if (!opt_find (o, "trans")) opt_addf (o, "trans=%s", "fd"); else if (!opt_find (o, "trans=fd")) msg_exit ("only -otrans=fd transport is supported"); /* Set msize if not already set. Validate it later. */ if (!opt_find (o, "msize")) opt_addf (o, "msize=%d", DIOD_DEFAULT_MSIZE); /* Only .L version is supported. */ if (!opt_find (o, "version")) opt_addf (o, "version=%s", "9p2000.L"); else if (!opt_find (o, "version=9p2000.L")) msg_exit ("only -oversion=9p2000.L is supported (little p, big L)"); /* Set debug level. */ if (!opt_find (o, "debug")) opt_addf (o, "debug=%d", 0x1); /* send errors to dmesg */ /* Set rwdepth (number of concurrent reads with buffer > msize). * N.B. this option is not upstream yet but unknown options are ignored. */ if (!opt_find (o, "rwdepth")) opt_addf (o, "rwdepth=%d", 1); /* Server is on an inherited file descriptor. * For testing, we start server on a socketpair duped to fd 0. */ if (opt_find (o, "rfdno") || opt_find (o, "wfdno")) { if (!opt_scanf (o, "rfdno=%d", &rfd) || !opt_scanf (o, "wfdno=%d",&wfd)) msg_exit ("-orfdno,wfdno must be used together"); nopt = 1; /* force no mtab */ /* Connect to server on UNIX domain socket */ } else if (host[0] == '/') { if (opt_find (o, "port")) msg_exit ("-oport won't work with UNIX domain socket"); if ((rfd = diod_sock_connect_unix (host, 0)) < 0) exit (1); wfd = rfd; opt_addf (o, "rfdno=%d", rfd); opt_addf (o, "wfdno=%d", wfd); /* Connect to server on IANA port (or user-specified) and host. */ } else { char *port = opt_find (o, "port"); hostlist_iterator_t hi; hostlist_t hl; char *h; if (!port) port = "564"; if (!(hl = hostlist_create (host))) msg_exit ("error parsing host string: %s", host); if (!(hi = hostlist_iterator_create (hl))) msg_exit ("out of memory"); while ((h = hostlist_next (hi))) { if (vopt) msg ("trying to connect to %s:%s", h, port); if ((rfd = diod_sock_connect_inet (h, port, DIOD_SOCK_QUIET)) >= 0) break; } if (h) { /* create new 'spec' string identifying successful host */ char *p = strchr (spec , ':'); int len = strlen (h) + (p ? strlen (p) : 0) + 1; if (!(nspec = malloc (len))) msg_exit ("out of memory"); snprintf (nspec, len, "%s%s", h, p ? p : ""); } hostlist_destroy (hl); if (rfd < 0) msg_exit ("could not connect to server(s), giving up"); wfd = rfd; opt_delete (o, "port"); opt_addf (o, "rfdno=%d", rfd); opt_addf (o, "wfdno=%d", wfd); } NP_ASSERT (opt_find (o, "trans=fd")); NP_ASSERT (opt_scanf (o, "msize=%d", &i)); NP_ASSERT (opt_find (o, "version=9p2000.L")); NP_ASSERT (opt_scanf (o, "debug=%d", &i) || opt_scanf (o, "debug=%x", &i)); NP_ASSERT (opt_scanf (o, "wfdno=%d", &i) && opt_scanf (o, "rfdno=%d", &i)); NP_ASSERT ((opt_find (o, "access=user") && opt_find(o, "uname=root")) || (opt_scanf (o, "access=%d", &i) && opt_find(o, "uname"))); NP_ASSERT (!opt_find (o, "port")); _diod_mount (o, rfd, wfd, nspec ? nspec : spec, dir, vopt, fopt, nopt); done: opt_destroy (o); exit (0); }