275 lines · 6.5 KB · C
RawDownload
/* © 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;} voidparsedial(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);	}} intmain(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;					default:						errx(1, "unrecognised argument '%c'", *cp);				}			}		} 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;}