libixp

git clone git://oldgit.suckless.org/libixp/
Log | Files | Refs | LICENSE

ixpsrv.c (10407B)


      1 /* Copyright ©2007 Ron Minnich <rminnich at gmail dot com>
      2 /* Copyright ©2007-2010 Kris Maglione <maglione.k@gmail.com>
      3  * See LICENSE file for license details.
      4  */
      5 
      6 /* This is a simple 9P file server which serves a normal filesystem
      7  * hierarchy. While some of the code is from wmii, the server is by
      8  * Ron.
      9  *
     10  * Note: I added an ifdef for Linux vs. BSD for the mount call, so
     11  * this compiles on BSD, but it won't actually run. It should,
     12  * ideally, have the option of not mounting the FS.
     13  *   --Kris
     14  */
     15 #include <assert.h>
     16 #include <dirent.h>
     17 #include <err.h>
     18 #include <errno.h>
     19 #include <fcntl.h>
     20 #include <stdint.h>
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/param.h>
     25 #include <sys/mount.h>
     26 #include <sys/socket.h>
     27 #include <sys/stat.h>
     28 #include <sys/types.h>
     29 #include <time.h>
     30 #include <unistd.h>
     31 
     32 #include <ixp_local.h>
     33 
     34 /* Temporary */
     35 #define fatal(...) ixp_eprint("ixpsrv: fatal: " __VA_ARGS__)
     36 #define debug(...) if(debuglevel) fprintf(stderr, "ixpsrv: " __VA_ARGS__)
     37 
     38 /* Datatypes: */
     39 typedef struct FidAux FidAux;
     40 struct FidAux {
     41 	DIR *dir;
     42 	int fd;
     43 	char name[]; /* c99 */
     44 };
     45 
     46 /* Error messages */
     47 static char
     48 	Enoperm[] = "permission denied",
     49 	Enofile[] = "file not found",
     50 	Ebadvalue[] = "bad value";
     51 /* Macros */
     52 #define QID(t, i) ((vlong)(t))
     53 
     54 /* Global Vars */
     55 static IxpServer server;
     56 static char *user;
     57 static int debuglevel = 0;
     58 
     59 static void fs_open(Ixp9Req *r);
     60 static void fs_walk(Ixp9Req *r);
     61 static void fs_read(Ixp9Req *r);
     62 static void fs_stat(Ixp9Req *r);
     63 static void fs_write(Ixp9Req *r);
     64 static void fs_clunk(Ixp9Req *r);
     65 static void fs_flush(Ixp9Req *r);
     66 static void fs_attach(Ixp9Req *r);
     67 static void fs_create(Ixp9Req *r);
     68 static void fs_remove(Ixp9Req *r);
     69 static void fs_freefid(IxpFid *f);
     70 
     71 Ixp9Srv p9srv = {
     72 	.open=	fs_open,
     73 	.walk=	fs_walk,
     74 	.read=	fs_read,
     75 	.stat=	fs_stat,
     76 	.write=	fs_write,
     77 	.clunk=	fs_clunk,
     78 	.flush=	fs_flush,
     79 	.attach=fs_attach,
     80 	.create=fs_create,
     81 	.remove=fs_remove,
     82 	.freefid=fs_freefid
     83 };
     84 
     85 static void
     86 usage() {
     87 	fprintf(stderr,
     88 		   "usage: %1$s [-a <address>] {create | read | ls [-ld] | remove | write} <file>\n"
     89 		   "       %1$s [-a <address>] xwrite <file> <data>\n"
     90 		   "       %1$s -v\n", argv0);
     91 	exit(1);
     92 }
     93 
     94 
     95 /* Utility Functions */
     96 
     97 static FidAux*
     98 newfidaux(char *name) {
     99 	FidAux *f;
    100 
    101 	f = ixp_emallocz(sizeof(*f) + strlen(name) + 1);
    102 	f->fd = -1;
    103 	strcpy(f->name, name);
    104 	return f;
    105 }
    106 /* is this a dir? */
    107 /* -1 means it ain't anything .. */
    108 static int
    109 isdir(char *path) {
    110 	struct stat buf;
    111 
    112 	if (stat(path, &buf) < 0)
    113 		return -1;
    114 
    115 	return S_ISDIR(buf.st_mode);
    116 }
    117 
    118 /* This should be moved to libixp */
    119 static void
    120 write_buf(Ixp9Req *r, char *buf, uint len) {
    121 
    122 	if(r->ifcall.tread.offset >= len)
    123 		return;
    124 
    125 	len -= r->ifcall.tread.offset;
    126 	if(len > r->ifcall.tread.count)
    127 		len = r->ifcall.tread.count;
    128 	r->ofcall.rread.data = ixp_emalloc(len);
    129 	memcpy(r->ofcall.rread.data, buf + r->ifcall.tread.offset, len);
    130 	r->ofcall.rread.count = len;
    131 }
    132 
    133 
    134 /* This should be moved to libixp */
    135 static void
    136 write_to_buf(Ixp9Req *r, void *buf, uint *len, uint max) {
    137 	uint offset, count;
    138 
    139 //	offset = (r->fid->omode&OAPPEND) ? *len : r->ifcall.tread.offset;
    140 	offset = r->ifcall.tread.offset;
    141 	if(offset > *len || r->ifcall.tread.count == 0) {
    142 		r->ofcall.rread.count = 0;
    143 		return;
    144 	}
    145 
    146 	count = r->ifcall.tread.count;
    147 	if(max && (count > max - offset))
    148 		count = max - offset;
    149 
    150 	*len = offset + count;
    151 
    152 	if(max == 0) {
    153 		*(void **)buf = ixp_erealloc(*(void **)buf, *len + 1);
    154 		buf = *(void **)buf;
    155 	}
    156 
    157 	memcpy((uchar*)buf + offset, r->ifcall.tread.data, count);
    158 	r->ofcall.rread.count = count;
    159 	((char *)buf)[offset+count] = '\0';
    160 }
    161 
    162 static void
    163 dostat(Stat *s, char *name, struct stat *buf) {
    164 
    165 	s->type = 0;
    166 	s->dev = 0;
    167 	s->qid.type = buf->st_mode&S_IFMT;
    168 	s->qid.path = buf->st_ino;
    169 	s->qid.version = 0;
    170 	s->mode = buf->st_mode & 0777;
    171 	if (S_ISDIR(buf->st_mode)) {
    172 		s->mode |= P9_DMDIR;
    173 		s->qid.type |= QTDIR;
    174 	}
    175 	s->atime = buf->st_atime;
    176 	s->mtime = buf->st_mtime;
    177 	s->length = buf->st_size;
    178 	s->name =name;
    179 	s->uid = user;
    180 	s->gid = user;
    181 	s->muid = user;
    182 }
    183 
    184 /* the gnu/linux guys have made a real mess of errno ... don't ask --ron */
    185 /* I agree. --Kris */
    186 void
    187 rerrno(Ixp9Req *r, char *m) {
    188 /*
    189 	char errbuf[128];
    190 	respond(r, strerror_r(errno, errbuf, sizeof(errbuf)));
    191  */
    192 	respond(r, m);
    193 }
    194 
    195 void
    196 fs_attach(Ixp9Req *r) {
    197 
    198 	debug("fs_attach(%p)\n", r);
    199 
    200 	r->fid->qid.type = QTDIR;
    201 	r->fid->qid.path = (uintptr_t)r->fid;
    202 	r->fid->aux = newfidaux("/");
    203 	r->ofcall.rattach.qid = r->fid->qid;
    204 	respond(r, nil);
    205 }
    206 
    207 void
    208 fs_walk(Ixp9Req *r) {
    209 	struct stat buf;
    210 	char *name;
    211 	FidAux *f;
    212 	int i;
    213 	
    214 	debug("fs_walk(%p)\n", r);
    215 
    216 	f = r->fid->aux;
    217 	name = malloc(PATH_MAX);
    218 	strcpy(name, f->name);
    219 	if (stat(name, &buf) < 0){
    220 		respond(r, Enofile);
    221 		return;
    222 	}
    223 
    224 	/* build full path. Stat full path. Done */
    225 	for(i=0; i < r->ifcall.twalk.nwname; i++) {
    226 		strcat(name, "/");
    227 		strcat(name, r->ifcall.twalk.wname[i]);
    228 		if (stat(name, &buf) < 0){
    229 			respond(r, Enofile);
    230 			free(name);
    231 			return;
    232 		}
    233 		r->ofcall.rwalk.wqid[i].type = buf.st_mode&S_IFMT;
    234 		r->ofcall.rwalk.wqid[i].path = buf.st_ino;
    235 	}
    236 
    237 	r->newfid->aux = newfidaux(name);
    238 	r->ofcall.rwalk.nwqid = i;
    239 	free(name);
    240 	respond(r, nil);
    241 }
    242 
    243 void
    244 fs_stat(Ixp9Req *r) {
    245 	struct stat st;
    246 	Stat s;
    247 	IxpMsg m;
    248 	char *name;
    249 	uchar *buf;
    250 	FidAux *f;
    251 	int size;
    252 	
    253 	f = r->fid->aux;
    254 	debug("fs_stat(%p)\n", r);
    255 	debug("fs_stat %s\n", f->name);
    256 
    257 	name = f->name;
    258 	if (stat(name, &st) < 0){
    259 		respond(r, Enofile);
    260 		return;
    261 	}
    262 
    263 	dostat(&s, name, &st);
    264 	r->fid->qid = s.qid;
    265 	r->ofcall.rstat.nstat = size = ixp_sizeof_stat(&s);
    266 
    267 	buf = ixp_emallocz(size);
    268 
    269 	m = ixp_message(buf, size, MsgPack);
    270 	ixp_pstat(&m, &s);
    271 
    272 	r->ofcall.rstat.stat = m.data;
    273 	respond(r, nil);
    274 }
    275 
    276 void
    277 fs_read(Ixp9Req *r) {
    278 	FidAux *f;
    279 	char *buf;
    280 	int n, offset;
    281 	int size;
    282 
    283 	debug("fs_read(%p)\n", r);
    284 
    285 	f = r->fid->aux;
    286 
    287 	if (f->dir) {
    288 		Stat s;
    289 		IxpMsg m;
    290 
    291 		offset = 0;
    292 		size = r->ifcall.tread.count;
    293 		buf = ixp_emallocz(size);
    294 		m = ixp_message((uchar*)buf, size, MsgPack);
    295 
    296 		/* note: we don't really handle lots of things well, so do one thing
    297 		 * at a time 
    298 		 */
    299 		/*for(f=f->next; f; f=f->next) */{
    300 			struct dirent *d;
    301 			struct stat st;
    302 			d = readdir(f->dir);
    303 			if (d) {
    304 				stat(d->d_name, &st);
    305 				dostat(&s, d->d_name, &st);
    306 				n = ixp_sizeof_stat(&s);
    307 				ixp_pstat(&m, &s);
    308 				offset += n;
    309 			} else n = 0;
    310 		}
    311 		r->ofcall.rread.count = n;
    312 		r->ofcall.rread.data = (char*)m.data;
    313 		respond(r, nil);
    314 		return;
    315 	} else {
    316 		r->ofcall.rread.data = ixp_emallocz(r->ifcall.tread.count);
    317 		if (! r->ofcall.rread.data) {
    318 			respond(r, nil);
    319 			return;
    320 		}
    321 		r->ofcall.rread.count = pread(f->fd, r->ofcall.rread.data, r->ifcall.tread.count, r->ifcall.tread.offset);
    322 		if (r->ofcall.rread.count < 0) 
    323 			rerrno(r, Enoperm);
    324 		else
    325 			respond(r, nil);
    326 		return;
    327 	}
    328 
    329 	/* 
    330 	 * This is an assert because this should this should not be called if
    331 	 * the file is not open for reading.
    332 	 */
    333 	assert(!"Read called on an unreadable file");
    334 }
    335 
    336 void
    337 fs_write(Ixp9Req *r) {
    338 	FidAux *f;
    339 
    340 	debug("fs_write(%p)\n", r);
    341 
    342 	if(r->ifcall.twrite.count == 0) {
    343 		respond(r, nil);
    344 		return;
    345 	}
    346 	f = r->fid->aux;
    347 	/*
    348 	 * This is an assert because this function should not be called if
    349 	 * the file is not open for writing.
    350 	 */
    351 	assert(!"Write called on an unwritable file");
    352 }
    353 
    354 void
    355 fs_open(Ixp9Req *r) {
    356 	int dir;
    357 	FidAux *f;
    358 
    359 	debug("fs_open(%p)\n", r);
    360 
    361 	f = r->fid->aux;
    362 	dir = isdir(f->name);
    363 	/* fucking stupid linux -- open dir is a DIR */
    364 
    365 	if (dir) {
    366 		f->dir = opendir(f->name);
    367 		if (! f->dir){
    368 			rerrno(r, Enoperm);
    369 			return;
    370 		}
    371 	} else {
    372 		f->fd = open(f->name, O_RDONLY);
    373 		if (f->fd < 0){
    374 			rerrno(r, Enoperm);
    375 			return;
    376 		}
    377 	}
    378 	respond(r, nil);
    379 }
    380 
    381 
    382 void
    383 fs_create(Ixp9Req *r) {
    384 	debug("fs_create(%p)\n", r);
    385 	respond(r, Enoperm);
    386 }
    387 
    388 void
    389 fs_remove(Ixp9Req *r) {
    390 	debug("fs_remove(%p)\n", r);
    391 	respond(r, Enoperm);
    392 
    393 }
    394 
    395 void
    396 fs_clunk(Ixp9Req *r) {
    397 	int dir;
    398 	FidAux *f;
    399 
    400 	debug("fs_clunk(%p)\n", f);
    401 
    402 	f = r->fid->aux;
    403 	dir = isdir(f->name);
    404 	if (dir) {
    405 		(void) closedir(f->dir);
    406 		f->dir = NULL;
    407 	} else {
    408 		(void) close(f->fd);
    409 		f->fd = -1;
    410 	}
    411 
    412 	respond(r, nil);
    413 }
    414 
    415 void
    416 fs_flush(Ixp9Req *r) {
    417 	debug("fs_flush(%p)\n", r);
    418 	respond(r, nil);
    419 }
    420 
    421 void
    422 fs_freefid(Fid *f) {
    423 	debug("fs_freefid(%p)\n", f);
    424 	free(f->aux);
    425 }
    426 
    427 // mount -t 9p 127.1 /tmp/cache -o port=20006,noextend
    428 /* Yuck. */
    429 #if defined(__linux__)
    430 #  define MF(n) MS_##n
    431 #  define mymount(src, dest, flags, opts) mount(src, dest, "9p", flags, opts)
    432 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
    433 #  define MF(n) MNT_##n
    434 #  define mymount(src, dest, flags, opts) mount("9p", dest, flags, src)
    435 #endif
    436 
    437 static ulong mountflags =
    438 	  MF(NOATIME)
    439 	| MF(NODEV)
    440 	/* | MF(NODIRATIME) */
    441 	| MF(NOEXEC)
    442 	| MF(NOSUID)
    443 	| MF(RDONLY);
    444 
    445 int
    446 getaddr(char *mountaddr, char **ip, char **port) {
    447 	char *cp;
    448 
    449 	if (!mountaddr)
    450 		mountaddr = getenv("XCPU_PARENT");
    451 	if (!mountaddr)
    452 		return -1;
    453 
    454 	cp = mountaddr;
    455 	if (strcmp(cp, "tcp!"))
    456 		cp += 4;
    457 
    458 	*ip = cp;
    459 
    460 	cp = strstr(cp, "!");
    461 	if (cp)
    462 		*port = cp + 1;
    463 	return strtoul(*port, 0, 0);
    464 }
    465 
    466 int
    467 main(int argc, char *argv[]) {
    468 	int fd;
    469 	int ret;
    470 	int domount, mountonly;
    471 	char *mountaddr;
    472 	char *address;
    473 	char *msg;
    474 	IxpConn *acceptor;
    475 
    476 	domount = 0;
    477 	mountonly = 0;
    478 	address = getenv("IXP_ADDRESS");
    479 	mountaddr = nil;
    480 
    481 	ARGBEGIN{
    482 	case 'v':
    483 		printf("%s-" VERSION ", ©2007 Ron Minnich\n", argv0);
    484 		exit(0);
    485 	case 'a':
    486 		address = EARGF(usage());
    487 		break;
    488 	case 'd':
    489 		debuglevel++;
    490 		break;
    491 	case 'm':
    492 		domount++;
    493 		break;
    494 	case 'M':
    495 		mountonly++;
    496 		break;
    497 	default:
    498 		usage();
    499 	}ARGEND;
    500 
    501 	if(!address)
    502 		fatal("$IXP_ADDRESS not set\n");
    503 
    504 	if(!(user = getenv("USER")))
    505 		fatal("$USER not set\n");
    506 
    507 	fd = ixp_announce(address);
    508 	if(fd < 0)
    509 		fatal("%s\n", errstr);
    510 
    511 	/* set up a fake client so we can grap connects. */
    512 	acceptor = ixp_listen(&server, fd, &p9srv, serve_9pcon, NULL);
    513 
    514 	/* we might need to mount ourselves. The bit of complexity is the need to fork so 
    515 	 * we can serve ourselves. We've done the listen so that's ok.
    516 	 */
    517 	if (domount){
    518 		int f = fork();
    519 		if (f < 0)
    520 			errx(1, "fork!");
    521 		if (!f){
    522 			char *addr, *aport;
    523 			int port;
    524 			char options[128];
    525 
    526 			port = getaddr(mountaddr, &addr, &aport);
    527 			sprintf(options, "port=%d,noextend", port);
    528 			if (mymount(addr, "/tmp/cache", mountflags, options) < 0)
    529 				errx(1, "Mount failed");
    530 		}
    531 		
    532 	}
    533 
    534 	if (mountonly)
    535 		exit(0);
    536 
    537 	ixp_serverloop(&server);
    538 	printf("msg %s\n", ixp_errbuf());
    539 	return ret;
    540 }
    541