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