static void _diod_mount (Opt o, int rfd, int wfd, char *spec, char *dir, int vopt, int fopt, int nopt) { char *options, *options9p, *aname, *uname; uid_t uid; int msize; Npcfsys *fs; Npcfid *afid, *root; unsigned long mountflags = 0; options = opt_csv (o); _getflags (o, &mountflags); options9p = opt_csv (o); /* after mountflags removed from opt list */ if (!(uname = opt_find (o, "uname"))) msg_exit ("uname is not set"); /* can't happen */ uid = _uname2uid (uname); aname = opt_find (o, "aname"); /* can be null */ if (!opt_scanf (o, "msize=%d", &msize) || msize < P9_IOHDRSZ) msg_exit ("msize must be set to integer >= %d", P9_IOHDRSZ); if (vopt) msg ("pre-authenticating connection to server"); if (!(fs = npc_start (rfd, wfd, msize, 0))) errn_exit (np_rerror (), "version"); if (!(afid = npc_auth (fs, aname, uid, diod_auth)) && np_rerror () != 0) errn_exit (np_rerror (), "auth"); if (!(root = npc_attach (fs, afid, aname, uid))) { errn (np_rerror (), "attach"); if (afid) (void)npc_clunk (afid); exit (1); } if (afid && npc_clunk (afid) < 0) errn_exit (np_rerror (), "clunk afid"); if (npc_clunk (root) < 0) errn_exit (np_rerror (), "clunk root"); if (vopt) msg ("mount -t 9p %s %s -o%s", spec, dir, options); if (!fopt) { /* kernel wants non-blocking */ if (fcntl (rfd, F_SETFL, O_NONBLOCK) < 0) err_exit ("setting O_NONBLOCK flag on rfd=%d", rfd); if (fcntl (wfd, F_SETFL, O_NONBLOCK) < 0) err_exit ("setting O_NONBLOCK flag on wfd=%d", wfd); _mount (spec, dir, mountflags, options9p); } npc_finish (fs); /* closes fd */ if (!nopt) { if (!_update_mtab (options, spec, dir)) msg_exit ("failed to update /etc/mtab"); } free (options); free (options9p); }
/* 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); }
static int opt_process_one(struct opt_entry *list, opt_flags *flg, char *opt) { char *param; struct opt_entry *entry; completed = NULL; param = opt_find(list, opt, &entry); if (!entry) return OPT_ERROR_UNKNOWN; if (*flg & entry->flg_set & entry->flg_clr) return OPT_ERROR_COMB; *flg &= ~entry->flg_clr; *flg |= entry->flg_set; if (entry->format) { if (!param) { if (entry->req_clr & OPT_REQ_PARAM) return OPT_ERROR_PARAM_REQ; } else if (opt_process_param(param, entry->format, entry->param)) return OPT_ERROR_PARAM_INV; /* Dupe checking without an option flag */ if (param && entry->flg_set == FLG_ZERO && entry->req_clr & OPT_REQ_PARAM && entry->param_set++) return OPT_ERROR_COMB; } else if (param) return OPT_ERROR_PARAM_EXT; return OPT_ERROR_NONE; }
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 int opt_process_one(struct opt_entry *list, opt_flags *flg, char *opt) { char *param; struct opt_entry *entry; param = opt_find(list, opt, &entry); if (!entry) return OPT_ERROR_UNKNOWN; if (*flg & entry->flg_set & entry->flg_clr) return OPT_ERROR_COMB; *flg &= ~entry->flg_clr; *flg |= entry->flg_set; if (entry->format) { if (!param) { if (entry->req_clr & OPT_REQ_PARAM) return OPT_ERROR_PARAM_REQ; } else if (opt_process_param(param, entry->format, entry->param)) return OPT_ERROR_PARAM_INV; } else if (param) return OPT_ERROR_PARAM_EXT; return OPT_ERROR_NONE; }
int opt_get_val(const char *opt_name, char *buf, size_t len) { int i = opt_find(opt_name); if (i == -1) return (-1); switch (global_pref[i].type) { case OPT_BOOL: snprintf(buf, len, "%d", global_pref[i].val.b); break; case OPT_STR: if (global_pref[i].val.s == NULL) return (-1); xstrncpy(buf, global_pref[i].val.s, len); break; case OPT_INT: snprintf(buf, len, "%d", global_pref[i].val.i); break; case OPT_CHAR: snprintf(buf, len, "%c", global_pref[i].val.c); break; case OPT_COLOR: color_get_str(global_pref[i].val.i, buf, len); break; } return (0); }
static void _getflags (Opt o, unsigned long *flags) { int i; for (i = 0; i < sizeof (setopt) / sizeof (map_t); i++) { if (opt_find (o, setopt[i].opt)) { *flags |= setopt[i].flag; opt_delete (o, setopt[i].opt); } } for (i = 0; i < sizeof (clropt) / sizeof (map_t); i++) { if (opt_find (o, clropt[i].opt)) { *flags &= ~clropt[i].flag; opt_delete (o, clropt[i].opt); } } }
static int opt_check_one(struct opt_entry *list, opt_flags flg, char *opt) { struct opt_entry *entry; opt_find(list, opt, &entry); if (!entry) return OPT_ERROR_UNKNOWN; if ((flg & entry->req_set) != entry->req_set || (flg & entry->req_clr)) return OPT_ERROR_COMB; return OPT_ERROR_NONE; }
/* 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); }