diff options
Diffstat (limited to '9mount.c')
-rw-r--r-- | 9mount.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/9mount.c b/9mount.c new file mode 100644 index 0000000..65bccac --- /dev/null +++ b/9mount.c @@ -0,0 +1,273 @@ +/* © 2008 sqweek <sqweek@gmail.com> + * See COPYING for details. + */ +#include <err.h> +#include <mntent.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <arpa/inet.h> +#include <pwd.h> +#include <netdb.h> + +#define nelem(x) (sizeof(x)/sizeof(*(x))) + +struct {char *mnemonic; int mask;} debug_flags[] = { + {"err", 0x001}, + {"devel", 0x002}, + {"9p", 0x004}, + {"vfs", 0x008}, + {"conv", 0x010}, + {"mux", 0x020}, + {"trans", 0x040}, + {"alloc", 0x080}, + {"fcall", 0x100} +}; + +char* +append(char **dest, char *src, int *destlen) +{ + while (strlen(*dest) + 1 + strlen(src) > *destlen) + *destlen *= 2; + if (!(*dest=realloc(*dest, *destlen))) + errx(1, "out of memory"); + + if (**dest) + strcat(*dest, ","); + strcat(*dest, src); + return *dest; +} + +char* +getarg(char opt, char *cp, char*** argv) +{ + if (*(cp+1)) { + return cp+1; + } else if (*(*argv+1)) { + return *++(*argv); + } else { + errx(1, "-%c: expected argument", opt); + } + return NULL; +} + +void +parsedial(char *dial, char **network, char **netaddr, int *port) +{ + char *cp; + if (!(*network=strtok(dial, "!"))) { + errx(1, "empty dial string"); + } + if (strcmp(*network, "unix") != 0 + && strcmp(*network, "tcp") != 0 + && strcmp(*network, "virtio") != 0) { + errx(1, "%s: unknown network (expecting unix, tcp or virtio)", *network); + } + if (!(*netaddr=strtok(NULL, "!"))) { + errx(1, "missing dial netaddress"); + } + if (strcmp(*network, "tcp") == 0) { + char *service; + if ((service=strtok(NULL, "!"))) { + if (strspn(service, "0123456789") == strlen(service)) { + *port = atoi(service); + } else { + struct servent *sv; + if ((sv=getservbyname(service, *network))) { + /* sv->s_port is a 16-bit big endian masquerading as an int */ + *port = ntohs((uint16_t)sv->s_port); + endservent(); + } else { + errx(1, "%s: unknown service", service); + } + } + } + } + if ((cp=strtok(NULL, "!"))) { + errx(1, "%s: junk trailing dial string", cp); + } +} + +int +main(int argc, char **argv) +{ + char buf[256], *opts, *dial = NULL, *mountpt = NULL; + int optlen = 64, port = 0, i; + struct stat stbuf; + struct passwd *pw; + int axess = 0, dotu = 0, uidgid = 0, dev = 0, debug = 0, dryrun = 0; + char *debugstr = NULL, *msize = NULL, *cache = NULL, *aname = NULL; + char *cp, *proto, *addr; + + if (!(opts=calloc(optlen, 1))) { + err(1, "calloc"); + } + while (*++argv) { + if (**argv == '-' && (*argv)[1] != '\0') { + for (cp=*argv+1; *cp; ++cp) { + switch (*cp) { + case 'i': uidgid = 1; break; + case 'n': dryrun = 1; break; + case 's': axess = -1; break; + case 'u': dotu = 1; break; + case 'v': dev = 1; break; + case 'x': axess = getuid(); break; + case 'a': + aname = getarg('a', cp, &argv); + *cp-- = '\0'; /* breaks out of for loop */ + break; + case 'c': + cache = getarg('c', cp, &argv); + *cp-- = '\0'; + break; + case 'd': + debugstr = getarg('d', cp, &argv); + *cp-- = '\0'; + break; + case 'm': + msize = getarg('m', cp, &argv); + *cp-- = '\0'; + break; + } + } + } else if (!dial) { + dial = *argv; + } else if (!mountpt) { + mountpt = *argv; + } else { + errx(1, "%s: too many arguments", *argv); + } + } + + if (!dial || !mountpt) { + errx(1, "usage: 9mount [ -insuvx ] [ -a spec ] [ -c cache ] [ -d debug ] [ -m msize ] dial mountpt"); + } + + if(!(pw=getpwuid(getuid()))) { + err(1, "who are you?? getpwuid failed"); + } + /* Make sure mount exists, is writable, and not sticky */ + if (stat(mountpt, &stbuf) || access(mountpt, W_OK)) { + err(1, "%s", mountpt); + } + if (stbuf.st_mode & S_ISVTX) { + errx(1, "%s: refusing to mount over sticky directory", mountpt); + } + + if (strcmp(dial, "-") == 0) { + proto = "fd"; + addr = "nodev"; + append(&opts, "rfdno=0,wrfdno=1", &optlen); + } else { + parsedial(dial, &proto, &addr, &port); + } + + /* set up mount options */ + append(&opts, proto, &optlen); /* < 2.6.24 */ + snprintf(buf, sizeof(buf), "trans=%s", proto); + append(&opts, buf, &optlen); /* >= 2.6.24 */ + + if (aname) { + if (strchr(aname, ',')) { + errx(1, "%s: spec can't contain commas", aname); + } + snprintf(buf, sizeof(buf), "aname=%s", aname); + append(&opts, buf, &optlen); + } + + if (cache) { + if (strcmp(cache, "loose") != 0) { + errx(1, "%s: unknown cache mode (expecting loose)", cache); + } + snprintf(buf, sizeof(buf), "cache=%s", cache); + append(&opts, buf, &optlen); + } + + if (debugstr) { + for (cp=strtok(debugstr, ","); cp; cp=strtok(NULL, ",")) { + for (i=0; i<nelem(debug_flags); ++i) { + if (strcmp(cp, debug_flags[i].mnemonic) == 0) { + debug |= debug_flags[i].mask; + break; + } + } + if (i >= nelem(debug_flags)) { + errx(1, "%s: unrecognised debug channel", cp); + } + } + snprintf(buf, sizeof(buf), "debug=0x%04x", debug); + append(&opts, buf, &optlen); + } + + if (msize) { + if (strspn(msize, "0123456789") < strlen(msize)) { + errx(1, "%s: msize must be an integer", msize); + } + snprintf(buf, sizeof(buf), "msize=%s", msize); + append(&opts, buf, &optlen); + } + + snprintf(buf, sizeof(buf), "name=%s", pw->pw_name); + append(&opts, buf, &optlen); + + if (getenv("USER")) { + snprintf(buf, sizeof(buf), "uname=%s", getenv("USER")); + } else { + snprintf(buf, sizeof(buf), "uname=%s", pw->pw_name); + } + if (strchr(buf, ',')) { + errx(1, "%s: username can't contain commas", buf+6); + } + append(&opts, buf, &optlen); + + if (axess == -1) { + append(&opts, "access=any", &optlen); + } else if (axess) { + snprintf(buf, sizeof(buf), "access=%d", axess); + append(&opts, buf, &optlen); + } + if (!dotu) { + append(&opts, "noextend", &optlen); + } + if (!dev) { + append(&opts, "nodev", &optlen); + } + if (uidgid) { + snprintf(buf, sizeof(buf), "uid=%d,gid=%d", getuid(), getgid()); + append(&opts, buf, &optlen); /* < 2.6.24 */ + snprintf(buf, sizeof(buf), "dfltuid=%d,dfltgid=%d", getuid(), getgid()); + append(&opts, buf, &optlen); /* >= 2.6.24 */ + } + if (port) { + snprintf(buf, sizeof(buf), "port=%d", port); + append(&opts, buf, &optlen); + } + + if (strcmp(proto, "tcp") == 0) { + struct addrinfo *ai; + int r; + if ((r=getaddrinfo(addr, NULL, NULL, &ai))) { + errx(1, "getaddrinfo: %s", gai_strerror(r)); + } + if ((r=getnameinfo(ai->ai_addr, ai->ai_addrlen, buf, + sizeof(buf), NULL, 0, NI_NUMERICHOST))) { + errx(1, "getnameinfo: %s", gai_strerror(r)); + } + } else { /* unix socket, virtio device or fd transport */ + snprintf(buf, sizeof(buf), "%s", addr); + } + + if(dryrun) { + fprintf(stderr, "mount -t 9p -o %s %s %s\n", opts, buf, mountpt); + } else if (mount(buf, mountpt, "9p", 0, (void*)opts)) { + err(1, "mount"); + } + + return 0; +} |