├── .gitignore ├── util.h ├── lib ├── strecpy.c ├── readn.c ├── read9pmsg.c ├── auth_getkey.c ├── auth_rpc.c ├── convM2D.c ├── convD2M.c ├── auth_proxy.c ├── convM2S.c └── convS2M.c ├── Makefile ├── 9pfs.h ├── util.c ├── readme.md ├── LICENSE ├── 9pfs.1 ├── libc.h ├── fcall.h ├── auth.h ├── 9p.c └── 9pfs.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | void *emalloc(size_t); 2 | void *erealloc(void*, size_t); 3 | void *ereallocarray(void*, size_t, size_t); 4 | void *ecalloc(size_t, size_t); 5 | char *estrdup(const char *); 6 | void dprint(char*, ...); 7 | -------------------------------------------------------------------------------- /lib/strecpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #define nil NULL 6 | 7 | char* 8 | strecpy(char *to, char *e, char *from) 9 | { 10 | if(to >= e) 11 | return to; 12 | to = memccpy(to, from, '\0', e - to); 13 | if(to == nil){ 14 | to = e - 1; 15 | *to = '\0'; 16 | }else{ 17 | to--; 18 | } 19 | return to; 20 | } 21 | -------------------------------------------------------------------------------- /lib/readn.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "../libc.h" 7 | 8 | long 9 | readn(int f, void *av, long n) 10 | { 11 | char *a; 12 | long m, t; 13 | 14 | a = av; 15 | t = 0; 16 | while(t < n){ 17 | m = read(f, a+t, n-t); 18 | if(m <= 0){ 19 | if(t == 0) 20 | return m; 21 | break; 22 | } 23 | t += m; 24 | } 25 | return t; 26 | } 27 | -------------------------------------------------------------------------------- /lib/read9pmsg.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "../libc.h" 6 | #include "../fcall.h" 7 | 8 | int 9 | read9pmsg(int fd, void *abuf, uint n) 10 | { 11 | int m, len; 12 | uchar *buf; 13 | 14 | buf = abuf; 15 | 16 | /* read count */ 17 | m = readn(fd, buf, BIT32SZ); 18 | if(m != BIT32SZ){ 19 | if(m < 0) 20 | return -1; 21 | return 0; 22 | } 23 | 24 | len = GBIT32(buf); 25 | if(len <= BIT32SZ || len > n){ 26 | return -1; 27 | } 28 | len -= BIT32SZ; 29 | m = readn(fd, buf+BIT32SZ, len); 30 | if(m < len) 31 | return 0; 32 | return BIT32SZ+m; 33 | } 34 | -------------------------------------------------------------------------------- /lib/auth_getkey.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include "../libc.h" 9 | 10 | int 11 | auth_getkey(char *params) 12 | { 13 | int pid, s; 14 | pid_t w; 15 | 16 | /* start /factotum to query for a key */ 17 | switch(pid = fork()){ 18 | case -1: 19 | return -1; 20 | case 0: 21 | if(execlp("factotum", "getkey", "-g", params, nil) == -1) 22 | err(1, "Could not exec factotum"); 23 | exit(0); 24 | default: 25 | for(;;){ 26 | w = wait(&s); 27 | if(w == -1) 28 | break; 29 | if(w == pid){ 30 | return WIFEXITED(s) ? 0 : -1; 31 | } 32 | } 33 | } 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN=/usr/local/bin 2 | MAN=/usr/share/man/man1 3 | TARG=9pfs 4 | OBJS=9pfs.o\ 5 | 9p.o\ 6 | util.o\ 7 | lib/strecpy.o\ 8 | lib/convD2M.o\ 9 | lib/convM2D.o\ 10 | lib/convM2S.o\ 11 | lib/convS2M.o\ 12 | lib/read9pmsg.o\ 13 | lib/readn.o\ 14 | lib/auth_proxy.o\ 15 | lib/auth_rpc.o\ 16 | lib/auth_getkey.o 17 | HFILES=9pfs.h\ 18 | auth.h\ 19 | fcall.h\ 20 | util.h\ 21 | libc.h 22 | CC= cc 23 | DEBUG= -g 24 | CFLAGS= -O2 -pipe\ 25 | ${DEBUG} -Wall\ 26 | -D_FILE_OFFSET_BITS=64\ 27 | -DFUSE_USE_VERSION=26\ 28 | -D_GNU_SOURCE 29 | LDFLAGS= 30 | LDADD= -lfuse 31 | 32 | all: ${TARG} 33 | 34 | install: ${TARG} ${TARG}.1 35 | install -s -m 555 -g bin ${TARG} ${BIN} 36 | install -m 444 -g bin ${TARG}.1 ${MAN} 37 | 38 | installman: ${TARG}.1 39 | install -m 444 -g bin ${TARG}.1 ${MAN} 40 | 41 | uninstall: 42 | rm -f ${BIN}/${TARG} 43 | rm -f ${MAN}/${TARG}.1 44 | 45 | uninstallman: 46 | rm -f ${MAN}/${TARG}.1 47 | 48 | ${TARG}: ${OBJS} ${HFILES} 49 | ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD} 50 | 51 | .c.o: 52 | ${CC} -c -o $@ ${CFLAGS} $< 53 | 54 | clean: 55 | rm -f ${TARG} ${OBJS} 56 | -------------------------------------------------------------------------------- /9pfs.h: -------------------------------------------------------------------------------- 1 | enum 2 | { 3 | ROOTFID = 0, 4 | AUTHFID, 5 | PUT = 0, 6 | DEL, 7 | GET, 8 | NHASH = 1009 9 | }; 10 | 11 | #define FDEL ((void*)~0) 12 | 13 | typedef struct FFid FFid; 14 | typedef struct FDir FDir; 15 | 16 | struct FFid 17 | { 18 | FFid *link; 19 | uchar mode; 20 | u32int fid; 21 | Qid qid; 22 | u32int iounit; 23 | u64int offset; 24 | char *path; 25 | }; 26 | 27 | struct FDir 28 | { 29 | FDir *link; 30 | char *path; 31 | Dir *dirs; 32 | long ndirs; 33 | }; 34 | 35 | FILE *logfile; 36 | 37 | FFid *rootfid; 38 | FFid *authfid; 39 | int msize; 40 | int srvfd; 41 | int debug; 42 | 43 | void init9p(); 44 | int _9pversion(u32int); 45 | FFid *_9pauth(u32int, char*, char*); 46 | FFid *_9pattach(u32int, u32int, char*, char*); 47 | FFid *_9pwalk(const char*); 48 | FFid *_9pwalkr(FFid*, char*); 49 | FFid *fidclone(FFid*); 50 | Dir *_9pstat(FFid*); 51 | int _9pwstat(FFid*, Dir*); 52 | int _9pclunk(FFid*); 53 | int _9popen(FFid*); 54 | FFid *_9pcreate(FFid*, char*, int, int); 55 | int _9premove(FFid*); 56 | int _9pread(FFid*, char*, u32int); 57 | int _9pwrite(FFid*, char*, u32int); 58 | long _9pdirread(FFid*, Dir**); 59 | 60 | int dircmp(const void*, const void*); 61 | FDir *lookupdir(const char*, int); 62 | 63 | #define DPRINT(...) \ 64 | do{ \ 65 | if(debug) \ 66 | fprintf(logfile, __VA_ARGS__); \ 67 | }while(0) 68 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | void* 13 | emalloc(size_t size) 14 | { 15 | void *v; 16 | 17 | if((v = malloc(size)) == NULL) 18 | err(1, "emalloc: out of memory"); 19 | memset(v, 0, size); 20 | return v; 21 | } 22 | 23 | 24 | void* 25 | erealloc(void *p, size_t size) 26 | { 27 | void *v; 28 | 29 | if((v = realloc(p, size)) == NULL) 30 | err(1, "emalloc: out of memory"); 31 | return v; 32 | } 33 | 34 | void* 35 | ecalloc(size_t nmemb, size_t size) 36 | { 37 | void *v; 38 | 39 | if((v = calloc(nmemb, size)) == NULL) 40 | err(1, "ecalloc: out of memory"); 41 | return v; 42 | } 43 | 44 | #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) 45 | 46 | void* 47 | ereallocarray(void *optr, size_t nmemb, size_t size) 48 | { 49 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 50 | nmemb > 0 && SIZE_MAX / nmemb < size) { 51 | errno = ENOMEM; 52 | err(1, "erallocarray: out of memory"); 53 | } 54 | return erealloc(optr, size * nmemb); 55 | } 56 | 57 | /* 58 | *void* 59 | *ereallocarray(void *ptr, size_t nmemb, size_t size) 60 | *{ 61 | * void *v; 62 | * 63 | * if((v = reallocarray(ptr, nmemb, size)) == NULL) 64 | * err(1, "ereallocarray: out of memory"); 65 | * return v; 66 | *} 67 | */ 68 | 69 | char* 70 | estrdup(const char *s) 71 | { 72 | char *r; 73 | 74 | if((r = strdup(s)) == NULL) 75 | err(1, "estrdup: out of memory"); 76 | return r; 77 | } 78 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 9pfs mounts a 9P service using the FUSE file system driver. It is a 2 | replacement for 9pfuse from [plan9port](https://swtch.com/plan9port/). 3 | Unlike 9pfuse, it works equally well on Linux, OpenBSD, FreeBSD, and 4 | any other OS with a FUSE implementation. 5 | 6 | It is also faster than 9pfuse. Below is the time it took to run du 7 | -a on the /sys/src directory on a [plan9front](http://9front.org) 8 | installation mounted by 9pfs. 9 | 10 | ``` 11 | $ time du -a >/dev/null 12 | real 0m8.952s 13 | user 0m0.180s 14 | sys 0m0.503s 15 | $ time du -a >/dev/null 16 | real 0m0.421s 17 | user 0m0.017s 18 | sys 0m0.070s 19 | $ time du -a >/dev/null 20 | real 0m0.421s 21 | user 0m0.030s 22 | sys 0m0.060s 23 | ``` 24 | 25 | The first time trial above is slow as 9pfs builds a cache as it reads 26 | directories. The second and third trials are subsequently much faster. 27 | 28 | Below is the time it takes for 9pfuse: 29 | 30 | ``` 31 | $ time du -a >/dev/null 32 | real 1m2.643s 33 | user 0m0.323s 34 | sys 0m0.833s 35 | $ time du -a >/dev/null 36 | real 1m15.849s 37 | user 0m0.287s 38 | sys 0m0.790s 39 | $ time du -a >/dev/null 40 | real 1m20.581s 41 | user 0m0.297s 42 | sys 0m0.753s 43 | ``` 44 | 45 | Installation 46 | ------------ 47 | If you are using OpenBSD, then 48 | ``` 49 | $ make 50 | $ sudo make install 51 | ``` 52 | will perform the installation. If you are using another operating 53 | system of your choice, edit the the BIN and MAN variables of the 54 | Makefile to choose where you want to install the 9pfs binary and 55 | man page, respectively. 56 | -------------------------------------------------------------------------------- /lib/auth_rpc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../libc.h" 11 | #include "../fcall.h" 12 | #include "../9pfs.h" 13 | #include "../auth.h" 14 | #include "../util.h" 15 | 16 | static struct { 17 | char *verb; 18 | int val; 19 | } tab[] = { 20 | { "ok", ARok }, 21 | { "done", ARdone }, 22 | { "error", ARerror }, 23 | { "needkey", ARneedkey }, 24 | { "badkey", ARbadkey }, 25 | { "phase", ARphase }, 26 | { "toosmall", ARtoosmall }, 27 | { "error", ARerror }, 28 | }; 29 | 30 | static int 31 | classify(char *buf, uint n, AuthRpc *rpc) 32 | { 33 | int i, len; 34 | 35 | for(i=0; i= len && memcmp(buf, tab[i].verb, len) == 0 && (n==len || buf[len]==' ')){ 38 | if(n==len){ 39 | rpc->narg = 0; 40 | rpc->arg = ""; 41 | }else{ 42 | rpc->narg = n - (len+1); 43 | rpc->arg = (char*)buf+len+1; 44 | } 45 | return tab[i].val; 46 | } 47 | } 48 | return ARrpcfailure; 49 | } 50 | 51 | AuthRpc* 52 | auth_allocrpc(int afd) 53 | { 54 | AuthRpc *rpc; 55 | 56 | rpc = emalloc(sizeof(*rpc)); 57 | if(rpc == nil) 58 | return nil; 59 | rpc->afd = afd; 60 | return rpc; 61 | } 62 | 63 | void 64 | auth_freerpc(AuthRpc *rpc) 65 | { 66 | free(rpc); 67 | } 68 | 69 | uint 70 | auth_rpc(AuthRpc *rpc, char *verb, void *a, int na) 71 | { 72 | int l, n; 73 | 74 | l = strlen(verb); 75 | if(na+l+1 > AuthRpcMax){ 76 | return ARtoobig; 77 | } 78 | 79 | memmove(rpc->obuf, verb, l); 80 | rpc->obuf[l] = ' '; 81 | memmove(rpc->obuf+l+1, a, na); 82 | if((n=write(rpc->afd, rpc->obuf, l+1+na)) != l+1+na) 83 | return ARrpcfailure; 84 | 85 | if((n=read(rpc->afd, rpc->ibuf, AuthRpcMax)) < 0) 86 | return ARrpcfailure; 87 | rpc->ibuf[n] = '\0'; 88 | return classify(rpc->ibuf, n, rpc); 89 | } 90 | -------------------------------------------------------------------------------- /lib/convM2D.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "../libc.h" 6 | #include "../fcall.h" 7 | 8 | int 9 | statcheck(uchar *buf, uint nbuf) 10 | { 11 | uchar *ebuf; 12 | int i, nstr; 13 | 14 | ebuf = buf + nbuf; 15 | 16 | if(nbuf < STATFIXLEN || nbuf != BIT16SZ + GBIT16(buf)) 17 | return -1; 18 | 19 | buf += STATFIXLEN - 4 * BIT16SZ; 20 | 21 | nstr = 4; 22 | for(i = 0; i < nstr; i++){ 23 | if(buf + BIT16SZ > ebuf) 24 | return -1; 25 | buf += BIT16SZ + GBIT16(buf); 26 | } 27 | 28 | if(buf != ebuf) 29 | return -1; 30 | 31 | return 0; 32 | } 33 | 34 | static char nullstring[] = ""; 35 | 36 | uint 37 | convM2D(uchar *buf, uint nbuf, Dir *d, char *strs) 38 | { 39 | uchar *p, *ebuf; 40 | char *sv[5]; 41 | int i, ns, nstr; 42 | 43 | if(nbuf < STATFIXLEN) 44 | return 0; 45 | 46 | p = buf; 47 | ebuf = buf + nbuf; 48 | 49 | p += BIT16SZ; /* ignore size */ 50 | d->type = GBIT16(p); 51 | p += BIT16SZ; 52 | d->dev = GBIT32(p); 53 | p += BIT32SZ; 54 | d->qid.type = GBIT8(p); 55 | p += BIT8SZ; 56 | d->qid.vers = GBIT32(p); 57 | p += BIT32SZ; 58 | d->qid.path = GBIT64(p); 59 | p += BIT64SZ; 60 | d->mode = GBIT32(p); 61 | p += BIT32SZ; 62 | d->atime = GBIT32(p); 63 | p += BIT32SZ; 64 | d->mtime = GBIT32(p); 65 | p += BIT32SZ; 66 | d->length = GBIT64(p); 67 | p += BIT64SZ; 68 | 69 | nstr = 4; 70 | for(i = 0; i < nstr; i++){ 71 | if(p + BIT16SZ > ebuf) 72 | return 0; 73 | ns = GBIT16(p); 74 | p += BIT16SZ; 75 | if(p + ns > ebuf) 76 | return 0; 77 | if(strs){ 78 | sv[i] = strs; 79 | memmove(strs, p, ns); 80 | strs += ns; 81 | *strs++ = '\0'; 82 | } 83 | p += ns; 84 | } 85 | 86 | if(strs){ 87 | d->name = sv[0]; 88 | d->uid = sv[1]; 89 | d->gid = sv[2]; 90 | d->muid = sv[3]; 91 | }else{ 92 | d->name = nullstring; 93 | d->uid = nullstring; 94 | d->gid = nullstring; 95 | d->muid = nullstring; 96 | } 97 | 98 | return p - buf; 99 | } 100 | -------------------------------------------------------------------------------- /lib/convD2M.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "../libc.h" 6 | #include "../fcall.h" 7 | 8 | uint 9 | sizeD2M(Dir *d) 10 | { 11 | char *sv[5]; 12 | int i, ns, nstr, fixlen; 13 | 14 | sv[0] = d->name; 15 | sv[1] = d->uid; 16 | sv[2] = d->gid; 17 | sv[3] = d->muid; 18 | 19 | fixlen = STATFIXLEN; 20 | nstr = 4; 21 | 22 | ns = 0; 23 | for(i = 0; i < nstr; i++) 24 | if(sv[i]) 25 | ns += strlen(sv[i]); 26 | 27 | return fixlen + ns; 28 | } 29 | 30 | uint 31 | convD2M(Dir *d, uchar *buf, uint nbuf) 32 | { 33 | uchar *p, *ebuf; 34 | char *sv[5]; 35 | int i, ns, nsv[5], ss, nstr, fixlen; 36 | 37 | if(nbuf < BIT16SZ) 38 | return 0; 39 | 40 | p = buf; 41 | ebuf = buf + nbuf; 42 | 43 | sv[0] = d->name; 44 | sv[1] = d->uid; 45 | sv[2] = d->gid; 46 | sv[3] = d->muid; 47 | 48 | fixlen = STATFIXLEN; 49 | nstr = 4; 50 | 51 | ns = 0; 52 | for(i = 0; i < nstr; i++){ 53 | if(sv[i]) 54 | nsv[i] = strlen(sv[i]); 55 | else 56 | nsv[i] = 0; 57 | ns += nsv[i]; 58 | } 59 | 60 | ss = fixlen + ns; 61 | 62 | /* set size befor erroring, so user can know how much is needed */ 63 | /* note that length excludes count field itself */ 64 | PBIT16(p, ss-BIT16SZ); 65 | p += BIT16SZ; 66 | 67 | if(ss > nbuf) 68 | return BIT16SZ; 69 | 70 | PBIT16(p, d->type); 71 | p += BIT16SZ; 72 | PBIT32(p, d->dev); 73 | p += BIT32SZ; 74 | PBIT8(p, d->qid.type); 75 | p += BIT8SZ; 76 | PBIT32(p, d->qid.vers); 77 | p += BIT32SZ; 78 | PBIT64(p, d->qid.path); 79 | p += BIT64SZ; 80 | PBIT32(p, d->mode); 81 | p += BIT32SZ; 82 | PBIT32(p, d->atime); 83 | p += BIT32SZ; 84 | PBIT32(p, d->mtime); 85 | p += BIT32SZ; 86 | PBIT64(p, d->length); 87 | p += BIT64SZ; 88 | 89 | for(i = 0; i < nstr; i++){ 90 | ns = nsv[i]; 91 | if(p + ns + BIT16SZ > ebuf) 92 | return 0; 93 | PBIT16(p, ns); 94 | p += BIT16SZ; 95 | if(ns) 96 | memmove(p, sv[i], ns); 97 | p += ns; 98 | } 99 | 100 | if(ss != p - buf) 101 | return 0; 102 | 103 | return p - buf; 104 | } 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | All original work is released under the following license: 2 | 3 | * Copyright (c) 2015 Benjamin Scher Purcell 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | The work in the lib directory is derived from plan9front and is owned 18 | by: 19 | 20 | * Copyright © 2002 Lucent Technologies Inc. 21 | * All Rights Reserved 22 | 23 | and released under the terms of the Lucent Public License Version 1.02 24 | (see: http://9front.org/9front/lib/legal/lpl). 25 | 26 | The method "ereallocarray" is derived from OpenBSD "reallocarray" and 27 | is governed by the following license: 28 | 29 | * Copyright (c) 2008 Otto Moerbeek 30 | * 31 | * Permission to use, copy, modify, and distribute this software for any 32 | * purpose with or without fee is hereby granted, provided that the above 33 | * copyright notice and this permission notice appear in all copies. 34 | * 35 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 36 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 37 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 38 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 39 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 40 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 41 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42 | -------------------------------------------------------------------------------- /9pfs.1: -------------------------------------------------------------------------------- 1 | .Dd $Mdocdate: August 24 2015 $ 2 | .Dt 9PFS 1 3 | .Os 4 | .Sh NAME 5 | .Nm 9pfs 6 | .Nd mount 9P service via FUSE 7 | .Sh SYNOPSIS 8 | .Nm 9pfs 9 | .Bk -words 10 | .Op Fl anU 11 | .Op Fl A Ar aname 12 | .Op Fl p Ar port 13 | .Op Fl u Ar user 14 | .Ar service 15 | .Ar mtpt 16 | .Ek 17 | .Sh DESCRIPTION 18 | .Nm 9pfs 19 | mounts the 9P service running at 20 | .Ar service 21 | onto 22 | .Ar mtpt 23 | using the FUSE user-level file system driver. 24 | .Pp 25 | The following options are available: 26 | .Bl -tag -width Ds 27 | .It Fl a 28 | Negotiate authentication using a local factotum. See 29 | .Sx ENVIRONMENT 30 | and 31 | .Sx EXAMPLES 32 | for more details. 33 | .It Fl n 34 | Do not negotiate authentication. This is the 35 | default. 36 | .It Fl U 37 | The 9P service is a UNIX-domain 38 | socket. 39 | .It Fl A Ar aname 40 | The attach name to send to the 9P service. 41 | .It Fl p Ar port 42 | Specifies the connection port for the 9P service. 43 | The default is 564. 44 | .It Fl u Ar user 45 | Specifies the username to use on authentication 46 | and attach to the 9P service. 47 | .El 48 | .Pp 49 | 9pfs caches directories it reads which will cause 50 | inconsistencies if a different 9P client is connected 51 | to and making changes to the same 9P service. There 52 | is a virtual control file 53 | .Ql .fscache 54 | in each directory served by 9pfs. 55 | Reading or writing anything to that file will clear 56 | the cache for that directory. 57 | .Sh ENVIRONMENT 58 | .Bl -tag -width FACTOTUM 59 | .It Ev FACTOTUM 60 | Authentication requires that the 9P service 61 | provided by a factotum 62 | .Po see 63 | .Lk http://man.cat-v.org/9front/4/factotum 64 | .Pc 65 | is mounted at the path specified by the 66 | .Ev FACTOTUM 67 | environment variable. Plan 9 from User Space 68 | .Po 69 | .Lk https://swtch.com/plan9port/ 70 | .Pc 71 | provides a port of factotum to 72 | UNIX-like operating systems. 73 | See the 74 | .Sx EXAMPLES 75 | section below for details. 76 | .El 77 | .Sh EXAMPLES 78 | The following will mount a remote 9P service 79 | with authentication, so long as plan9port is 80 | installed. 81 | .Pp 82 | .Dl $ factotum -n 83 | .Dl $ 9pfs -U `namespace`/factotum mnt/factotum 84 | .Dl $ export FACTOTUM=`pwd`/mnt/factotum 85 | .Dl $ 9pfs -a spew.net mnt/spew 86 | .Dl !adding key: role=client proto=p9sk1 dom=spew 87 | .Dl user[glenda]: 88 | .Dl password: 89 | .Dl ! 90 | .Pp 91 | The dom for authentication will need 92 | to assigned an IP address via /etc/hosts or your 93 | plan9port $PLAN9/ndb/local file. The utilities 94 | .Ar factotum 95 | and 96 | .Ar namespace 97 | are from Russ Cox's 98 | .Lk https://swtch.com/plan9port/ plan9port 99 | .Sh BUGS 100 | Files of 0 reported size are not read correctly under Linux. This 101 | prevents 9pfs from mounting a useful factotum. For a 102 | work-around, use 9pfuse to mount factotum and then proceed with 103 | 9pfs authentication as usual. 104 | .Pp 105 | OpenBSD does not display all the files in a very large 106 | directory. 107 | .Pp 108 | Moving a file to a different directory is not implemented. 109 | To do so, copy the file and then remove it. 110 | .Pp 111 | You cannot access or create a file on the 9P server 112 | named .fscache in any directory. 113 | -------------------------------------------------------------------------------- /libc.h: -------------------------------------------------------------------------------- 1 | #define nil NULL 2 | 3 | #define nelem(x) (sizeof(x)/sizeof((x)[0])) 4 | 5 | #define ERRMAX 256 6 | #define OREAD 0 /* open for read */ 7 | #define OWRITE 1 /* write */ 8 | #define ORDWR 2 /* read and write */ 9 | #define OEXEC 3 /* execute, == read but check execute permission */ 10 | #define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ 11 | #define OCEXEC 32 /* or'ed in, close on exec */ 12 | #define ORCLOSE 64 /* or'ed in, remove on close */ 13 | #define ODIRECT 128 /* or'ed in, direct access */ 14 | #define ONONBLOCK 256 /* or'ed in, non-blocking call */ 15 | #define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ 16 | #define OLOCK 0x2000 /* or'ed in, lock after opening */ 17 | #define OAPPEND 0x4000 /* or'ed in, append only */ 18 | 19 | /* bits in Qid.type */ 20 | #define QTDIR 0x80 /* type bit for directories */ 21 | #define QTAPPEND 0x40 /* type bit for append only files */ 22 | #define QTEXCL 0x20 /* type bit for exclusive use files */ 23 | #define QTMOUNT 0x10 /* type bit for mounted channel */ 24 | #define QTAUTH 0x08 /* type bit for authentication file */ 25 | #define QTTMP 0x04 /* type bit for non-backed-up file */ 26 | #define QTSYMLINK 0x02 /* type bit for symbolic link */ 27 | #define QTFILE 0x00 /* type bits for plain file */ 28 | 29 | /* bits in Dir.mode */ 30 | #define DMDIR 0x80000000 /* mode bit for directories */ 31 | #define DMAPPEND 0x40000000 /* mode bit for append only files */ 32 | #define DMEXCL 0x20000000 /* mode bit for exclusive use files */ 33 | #define DMMOUNT 0x10000000 /* mode bit for mounted channel */ 34 | #define DMAUTH 0x08000000 /* mode bit for authentication file */ 35 | #define DMTMP 0x04000000 /* mode bit for non-backed-up file */ 36 | #define DMSYMLINK 0x02000000 /* mode bit for symbolic link (Unix, 9P2000.u) */ 37 | #define DMDEVICE 0x00800000 /* mode bit for device file (Unix, 9P2000.u) */ 38 | #define DMNAMEDPIPE 0x00200000 /* mode bit for named pipe (Unix, 9P2000.u) */ 39 | #define DMSOCKET 0x00100000 /* mode bit for socket (Unix, 9P2000.u) */ 40 | #define DMSETUID 0x00080000 /* mode bit for setuid (Unix, 9P2000.u) */ 41 | #define DMSETGID 0x00040000 /* mode bit for setgid (Unix, 9P2000.u) */ 42 | 43 | #define DMREAD 0x4 /* mode bit for read permission */ 44 | #define DMWRITE 0x2 /* mode bit for write permission */ 45 | #define DMEXEC 0x1 /* mode bit for execute permission */ 46 | 47 | #define STATMAX 65535U /* max length of machine-independent stat structure */ 48 | #define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ 49 | 50 | typedef uint64_t u64int; 51 | typedef int64_t s64int; 52 | typedef uint8_t u8int; 53 | typedef int8_t s8int; 54 | typedef uint16_t u16int; 55 | typedef int16_t s16int; 56 | typedef uintptr_t uintptr; 57 | typedef intptr_t intptr; 58 | typedef uint32_t u32int; 59 | typedef int32_t s32int; 60 | typedef unsigned char uchar; 61 | typedef unsigned long long uvlong; 62 | typedef long long vlong; 63 | 64 | typedef 65 | struct Qid 66 | { 67 | uvlong path; 68 | ulong vers; 69 | uchar type; 70 | } Qid; 71 | 72 | typedef 73 | struct Dir { 74 | /* system-modified data */ 75 | ushort type; /* server type */ 76 | uint dev; /* server subtype */ 77 | /* file data */ 78 | Qid qid; /* unique id from server */ 79 | ulong mode; /* permissions */ 80 | ulong atime; /* last read time */ 81 | ulong mtime; /* last write time */ 82 | vlong length; /* file length */ 83 | char *name; /* last element of path */ 84 | char *uid; /* owner name */ 85 | char *gid; /* group name */ 86 | char *muid; /* last modifier name */ 87 | } Dir; 88 | 89 | char *strecpy(char*, char*, char*); 90 | long readn(int, void*, long); 91 | int fauth(int fd, char *aname); 92 | -------------------------------------------------------------------------------- /fcall.h: -------------------------------------------------------------------------------- 1 | #define VERSION9P "9P2000" 2 | #define MAXWELEM 16 3 | 4 | typedef 5 | struct Fcall 6 | { 7 | uchar type; 8 | u32int fid; 9 | ushort tag; 10 | u32int msize; /* Tversion, Rversion */ 11 | char *version; /* Tversion, Rversion */ 12 | ushort oldtag; /* Tflush */ 13 | char *ename; /* Rerror */ 14 | Qid qid; /* Rattach, Ropen, Rcreate */ 15 | u32int iounit; /* Ropen, Rcreate */ 16 | Qid aqid; /* Rauth */ 17 | u32int afid; /* Tauth, Tattach */ 18 | char *uname; /* Tauth, Tattach */ 19 | char *aname; /* Tauth, Tattach */ 20 | u32int perm; /* Tcreate */ 21 | char *name; /* Tcreate */ 22 | uchar mode; /* Tcreate, Topen */ 23 | u32int newfid; /* Twalk */ 24 | ushort nwname; /* Twalk */ 25 | char *wname[MAXWELEM]; /* Twalk */ 26 | ushort nwqid; /* Rwalk */ 27 | Qid wqid[MAXWELEM]; /* Rwalk */ 28 | vlong offset; /* Tread, Twrite */ 29 | u32int count; /* Tread, Twrite, Rread */ 30 | char *data; /* Twrite, Rread */ 31 | ushort nstat; /* Twstat, Rstat */ 32 | uchar *stat; /* Twstat, Rstat */ 33 | int unixfd; /* Ropenfd */ 34 | 35 | /* 9P2000.u extensions */ 36 | int errornum; /* Rerror */ 37 | int uidnum; /* Tattach, Tauth */ 38 | char *extension; /* Tcreate */ 39 | } Fcall; 40 | 41 | 42 | #define GBIT8(p) ((p)[0]) 43 | #define GBIT16(p) ((p)[0]|((p)[1]<<8)) 44 | #define GBIT32(p) ((u32int)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))) 45 | #define GBIT64(p) ((u32int)((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24)) |\ 46 | ((vlong)((p)[4]|((p)[5]<<8)|((p)[6]<<16)|((p)[7]<<24)) << 32)) 47 | 48 | #define PBIT8(p,v) (p)[0]=(v) 49 | #define PBIT16(p,v) (p)[0]=(v);(p)[1]=(v)>>8 50 | #define PBIT32(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24 51 | #define PBIT64(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24;\ 52 | (p)[4]=(v)>>32;(p)[5]=(v)>>40;(p)[6]=(v)>>48;(p)[7]=(v)>>56 53 | 54 | #define BIT8SZ 1 55 | #define BIT16SZ 2 56 | #define BIT32SZ 4 57 | #define BIT64SZ 8 58 | #define QIDSZ (BIT8SZ+BIT32SZ+BIT64SZ) 59 | 60 | /* STATFIXLEN includes leading 16-bit count */ 61 | /* The count, however, excludes itself; total size is BIT16SZ+count */ 62 | #define STATFIXLEN (BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ) /* amount of fixed length data in a stat buffer */ 63 | #define STATFIXLENU (STATFIXLEN+BIT16SZ+3*BIT32SZ) /* for 9P2000.u */ 64 | 65 | #define NOTAG (ushort)~0U /* Dummy tag */ 66 | #define NOFID (u32int)~0U /* Dummy fid */ 67 | #define NOUID (-1) /* Dummy uid */ 68 | #define IOHDRSZ 24 /* ample room for Twrite/Rread header (iounit) */ 69 | 70 | enum 71 | { 72 | Tversion = 100, 73 | Rversion, 74 | Tauth = 102, 75 | Rauth, 76 | Tattach = 104, 77 | Rattach, 78 | Terror = 106, /* illegal */ 79 | Rerror, 80 | Tflush = 108, 81 | Rflush, 82 | Twalk = 110, 83 | Rwalk, 84 | Topen = 112, 85 | Ropen, 86 | Tcreate = 114, 87 | Rcreate, 88 | Tread = 116, 89 | Rread, 90 | Twrite = 118, 91 | Rwrite, 92 | Tclunk = 120, 93 | Rclunk, 94 | Tremove = 122, 95 | Rremove, 96 | Tstat = 124, 97 | Rstat, 98 | Twstat = 126, 99 | Rwstat, 100 | Tmax, 101 | 102 | Topenfd = 98, 103 | Ropenfd 104 | }; 105 | 106 | uint convM2S(uchar*, uint, Fcall*); 107 | uint convS2M(Fcall*, uchar*, uint); 108 | uint sizeS2M(Fcall*); 109 | 110 | int statcheck(uchar *abuf, uint nbuf); 111 | uint convM2D(uchar*, uint, Dir*, char*); 112 | uint convD2M(Dir*, uchar*, uint); 113 | uint sizeD2M(Dir*); 114 | 115 | uint convM2Su(uchar*, uint, Fcall*, int); 116 | uint convS2Mu(Fcall*, uchar*, uint, int); 117 | uint sizeS2Mu(Fcall*, int); 118 | 119 | int statchecku(uchar *abuf, uint nbuf, int); 120 | uint convM2Du(uchar*, uint, Dir*, char*, int); 121 | uint convD2Mu(Dir*, uchar*, uint, int); 122 | uint sizeD2Mu(Dir*, int); 123 | 124 | int read9pmsg(int, void*, uint); 125 | -------------------------------------------------------------------------------- /auth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface for typical callers. 3 | */ 4 | 5 | typedef struct AuthInfo AuthInfo; 6 | typedef struct Chalstate Chalstate; 7 | typedef struct Chapreply Chapreply; 8 | typedef struct MSchapreply MSchapreply; 9 | typedef struct UserPasswd UserPasswd; 10 | typedef struct AuthRpc AuthRpc; 11 | 12 | enum 13 | { 14 | MAXCHLEN= 256, /* max challenge length */ 15 | MAXNAMELEN= 256, /* maximum name length */ 16 | MD5LEN= 16, 17 | 18 | ARok = 0, /* rpc return values */ 19 | ARdone, 20 | ARerror, 21 | ARneedkey, 22 | ARbadkey, 23 | ARwritenext, 24 | ARtoosmall, 25 | ARtoobig, 26 | ARrpcfailure, 27 | ARphase, 28 | 29 | AuthRpcMax = 4096 30 | }; 31 | 32 | struct AuthRpc 33 | { 34 | int afd; 35 | char ibuf[AuthRpcMax+1]; 36 | char obuf[AuthRpcMax]; 37 | char *arg; 38 | uint narg; 39 | }; 40 | 41 | struct AuthInfo 42 | { 43 | char *cuid; /* caller id */ 44 | char *suid; /* server id */ 45 | char *cap; /* capability (only valid on server side) */ 46 | int nsecret; /* length of secret */ 47 | uchar *secret; /* secret */ 48 | }; 49 | 50 | struct Chalstate 51 | { 52 | char *user; 53 | char chal[MAXCHLEN]; 54 | int nchal; 55 | void *resp; 56 | int nresp; 57 | 58 | /* for implementation only */ 59 | int afd; 60 | AuthRpc *rpc; /* to factotum */ 61 | char userbuf[MAXNAMELEN]; /* temp space if needed */ 62 | int userinchal; /* user was sent to obtain challenge */ 63 | }; 64 | 65 | struct Chapreply /* for protocol "chap" */ 66 | { 67 | uchar id; 68 | char resp[MD5LEN]; 69 | }; 70 | 71 | struct MSchapreply /* for protocol "mschap" */ 72 | { 73 | char LMresp[24]; /* Lan Manager response */ 74 | char NTresp[24]; /* NT response */ 75 | }; 76 | 77 | struct UserPasswd 78 | { 79 | char *user; 80 | char *passwd; 81 | }; 82 | 83 | extern int newns(char*, char*); 84 | extern int addns(char*, char*); 85 | 86 | extern int noworld(char*); 87 | extern int amount(int, char*, int, char*); 88 | 89 | /* these two may get generalized away -rsc */ 90 | extern int login(char*, char*, char*); 91 | extern int httpauth(char*, char*); 92 | 93 | typedef struct Attr Attr; 94 | enum { 95 | AttrNameval, /* name=val -- when matching, must have name=val */ 96 | AttrQuery, /* name? -- when matching, must be present */ 97 | AttrDefault /* name:=val -- when matching, if present must match INTERNAL */ 98 | }; 99 | struct Attr 100 | { 101 | int type; 102 | Attr *next; 103 | char *name; 104 | char *val; 105 | }; 106 | 107 | typedef int AuthGetkey(char*); 108 | 109 | Attr *_copyattr(Attr*); 110 | Attr *_delattr(Attr*, char*); 111 | Attr *_findattr(Attr*, char*); 112 | void _freeattr(Attr*); 113 | Attr *_mkattr(int, char*, char*, Attr*); 114 | Attr *_parseattr(char*); 115 | char *_strfindattr(Attr*, char*); 116 | 117 | extern AuthInfo* fauth_proxy(FFid*, AuthRpc *rpc, AuthGetkey *getkey, char *params); 118 | extern AuthInfo* auth_proxy(FFid*, AuthGetkey *getkey, char *fmt, ...); 119 | extern int auth_getkey(char*); 120 | extern int (*amount_getkey)(char*); 121 | extern void auth_freeAI(AuthInfo *ai); 122 | extern int auth_chuid(AuthInfo *ai, char *ns); 123 | extern Chalstate *auth_challenge(char*, ...); 124 | extern AuthInfo* auth_response(Chalstate*); 125 | extern int auth_respond(void*, uint, char*, uint, void*, uint, AuthGetkey *getkey, char*, ...); 126 | extern void auth_freechal(Chalstate*); 127 | extern AuthInfo* auth_userpasswd(char *user, char *passwd); 128 | extern UserPasswd* auth_getuserpasswd(AuthGetkey *getkey, char*, ...); 129 | extern AuthInfo* auth_getinfo(AuthRpc *rpc); 130 | extern AuthRpc* auth_allocrpc(int afd); 131 | extern Attr* auth_attr(AuthRpc *rpc); 132 | extern void auth_freerpc(AuthRpc *rpc); 133 | extern uint auth_rpc(AuthRpc *rpc, char *verb, void *a, int n); 134 | extern int auth_wep(char*, char*, ...); 135 | -------------------------------------------------------------------------------- /lib/auth_proxy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../libc.h" 13 | #include "../fcall.h" 14 | #include "../9pfs.h" 15 | #include "../auth.h" 16 | #include "../util.h" 17 | 18 | enum { 19 | ARgiveup = 100, 20 | }; 21 | 22 | static uchar* 23 | gstring(uchar *p, uchar *ep, char **s) 24 | { 25 | uint n; 26 | 27 | if(p == nil) 28 | return nil; 29 | if(p+BIT16SZ > ep) 30 | return nil; 31 | n = GBIT16(p); 32 | p += BIT16SZ; 33 | if(p+n > ep) 34 | return nil; 35 | *s = emalloc(n+1); 36 | memmove((*s), p, n); 37 | (*s)[n] = '\0'; 38 | p += n; 39 | return p; 40 | } 41 | 42 | static uchar* 43 | gcarray(uchar *p, uchar *ep, uchar **s, int *np) 44 | { 45 | uint n; 46 | 47 | if(p == nil) 48 | return nil; 49 | if(p+BIT16SZ > ep) 50 | return nil; 51 | n = GBIT16(p); 52 | p += BIT16SZ; 53 | if(p+n > ep) 54 | return nil; 55 | *s = emalloc(n); 56 | if(*s == nil) 57 | return nil; 58 | memmove((*s), p, n); 59 | *np = n; 60 | p += n; 61 | return p; 62 | } 63 | 64 | void 65 | auth_freeAI(AuthInfo *ai) 66 | { 67 | if(ai == nil) 68 | return; 69 | free(ai->cuid); 70 | free(ai->suid); 71 | free(ai->cap); 72 | free(ai->secret); 73 | free(ai); 74 | } 75 | 76 | static uchar* 77 | convM2AI(uchar *p, int n, AuthInfo **aip) 78 | { 79 | uchar *e = p+n; 80 | AuthInfo *ai; 81 | 82 | ai = emalloc(sizeof(*ai)); 83 | if(ai == nil) 84 | return nil; 85 | 86 | p = gstring(p, e, &ai->cuid); 87 | p = gstring(p, e, &ai->suid); 88 | p = gstring(p, e, &ai->cap); 89 | p = gcarray(p, e, &ai->secret, &ai->nsecret); 90 | if(p == nil) 91 | auth_freeAI(ai); 92 | else 93 | *aip = ai; 94 | return p; 95 | } 96 | 97 | AuthInfo* 98 | auth_getinfo(AuthRpc *rpc) 99 | { 100 | AuthInfo *a; 101 | 102 | if(auth_rpc(rpc, "authinfo", nil, 0) != ARok) 103 | return nil; 104 | if(convM2AI((uchar*)rpc->arg, rpc->narg, &a) == nil){ 105 | return nil; 106 | } 107 | return a; 108 | } 109 | 110 | static int 111 | dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey) 112 | { 113 | int ret; 114 | 115 | for(;;){ 116 | if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey) 117 | return ret; 118 | if(getkey == nil) 119 | return ARgiveup; /* don't know how */ 120 | if((*getkey)(rpc->arg) < 0) 121 | return ARgiveup; /* user punted */ 122 | } 123 | } 124 | 125 | /* 126 | * this just proxies what the factotum tells it to. 127 | */ 128 | AuthInfo* 129 | fauth_proxy(FFid *f, AuthRpc *rpc, AuthGetkey *getkey, char *params) 130 | { 131 | char *buf; 132 | int m, n, ret; 133 | AuthInfo *a; 134 | 135 | if(rpc == nil){ 136 | return nil; 137 | } 138 | 139 | if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok){ 140 | return nil; 141 | } 142 | 143 | buf = emalloc(AuthRpcMax); 144 | if(buf == nil) 145 | return nil; 146 | for(;;){ 147 | switch(dorpc(rpc, "read", nil, 0, getkey)){ 148 | case ARdone: 149 | free(buf); 150 | a = auth_getinfo(rpc); 151 | return a; 152 | case ARok: 153 | if(_9pwrite(f, rpc->arg, rpc->narg) != rpc->narg){ 154 | goto Error; 155 | } 156 | break; 157 | case ARphase: 158 | n = 0; 159 | memset(buf, 0, AuthRpcMax); 160 | while((ret = dorpc(rpc, "write", buf, n, getkey)) == ARtoosmall){ 161 | m = atoi(rpc->arg); 162 | if(m <= n || m > AuthRpcMax) 163 | break; 164 | m = _9pread(f, buf + n, m - n); 165 | if(m <= 0) 166 | goto Error; 167 | n += m; 168 | } 169 | if(ret != ARok) 170 | goto Error; 171 | break; 172 | default: 173 | goto Error; 174 | } 175 | } 176 | Error: 177 | free(buf); 178 | return nil; 179 | } 180 | 181 | AuthInfo* 182 | auth_proxy(FFid *f, AuthGetkey *getkey, char *fmt, ...) 183 | { 184 | int afd; 185 | char *p, *ftm, *rpcpath; 186 | va_list arg; 187 | AuthInfo *ai; 188 | AuthRpc *rpc; 189 | 190 | va_start(arg, fmt); 191 | vasprintf(&p, fmt, arg); 192 | va_end(arg); 193 | 194 | ai = nil; 195 | ftm = getenv("FACTOTUM"); 196 | asprintf(&rpcpath, "%s/rpc", ftm); 197 | afd = open(rpcpath, ORDWR); 198 | if(afd < 0){ 199 | free(p); 200 | free(rpcpath); 201 | return nil; 202 | } 203 | 204 | rpc = auth_allocrpc(afd); 205 | if(rpc){ 206 | ai = fauth_proxy(f, rpc, getkey, p); 207 | auth_freerpc(rpc); 208 | } 209 | close(afd); 210 | free(p); 211 | free(rpcpath); 212 | return ai; 213 | } 214 | -------------------------------------------------------------------------------- /lib/convM2S.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "../libc.h" 6 | #include "../fcall.h" 7 | 8 | static 9 | uchar* 10 | gstring(uchar *p, uchar *ep, char **s) 11 | { 12 | uint n; 13 | 14 | if(p+BIT16SZ > ep) 15 | return nil; 16 | n = GBIT16(p); 17 | p += BIT16SZ - 1; 18 | if(p+n+1 > ep) 19 | return nil; 20 | /* move it down, on top of count, to make room for '\0' */ 21 | memmove(p, p + 1, n); 22 | p[n] = '\0'; 23 | *s = (char*)p; 24 | p += n+1; 25 | return p; 26 | } 27 | 28 | static 29 | uchar* 30 | gqid(uchar *p, uchar *ep, Qid *q) 31 | { 32 | if(p+QIDSZ > ep) 33 | return nil; 34 | q->type = GBIT8(p); 35 | p += BIT8SZ; 36 | q->vers = GBIT32(p); 37 | p += BIT32SZ; 38 | q->path = GBIT64(p); 39 | p += BIT64SZ; 40 | return p; 41 | } 42 | 43 | /* 44 | * no syntactic checks. 45 | * three causes for error: 46 | * 1. message size field is incorrect 47 | * 2. input buffer too short for its own data (counts too long, etc.) 48 | * 3. too many names or qids 49 | * gqid() and gstring() return nil if they would reach beyond buffer. 50 | * main switch statement checks range and also can fall through 51 | * to test at end of routine. 52 | */ 53 | uint 54 | convM2Su(uchar *ap, uint nap, Fcall *f, int dotu) 55 | { 56 | uchar *p, *ep; 57 | uint i, size; 58 | 59 | p = ap; 60 | ep = p + nap; 61 | 62 | if(p+BIT32SZ+BIT8SZ+BIT16SZ > ep) 63 | return 0; 64 | size = GBIT32(p); 65 | p += BIT32SZ; 66 | 67 | if(size < BIT32SZ+BIT8SZ+BIT16SZ) 68 | return 0; 69 | 70 | f->type = GBIT8(p); 71 | p += BIT8SZ; 72 | f->tag = GBIT16(p); 73 | p += BIT16SZ; 74 | 75 | switch(f->type) 76 | { 77 | default: 78 | return 0; 79 | 80 | case Tversion: 81 | if(p+BIT32SZ > ep) 82 | return 0; 83 | f->msize = GBIT32(p); 84 | p += BIT32SZ; 85 | p = gstring(p, ep, &f->version); 86 | break; 87 | 88 | case Tflush: 89 | if(p+BIT16SZ > ep) 90 | return 0; 91 | f->oldtag = GBIT16(p); 92 | p += BIT16SZ; 93 | break; 94 | 95 | case Tauth: 96 | if(p+BIT32SZ > ep) 97 | return 0; 98 | f->afid = GBIT32(p); 99 | p += BIT32SZ; 100 | p = gstring(p, ep, &f->uname); 101 | if(p == nil) 102 | break; 103 | p = gstring(p, ep, &f->aname); 104 | if(p == nil) 105 | break; 106 | f->uidnum = NOUID; 107 | if(dotu){ 108 | if(p+BIT32SZ > ep) 109 | return 0; 110 | f->uidnum = GBIT32(p); 111 | p += BIT32SZ; 112 | } 113 | break; 114 | 115 | case Tattach: 116 | if(p+BIT32SZ > ep) 117 | return 0; 118 | f->fid = GBIT32(p); 119 | p += BIT32SZ; 120 | if(p+BIT32SZ > ep) 121 | return 0; 122 | f->afid = GBIT32(p); 123 | p += BIT32SZ; 124 | p = gstring(p, ep, &f->uname); 125 | if(p == nil) 126 | break; 127 | p = gstring(p, ep, &f->aname); 128 | if(p == nil) 129 | break; 130 | f->uidnum = NOUID; 131 | if(dotu){ 132 | if(p+BIT32SZ > ep) 133 | return 0; 134 | f->uidnum = GBIT32(p); 135 | p += BIT32SZ; 136 | } 137 | break; 138 | 139 | case Twalk: 140 | if(p+BIT32SZ+BIT32SZ+BIT16SZ > ep) 141 | return 0; 142 | f->fid = GBIT32(p); 143 | p += BIT32SZ; 144 | f->newfid = GBIT32(p); 145 | p += BIT32SZ; 146 | f->nwname = GBIT16(p); 147 | p += BIT16SZ; 148 | if(f->nwname > MAXWELEM) 149 | return 0; 150 | for(i=0; inwname; i++){ 151 | p = gstring(p, ep, &f->wname[i]); 152 | if(p == nil) 153 | break; 154 | } 155 | break; 156 | 157 | case Topen: 158 | case Topenfd: 159 | if(p+BIT32SZ+BIT8SZ > ep) 160 | return 0; 161 | f->fid = GBIT32(p); 162 | p += BIT32SZ; 163 | f->mode = GBIT8(p); 164 | p += BIT8SZ; 165 | break; 166 | 167 | case Tcreate: 168 | if(p+BIT32SZ > ep) 169 | return 0; 170 | f->fid = GBIT32(p); 171 | p += BIT32SZ; 172 | p = gstring(p, ep, &f->name); 173 | if(p == nil) 174 | break; 175 | if(p+BIT32SZ+BIT8SZ > ep) 176 | return 0; 177 | f->perm = GBIT32(p); 178 | p += BIT32SZ; 179 | f->mode = GBIT8(p); 180 | p += BIT8SZ; 181 | if(dotu) 182 | p = gstring(p, ep, &f->extension); 183 | break; 184 | 185 | case Tread: 186 | if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) 187 | return 0; 188 | f->fid = GBIT32(p); 189 | p += BIT32SZ; 190 | f->offset = GBIT64(p); 191 | p += BIT64SZ; 192 | f->count = GBIT32(p); 193 | p += BIT32SZ; 194 | break; 195 | 196 | case Twrite: 197 | if(p+BIT32SZ+BIT64SZ+BIT32SZ > ep) 198 | return 0; 199 | f->fid = GBIT32(p); 200 | p += BIT32SZ; 201 | f->offset = GBIT64(p); 202 | p += BIT64SZ; 203 | f->count = GBIT32(p); 204 | p += BIT32SZ; 205 | if(p+f->count > ep) 206 | return 0; 207 | f->data = (char*)p; 208 | p += f->count; 209 | break; 210 | 211 | case Tclunk: 212 | case Tremove: 213 | if(p+BIT32SZ > ep) 214 | return 0; 215 | f->fid = GBIT32(p); 216 | p += BIT32SZ; 217 | break; 218 | 219 | case Tstat: 220 | if(p+BIT32SZ > ep) 221 | return 0; 222 | f->fid = GBIT32(p); 223 | p += BIT32SZ; 224 | break; 225 | 226 | case Twstat: 227 | if(p+BIT32SZ+BIT16SZ > ep) 228 | return 0; 229 | f->fid = GBIT32(p); 230 | p += BIT32SZ; 231 | f->nstat = GBIT16(p); 232 | p += BIT16SZ; 233 | if(p+f->nstat > ep) 234 | return 0; 235 | f->stat = p; 236 | p += f->nstat; 237 | break; 238 | 239 | /* 240 | */ 241 | case Rversion: 242 | if(p+BIT32SZ > ep) 243 | return 0; 244 | f->msize = GBIT32(p); 245 | p += BIT32SZ; 246 | p = gstring(p, ep, &f->version); 247 | break; 248 | 249 | case Rerror: 250 | p = gstring(p, ep, &f->ename); 251 | f->errornum = 0; 252 | if(dotu){ 253 | if(p+BIT32SZ > ep) 254 | return 0; 255 | f->errornum = GBIT32(p); 256 | p += BIT32SZ; 257 | } 258 | break; 259 | 260 | case Rflush: 261 | break; 262 | 263 | case Rauth: 264 | p = gqid(p, ep, &f->aqid); 265 | if(p == nil) 266 | break; 267 | break; 268 | 269 | case Rattach: 270 | p = gqid(p, ep, &f->qid); 271 | if(p == nil) 272 | break; 273 | break; 274 | 275 | case Rwalk: 276 | if(p+BIT16SZ > ep) 277 | return 0; 278 | f->nwqid = GBIT16(p); 279 | p += BIT16SZ; 280 | if(f->nwqid > MAXWELEM) 281 | return 0; 282 | for(i=0; inwqid; i++){ 283 | p = gqid(p, ep, &f->wqid[i]); 284 | if(p == nil) 285 | break; 286 | } 287 | break; 288 | 289 | case Ropen: 290 | case Ropenfd: 291 | case Rcreate: 292 | p = gqid(p, ep, &f->qid); 293 | if(p == nil) 294 | break; 295 | if(p+BIT32SZ > ep) 296 | return 0; 297 | f->iounit = GBIT32(p); 298 | p += BIT32SZ; 299 | if(f->type == Ropenfd){ 300 | if(p+BIT32SZ > ep) 301 | return 0; 302 | f->unixfd = GBIT32(p); 303 | p += BIT32SZ; 304 | } 305 | break; 306 | 307 | case Rread: 308 | if(p+BIT32SZ > ep) 309 | return 0; 310 | f->count = GBIT32(p); 311 | p += BIT32SZ; 312 | if(p+f->count > ep) 313 | return 0; 314 | f->data = (char*)p; 315 | p += f->count; 316 | break; 317 | 318 | case Rwrite: 319 | if(p+BIT32SZ > ep) 320 | return 0; 321 | f->count = GBIT32(p); 322 | p += BIT32SZ; 323 | break; 324 | 325 | case Rclunk: 326 | case Rremove: 327 | break; 328 | 329 | case Rstat: 330 | if(p+BIT16SZ > ep) 331 | return 0; 332 | f->nstat = GBIT16(p); 333 | p += BIT16SZ; 334 | if(p+f->nstat > ep) 335 | return 0; 336 | f->stat = p; 337 | p += f->nstat; 338 | break; 339 | 340 | case Rwstat: 341 | break; 342 | } 343 | 344 | if(p==nil || p>ep) 345 | return 0; 346 | if(ap+size == p) 347 | return size; 348 | return 0; 349 | } 350 | 351 | uint 352 | convM2S(uchar *ap, uint nap, Fcall *f) 353 | { 354 | return convM2Su(ap, nap, f, 0); 355 | } 356 | -------------------------------------------------------------------------------- /lib/convS2M.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "../libc.h" 6 | #include "../fcall.h" 7 | 8 | static 9 | uchar* 10 | pstring(uchar *p, char *s) 11 | { 12 | uint n; 13 | 14 | if(s == nil){ 15 | PBIT16(p, 0); 16 | p += BIT16SZ; 17 | return p; 18 | } 19 | 20 | n = strlen(s); 21 | PBIT16(p, n); 22 | p += BIT16SZ; 23 | memmove(p, s, n); 24 | p += n; 25 | return p; 26 | } 27 | 28 | static 29 | uchar* 30 | pqid(uchar *p, Qid *q) 31 | { 32 | PBIT8(p, q->type); 33 | p += BIT8SZ; 34 | PBIT32(p, q->vers); 35 | p += BIT32SZ; 36 | PBIT64(p, q->path); 37 | p += BIT64SZ; 38 | return p; 39 | } 40 | 41 | static 42 | uint 43 | stringsz(char *s) 44 | { 45 | if(s == nil) 46 | return BIT16SZ; 47 | 48 | return BIT16SZ+strlen(s); 49 | } 50 | 51 | uint 52 | sizeS2Mu(Fcall *f, int dotu) 53 | { 54 | uint n; 55 | int i; 56 | 57 | n = 0; 58 | n += BIT32SZ; /* size */ 59 | n += BIT8SZ; /* type */ 60 | n += BIT16SZ; /* tag */ 61 | 62 | switch(f->type) 63 | { 64 | default: 65 | return 0; 66 | 67 | case Tversion: 68 | n += BIT32SZ; 69 | n += stringsz(f->version); 70 | break; 71 | 72 | case Tflush: 73 | n += BIT16SZ; 74 | break; 75 | 76 | case Tauth: 77 | n += BIT32SZ; 78 | n += stringsz(f->uname); 79 | n += stringsz(f->aname); 80 | if(dotu) 81 | n += BIT32SZ; 82 | break; 83 | 84 | case Tattach: 85 | n += BIT32SZ; 86 | n += BIT32SZ; 87 | n += stringsz(f->uname); 88 | n += stringsz(f->aname); 89 | if(dotu) 90 | n += BIT32SZ; 91 | break; 92 | 93 | case Twalk: 94 | n += BIT32SZ; 95 | n += BIT32SZ; 96 | n += BIT16SZ; 97 | for(i=0; inwname; i++) 98 | n += stringsz(f->wname[i]); 99 | break; 100 | 101 | case Topen: 102 | case Topenfd: 103 | n += BIT32SZ; 104 | n += BIT8SZ; 105 | break; 106 | 107 | case Tcreate: 108 | n += BIT32SZ; 109 | n += stringsz(f->name); 110 | n += BIT32SZ; 111 | n += BIT8SZ; 112 | if(dotu) 113 | n += stringsz(f->extension); 114 | break; 115 | 116 | case Tread: 117 | n += BIT32SZ; 118 | n += BIT64SZ; 119 | n += BIT32SZ; 120 | break; 121 | 122 | case Twrite: 123 | n += BIT32SZ; 124 | n += BIT64SZ; 125 | n += BIT32SZ; 126 | n += f->count; 127 | break; 128 | 129 | case Tclunk: 130 | case Tremove: 131 | n += BIT32SZ; 132 | break; 133 | 134 | case Tstat: 135 | n += BIT32SZ; 136 | break; 137 | 138 | case Twstat: 139 | n += BIT32SZ; 140 | n += BIT16SZ; 141 | n += f->nstat; 142 | break; 143 | /* 144 | */ 145 | 146 | case Rversion: 147 | n += BIT32SZ; 148 | n += stringsz(f->version); 149 | break; 150 | 151 | case Rerror: 152 | n += stringsz(f->ename); 153 | if(dotu) 154 | n += BIT32SZ; 155 | break; 156 | 157 | case Rflush: 158 | break; 159 | 160 | case Rauth: 161 | n += QIDSZ; 162 | break; 163 | 164 | case Rattach: 165 | n += QIDSZ; 166 | break; 167 | 168 | case Rwalk: 169 | n += BIT16SZ; 170 | n += f->nwqid*QIDSZ; 171 | break; 172 | 173 | case Ropen: 174 | case Rcreate: 175 | n += QIDSZ; 176 | n += BIT32SZ; 177 | break; 178 | 179 | case Ropenfd: 180 | n += QIDSZ; 181 | n += BIT32SZ; 182 | n += BIT32SZ; 183 | break; 184 | 185 | case Rread: 186 | n += BIT32SZ; 187 | n += f->count; 188 | break; 189 | 190 | case Rwrite: 191 | n += BIT32SZ; 192 | break; 193 | 194 | case Rclunk: 195 | break; 196 | 197 | case Rremove: 198 | break; 199 | 200 | case Rstat: 201 | n += BIT16SZ; 202 | n += f->nstat; 203 | break; 204 | 205 | case Rwstat: 206 | break; 207 | } 208 | return n; 209 | } 210 | 211 | uint 212 | sizeS2M(Fcall *f) 213 | { 214 | return sizeS2Mu(f, 0); 215 | } 216 | 217 | uint 218 | convS2Mu(Fcall *f, uchar *ap, uint nap, int dotu) 219 | { 220 | uchar *p; 221 | uint i, size; 222 | 223 | size = sizeS2Mu(f, dotu); 224 | if(size == 0) 225 | return 0; 226 | if(size > nap) 227 | return 0; 228 | 229 | p = (uchar*)ap; 230 | 231 | PBIT32(p, size); 232 | p += BIT32SZ; 233 | PBIT8(p, f->type); 234 | p += BIT8SZ; 235 | PBIT16(p, f->tag); 236 | p += BIT16SZ; 237 | 238 | switch(f->type) 239 | { 240 | default: 241 | return 0; 242 | 243 | case Tversion: 244 | PBIT32(p, f->msize); 245 | p += BIT32SZ; 246 | p = pstring(p, f->version); 247 | break; 248 | 249 | case Tflush: 250 | PBIT16(p, f->oldtag); 251 | p += BIT16SZ; 252 | break; 253 | 254 | case Tauth: 255 | PBIT32(p, f->afid); 256 | p += BIT32SZ; 257 | p = pstring(p, f->uname); 258 | p = pstring(p, f->aname); 259 | if(dotu){ 260 | f->uidnum = NOUID; 261 | PBIT32(p, f->uidnum); 262 | p += BIT32SZ; 263 | } 264 | break; 265 | 266 | case Tattach: 267 | PBIT32(p, f->fid); 268 | p += BIT32SZ; 269 | PBIT32(p, f->afid); 270 | p += BIT32SZ; 271 | p = pstring(p, f->uname); 272 | p = pstring(p, f->aname); 273 | if(dotu){ 274 | f->uidnum = NOUID; 275 | PBIT32(p, f->uidnum); 276 | p += BIT32SZ; 277 | } 278 | break; 279 | 280 | case Twalk: 281 | PBIT32(p, f->fid); 282 | p += BIT32SZ; 283 | PBIT32(p, f->newfid); 284 | p += BIT32SZ; 285 | PBIT16(p, f->nwname); 286 | p += BIT16SZ; 287 | if(f->nwname > MAXWELEM) 288 | return 0; 289 | for(i=0; inwname; i++) 290 | p = pstring(p, f->wname[i]); 291 | break; 292 | 293 | case Topen: 294 | case Topenfd: 295 | PBIT32(p, f->fid); 296 | p += BIT32SZ; 297 | PBIT8(p, f->mode); 298 | p += BIT8SZ; 299 | break; 300 | 301 | case Tcreate: 302 | PBIT32(p, f->fid); 303 | p += BIT32SZ; 304 | p = pstring(p, f->name); 305 | PBIT32(p, f->perm); 306 | p += BIT32SZ; 307 | PBIT8(p, f->mode); 308 | p += BIT8SZ; 309 | if(dotu) 310 | p = pstring(p, f->extension); 311 | break; 312 | 313 | case Tread: 314 | PBIT32(p, f->fid); 315 | p += BIT32SZ; 316 | PBIT64(p, f->offset); 317 | p += BIT64SZ; 318 | PBIT32(p, f->count); 319 | p += BIT32SZ; 320 | break; 321 | 322 | case Twrite: 323 | PBIT32(p, f->fid); 324 | p += BIT32SZ; 325 | PBIT64(p, f->offset); 326 | p += BIT64SZ; 327 | PBIT32(p, f->count); 328 | p += BIT32SZ; 329 | memmove(p, f->data, f->count); 330 | p += f->count; 331 | break; 332 | 333 | case Tclunk: 334 | case Tremove: 335 | PBIT32(p, f->fid); 336 | p += BIT32SZ; 337 | break; 338 | 339 | case Tstat: 340 | PBIT32(p, f->fid); 341 | p += BIT32SZ; 342 | break; 343 | 344 | case Twstat: 345 | PBIT32(p, f->fid); 346 | p += BIT32SZ; 347 | PBIT16(p, f->nstat); 348 | p += BIT16SZ; 349 | memmove(p, f->stat, f->nstat); 350 | p += f->nstat; 351 | break; 352 | /* 353 | */ 354 | 355 | case Rversion: 356 | PBIT32(p, f->msize); 357 | p += BIT32SZ; 358 | p = pstring(p, f->version); 359 | break; 360 | 361 | case Rerror: 362 | p = pstring(p, f->ename); 363 | if(dotu){ 364 | PBIT32(p, f->errornum); 365 | p += BIT32SZ; 366 | } 367 | break; 368 | 369 | case Rflush: 370 | break; 371 | 372 | case Rauth: 373 | p = pqid(p, &f->aqid); 374 | break; 375 | 376 | case Rattach: 377 | p = pqid(p, &f->qid); 378 | break; 379 | 380 | case Rwalk: 381 | PBIT16(p, f->nwqid); 382 | p += BIT16SZ; 383 | if(f->nwqid > MAXWELEM) 384 | return 0; 385 | for(i=0; inwqid; i++) 386 | p = pqid(p, &f->wqid[i]); 387 | break; 388 | 389 | case Ropen: 390 | case Rcreate: 391 | case Ropenfd: 392 | p = pqid(p, &f->qid); 393 | PBIT32(p, f->iounit); 394 | p += BIT32SZ; 395 | if(f->type == Ropenfd){ 396 | PBIT32(p, f->unixfd); 397 | p += BIT32SZ; 398 | } 399 | break; 400 | 401 | case Rread: 402 | PBIT32(p, f->count); 403 | p += BIT32SZ; 404 | memmove(p, f->data, f->count); 405 | p += f->count; 406 | break; 407 | 408 | case Rwrite: 409 | PBIT32(p, f->count); 410 | p += BIT32SZ; 411 | break; 412 | 413 | case Rclunk: 414 | break; 415 | 416 | case Rremove: 417 | break; 418 | 419 | case Rstat: 420 | PBIT16(p, f->nstat); 421 | p += BIT16SZ; 422 | memmove(p, f->stat, f->nstat); 423 | p += f->nstat; 424 | break; 425 | 426 | case Rwstat: 427 | break; 428 | } 429 | if(size != p-ap) 430 | return 0; 431 | return size; 432 | } 433 | 434 | uint 435 | convS2M(Fcall *f, uchar *ap, uint nap) 436 | { 437 | return convS2Mu(f, ap, nap, 0); 438 | } 439 | -------------------------------------------------------------------------------- /9p.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "libc.h" 16 | #include "fcall.h" 17 | #include "9pfs.h" 18 | #include "util.h" 19 | 20 | char *calls2str[] = { 21 | [Tversion] "Tversion", 22 | [Tauth] "Tauth", 23 | [Tattach] "Tattach", 24 | [Terror] "Terror", 25 | [Tflush] "Tflush", 26 | [Twalk] "Twalk", 27 | [Topen] "Topen", 28 | [Tcreate] "Tcreate", 29 | [Tread] "Tread", 30 | [Twrite] "Twrite", 31 | [Tclunk] "Tclunk", 32 | [Tremove] "Tremove", 33 | [Tstat] "Tstat", 34 | [Twstat] "Twstat", 35 | [Rversion] "Rversion", 36 | [Rauth] "Rauth", 37 | [Rattach] "Rattach", 38 | [Rerror] "Rerror", 39 | [Rflush] "Rflush", 40 | [Rwalk] "Rwalk", 41 | [Ropen] "Ropen", 42 | [Rcreate] "Rcreate", 43 | [Rread] "Rread", 44 | [Rwrite] "Rwrite", 45 | [Rclunk] "Rclunk", 46 | [Rremove] "Rremove", 47 | [Rstat] "Rstat", 48 | [Rwstat] "Rwstat" 49 | }; 50 | 51 | void *tbuf, *rbuf; 52 | FFid *fidhash[NHASH]; 53 | FDir *dirhash[NHASH]; 54 | 55 | FFid *lookupfid(u32int, int); 56 | FFid *uniqfid(void); 57 | 58 | void 59 | init9p(void) 60 | { 61 | unsigned int seed; 62 | int rfd; 63 | 64 | if((rfd = open("/dev/random", O_RDONLY)) == -1) 65 | err(1, "Could not open /dev/random"); 66 | if(read(rfd, &seed, sizeof(seed)) != sizeof(seed)) 67 | err(1, "Bad /dev/random read"); 68 | close(rfd); 69 | srandom(seed); 70 | } 71 | 72 | int 73 | do9p(Fcall *t, Fcall *r) 74 | { 75 | int n; 76 | 77 | if(t->type == Tversion) 78 | t->tag = (ushort)~0; 79 | else 80 | t->tag = random(); 81 | if((n = convS2M(t, tbuf, msize)) == 0){ 82 | r->ename = "Bad S2M conversion"; 83 | goto err; 84 | } 85 | if(write(srvfd, tbuf, n) != n){ 86 | r->ename = "Bad 9p write"; 87 | goto err; 88 | } 89 | if((n = read9pmsg(srvfd, rbuf, msize)) == -1){ 90 | r->ename = "Bad 9p read"; 91 | goto err; 92 | } 93 | if(convM2S(rbuf, n, r) != n){ 94 | r->ename = "Bad M2S conversion"; 95 | goto err; 96 | } 97 | if(r->tag != t->tag) 98 | errx(1, "tag mismatch"); 99 | if(r->type != t->type+1){ 100 | goto err; 101 | } 102 | return 0; 103 | 104 | err: 105 | r->type = Rerror; 106 | return -1; 107 | } 108 | 109 | int 110 | _9pversion(u32int m) 111 | { 112 | Fcall tver, rver; 113 | 114 | msize = m; 115 | tver.type = Tversion; 116 | tver.msize = m; 117 | tver.version = VERSION9P; 118 | tbuf = erealloc(tbuf, m); 119 | rbuf = erealloc(rbuf, m); 120 | if(do9p(&tver, &rver) != 0) 121 | errx(1, "Could not establish version: %s", rver.ename); 122 | if(rver.msize != m){ 123 | msize = rver.msize; 124 | tbuf = erealloc(tbuf, msize); 125 | rbuf = erealloc(rbuf, msize); 126 | } 127 | return msize; 128 | } 129 | 130 | FFid* 131 | _9pauth(u32int afid, char *uname, char *aname) 132 | { 133 | FFid *f; 134 | Fcall tauth, rauth; 135 | 136 | tauth.type = Tauth; 137 | tauth.afid = afid; 138 | tauth.uname = uname; 139 | tauth.aname = aname; 140 | if(do9p(&tauth, &rauth) == -1) 141 | errx(1, "Could not establish authentication: %s", rauth.ename); 142 | f = lookupfid(afid, PUT); 143 | f->path = "AUTHFID"; 144 | f->fid = afid; 145 | f->qid = rauth.aqid; 146 | f->iounit = msize - IOHDRSZ; 147 | return f; 148 | } 149 | 150 | FFid* 151 | _9pattach(u32int fid, u32int afid, char* uname, char *aname) 152 | { 153 | FFid *f; 154 | Fcall tattach, rattach; 155 | 156 | tattach.type = Tattach; 157 | tattach.fid = fid; 158 | tattach.afid = afid; 159 | tattach.uname = uname; 160 | tattach.aname = aname; 161 | if(do9p(&tattach, &rattach) == -1) 162 | errx(1, "Could not attach"); 163 | f = lookupfid(fid, PUT); 164 | f->path = ""; 165 | f->fid = fid; 166 | f->qid = rattach.qid; 167 | return f; 168 | } 169 | 170 | FFid* 171 | _9pwalkr(FFid *r, char *path) 172 | { 173 | FFid *f; 174 | Fcall twalk, rwalk; 175 | char **s, *buf, *bp; 176 | 177 | buf = bp = estrdup(path); 178 | f = NULL; 179 | twalk.type = Twalk; 180 | twalk.newfid = r->fid; 181 | while(bp != NULL){ 182 | for(s = twalk.wname; s < twalk.wname + MAXWELEM && bp != NULL; s++) 183 | *s = strsep(&bp, "/"); 184 | _9pclunk(f); 185 | twalk.fid = twalk.newfid; 186 | f = uniqfid(); 187 | twalk.newfid = f->fid; 188 | twalk.nwname = s - twalk.wname; 189 | if(do9p(&twalk, &rwalk) == -1 || rwalk.nwqid < twalk.nwname){ 190 | if(lookupfid(f->fid, DEL) != FDEL) 191 | errx(1, "Fid %d not found in hash", f->fid); 192 | free(buf); 193 | return NULL; 194 | } 195 | } 196 | f->qid = rwalk.wqid[rwalk.nwqid - 1]; 197 | free(buf); 198 | return f; 199 | } 200 | 201 | FFid* 202 | _9pwalk(const char *path) 203 | { 204 | FFid *f; 205 | char *pnew; 206 | 207 | if(*path == '\0' || strcmp(path, "/") == 0) 208 | return fidclone(rootfid); 209 | pnew = estrdup(path); 210 | if((f = _9pwalkr(rootfid, pnew+1)) == NULL){ 211 | free(pnew); 212 | return NULL; 213 | } 214 | f->path = pnew; 215 | return f; 216 | } 217 | 218 | Dir* 219 | _9pstat(FFid *f) 220 | { 221 | Dir *d; 222 | Fcall tstat, rstat; 223 | 224 | tstat.type = Tstat; 225 | tstat.fid = f->fid; 226 | if(do9p(&tstat, &rstat) == -1) 227 | return NULL; 228 | d = emalloc(sizeof(*d) + rstat.nstat); 229 | if(convM2D(rstat.stat, rstat.nstat, d, (char*)(d+1)) != rstat.nstat){ 230 | free(d); 231 | return NULL; 232 | } 233 | return d; 234 | } 235 | 236 | int 237 | _9pwstat(FFid *f, Dir *d) 238 | { 239 | Fcall twstat, rwstat; 240 | uchar *st; 241 | int n; 242 | 243 | n = sizeD2M(d); 244 | st = emalloc(n); 245 | if(convD2M(d, st, n) != n){ 246 | free(st); 247 | return -1; 248 | } 249 | twstat.type = Twstat; 250 | twstat.fid = f->fid; 251 | twstat.nstat = n; 252 | twstat.stat = st; 253 | if(do9p(&twstat, &rwstat) == -1){ 254 | free(st); 255 | return -1; 256 | } 257 | free(st); 258 | return 0; 259 | } 260 | 261 | int 262 | _9popen(FFid *f) 263 | { 264 | Fcall topen, ropen; 265 | 266 | topen.type = Topen; 267 | topen.fid = f->fid; 268 | topen.mode = f->mode; 269 | if(do9p(&topen, &ropen) == -1) 270 | return -1; 271 | f->qid = ropen.qid; 272 | if(ropen.iounit != 0) 273 | f->iounit = ropen.iounit; 274 | else 275 | f->iounit = msize - IOHDRSZ; 276 | return 0; 277 | } 278 | 279 | FFid* 280 | _9pcreate(FFid *f, char *name, int perm, int isdir) 281 | { 282 | Fcall tcreate, rcreate; 283 | 284 | perm &= 0777; 285 | if(isdir) 286 | perm |= DMDIR; 287 | tcreate.type = Tcreate; 288 | tcreate.fid = f->fid; 289 | tcreate.name = name; 290 | tcreate.perm = perm; 291 | tcreate.mode = f->mode; 292 | if(do9p(&tcreate, &rcreate) == -1){ 293 | _9pclunk(f); 294 | return NULL; 295 | } 296 | if(rcreate.iounit != 0) 297 | f->iounit = rcreate.iounit; 298 | else 299 | f->iounit = msize - IOHDRSZ; 300 | f->qid = rcreate.qid; 301 | return f; 302 | } 303 | 304 | int 305 | _9premove(FFid *f) 306 | { 307 | Fcall tremove, rremove; 308 | 309 | if(f == NULL) 310 | return 0; 311 | tremove.type = Tremove; 312 | tremove.fid = f->fid; 313 | if(do9p(&tremove, &rremove) == -1){ 314 | _9pclunk(f); 315 | return -1; 316 | } 317 | if(lookupfid(f->fid, DEL) != FDEL) 318 | errx(1, "Fid %d not found in hash", f->fid); 319 | return 0; 320 | } 321 | 322 | long 323 | dirpackage(uchar *buf, long ts, Dir **d) 324 | { 325 | char *s; 326 | long ss, i, n, nn, m; 327 | 328 | *d = nil; 329 | if(ts <= 0) 330 | return 0; 331 | 332 | /* 333 | * first find number of all stats, check they look like stats, & size all associated strings 334 | */ 335 | ss = 0; 336 | n = 0; 337 | for(i = 0; i < ts; i += m){ 338 | m = BIT16SZ + GBIT16(&buf[i]); 339 | if(statcheck(&buf[i], m) < 0) 340 | break; 341 | ss += m; 342 | n++; 343 | } 344 | 345 | if(i != ts) 346 | return -1; 347 | 348 | *d = malloc(n * sizeof(Dir) + ss); 349 | if(*d == nil) 350 | return -1; 351 | 352 | /* 353 | * then convert all buffers 354 | */ 355 | s = (char*)*d + n * sizeof(Dir); 356 | nn = 0; 357 | for(i = 0; i < ts; i += m){ 358 | m = BIT16SZ + GBIT16((uchar*)&buf[i]); 359 | if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){ 360 | free(*d); 361 | *d = nil; 362 | return -1; 363 | } 364 | nn++; 365 | s += m; 366 | } 367 | 368 | return nn; 369 | } 370 | 371 | int 372 | dircmp(const void *v1, const void *v2) 373 | { 374 | return strcmp(((Dir*)v1)->name, ((Dir*)v2)->name); 375 | } 376 | 377 | long 378 | _9pdirread(FFid *f, Dir **d) 379 | { 380 | FDir *fdir; 381 | char *buf; 382 | int t; 383 | u32int n; 384 | long bufsize, r; 385 | 386 | DPRINT("_9pdirread on %s\n", f->path); 387 | n = f->iounit; 388 | bufsize = DIRMAX; 389 | buf = emalloc(bufsize); 390 | r = 0; 391 | while((t = _9pread(f, buf+r, n)) > 0){ 392 | r += t; 393 | if(r > bufsize - n){ 394 | bufsize += DIRMAX; 395 | buf = erealloc(buf, bufsize); 396 | } 397 | } 398 | if(r <= 0){ 399 | free(buf); 400 | return r; 401 | } 402 | r = dirpackage((uchar*)buf, r, d); 403 | fdir = lookupdir(f->path, PUT); 404 | fdir->dirs = *d; 405 | fdir->ndirs = r; 406 | qsort(fdir->dirs, fdir->ndirs, sizeof(*fdir->dirs), dircmp); 407 | free(buf); 408 | return r; 409 | } 410 | 411 | int 412 | _9pread(FFid *f, char *buf, u32int n) 413 | { 414 | Fcall tread, rread; 415 | u32int tot; 416 | 417 | if(n == 0) 418 | return 0; 419 | tread.type = Tread; 420 | tread.fid = f->fid; 421 | tot = 0; 422 | while(n > 0){ 423 | DPRINT("_9pread n is %d\n", n); 424 | tread.offset = f->offset; 425 | tread.count = n < f->iounit ? n : f->iounit; 426 | if(do9p(&tread, &rread) == -1) 427 | return -1; 428 | memcpy(buf+tot, rread.data, rread.count); 429 | f->offset += rread.count; 430 | tot += rread.count; 431 | if(rread.count < tread.count) 432 | break; 433 | n -= rread.count; 434 | } 435 | DPRINT("_9pread ending n is %d, tot is %d\n", n, tot); 436 | return tot; 437 | } 438 | 439 | int 440 | _9pwrite(FFid *f, char *buf, u32int n) 441 | { 442 | Fcall twrite, rwrite; 443 | u32int tot; 444 | 445 | if(n == 0) 446 | return 0; 447 | twrite.type = Twrite; 448 | twrite.fid = f->fid; 449 | tot = 0; 450 | while(n > 0){ 451 | DPRINT("_9pwrite n is %d\n", n); 452 | twrite.offset = f->offset; 453 | twrite.count = n < f->iounit ? n : f->iounit; 454 | twrite.data = buf+tot; 455 | if(do9p(&twrite, &rwrite) == -1) 456 | return -1; 457 | f->offset += rwrite.count; 458 | tot += rwrite.count; 459 | if(rwrite.count < twrite.count) 460 | break; 461 | n -= rwrite.count; 462 | } 463 | DPRINT("_9pwrite ending n is %d, tot is %d\n", n, tot); 464 | return tot; 465 | } 466 | 467 | int 468 | _9pclunk(FFid *f) 469 | { 470 | Fcall tclunk, rclunk; 471 | 472 | if(f == NULL) 473 | return 0; 474 | tclunk.type = Tclunk; 475 | tclunk.fid = f->fid; 476 | if(lookupfid(f->fid, DEL) != FDEL) 477 | errx(1, "Fid %d not found in hash", f->fid); 478 | return do9p(&tclunk, &rclunk); 479 | } 480 | 481 | FFid* 482 | uniqfid(void) 483 | { 484 | FFid *f; 485 | u32int fid; 486 | 487 | do 488 | fid = random(); 489 | while((f = lookupfid(fid, PUT)) == NULL); 490 | return f; 491 | } 492 | 493 | 494 | FFid* 495 | lookupfid(u32int fid, int act) 496 | { 497 | FFid **floc, *f; 498 | 499 | f = NULL; 500 | for(floc = fidhash + fid % NHASH; *floc != NULL; floc = &(*floc)->link){ 501 | if((*floc)->fid == fid) 502 | break; 503 | } 504 | switch(act){ 505 | case GET: 506 | f = *floc; 507 | break; 508 | case PUT: 509 | if(*floc != NULL) 510 | return NULL; 511 | f = emalloc(sizeof(*f)); 512 | f->fid = fid; 513 | *floc = f; 514 | break; 515 | case DEL: 516 | if(*floc == NULL || *floc == rootfid) 517 | return NULL; 518 | f = *floc; 519 | *floc = f->link; 520 | free(f->path); 521 | free(f); 522 | f = FDEL; 523 | break; 524 | } 525 | return f; 526 | } 527 | 528 | FFid* 529 | fidclone(FFid *f) 530 | { 531 | Fcall twalk, rwalk; 532 | FFid *newf; 533 | 534 | newf = uniqfid(); 535 | twalk.type = Twalk; 536 | twalk.fid = f->fid; 537 | twalk.newfid = newf->fid; 538 | twalk.nwname = 0; 539 | if(do9p(&twalk, &rwalk) == -1){ 540 | lookupfid(f->fid, DEL); 541 | return NULL; 542 | } 543 | if(rwalk.nwqid != 0) 544 | err(1, "fidclone was not zero"); 545 | newf->qid = *rwalk.wqid; 546 | newf->path = estrdup(f->path); 547 | return newf; 548 | } 549 | 550 | uint 551 | strkey(const char *s) 552 | { 553 | const char *p; 554 | uint h; 555 | 556 | h = 0; 557 | for(p = s; *p != '\0'; p++) 558 | h = h*31 + *p; 559 | return h; 560 | } 561 | 562 | FDir* 563 | lookupdir(const char *path, int act) 564 | { 565 | FDir **fdloc, *fd; 566 | uint h; 567 | 568 | fd = NULL; 569 | h = strkey(path); 570 | for(fdloc = dirhash + h % NHASH; *fdloc != NULL; fdloc = &(*fdloc)->link){ 571 | if(strcmp((*fdloc)->path, path) == 0) 572 | break; 573 | } 574 | switch(act){ 575 | case GET: 576 | fd = *fdloc; 577 | break; 578 | case PUT: 579 | if(*fdloc != NULL){ 580 | fd = *fdloc; 581 | free(fd->dirs); 582 | fd->dirs = NULL; 583 | fd->ndirs = 0; 584 | }else{ 585 | fd = emalloc(sizeof(*fd)); 586 | fd->path = estrdup(path); 587 | *fdloc = fd; 588 | } 589 | break; 590 | case DEL: 591 | if(*fdloc == NULL) 592 | return NULL; 593 | fd = *fdloc; 594 | *fdloc = fd->link; 595 | free(fd->path); 596 | free(fd->dirs); 597 | free(fd); 598 | fd = FDEL; 599 | break; 600 | } 601 | return fd; 602 | } 603 | -------------------------------------------------------------------------------- /9pfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "libc.h" 23 | #include "fcall.h" 24 | #include "9pfs.h" 25 | #include "auth.h" 26 | #include "util.h" 27 | 28 | #define CACHECTL ".fscache" 29 | 30 | enum 31 | { 32 | CACHECTLSIZE = 8, /* sizeof("cleared\n") - 1 */ 33 | MSIZE = 8192 34 | }; 35 | 36 | void dir2stat(struct stat*, Dir*); 37 | Dir *iscached(const char*); 38 | Dir *addtocache(const char*); 39 | void clearcache(const char*); 40 | int iscachectl(const char*); 41 | char *breakpath(char*); 42 | void usage(void); 43 | 44 | Dir *rootdir; 45 | 46 | int 47 | fsstat(const char *path, struct stat *st) 48 | { 49 | FFid *f; 50 | Dir *d; 51 | 52 | if((f = _9pwalk(path)) == NULL) 53 | return -EIO; 54 | if((d = _9pstat(f)) == NULL){ 55 | _9pclunk(f); 56 | return -EACCES; 57 | } 58 | dir2stat(st, d); 59 | _9pclunk(f); 60 | free(d); 61 | return 0; 62 | } 63 | 64 | int 65 | fsgetattr(const char *path, struct stat *st) 66 | { 67 | Dir *d; 68 | 69 | if(iscachectl(path)){ 70 | st->st_mode = 0666 | S_IFREG; 71 | st->st_uid = getuid(); 72 | st->st_gid = getgid(); 73 | st->st_size = CACHECTLSIZE; 74 | return 0; 75 | } 76 | if((d = iscached(path)) == NULL) 77 | d = addtocache(path); 78 | if(d == NULL) 79 | return -ENOENT; 80 | if(strcmp(d->uid, "stub") == 0) /* hack for aux/stub */ 81 | return fsstat(path, st); 82 | dir2stat(st, d); 83 | return 0; 84 | } 85 | 86 | int 87 | fsrelease(const char *path, struct fuse_file_info *ffi) 88 | { 89 | return _9pclunk((FFid*)ffi->fh); 90 | } 91 | 92 | int 93 | fsreleasedir(const char *path, struct fuse_file_info *ffi) 94 | { 95 | FFid *f; 96 | 97 | if((FFid*)ffi->fh == NULL) 98 | return 0; 99 | f = (FFid*)ffi->fh; 100 | if((f->qid.type & QTDIR) == 0) 101 | return -ENOTDIR; 102 | return _9pclunk(f); 103 | } 104 | 105 | int 106 | fstruncate(const char *path, off_t off) 107 | { 108 | FFid *f; 109 | Dir *d; 110 | 111 | if(iscachectl(path)) 112 | return 0; 113 | if((f = _9pwalk(path)) == NULL) 114 | return -ENOENT; 115 | if(off == 0){ 116 | f->mode = OWRITE | OTRUNC; 117 | if(_9popen(f) == -1){ 118 | _9pclunk(f); 119 | return -EIO; 120 | } 121 | }else{ 122 | if((d = _9pstat(f)) == NULL){ 123 | _9pclunk(f); 124 | return -EIO; 125 | } 126 | d->length = off; 127 | if(_9pwstat(f, d) == -1){ 128 | _9pclunk(f); 129 | free(d); 130 | return -EACCES; 131 | } 132 | free(d); 133 | } 134 | _9pclunk(f); 135 | clearcache(path); 136 | return 0; 137 | } 138 | 139 | int 140 | fsrename(const char *opath, const char *npath) 141 | { 142 | Dir *d; 143 | FFid *f; 144 | char *dname, *bname; 145 | 146 | if(iscachectl(opath)) 147 | return -EACCES; 148 | if((f = _9pwalk(opath)) == NULL) 149 | return -ENOENT; 150 | dname = estrdup(npath); 151 | bname = strrchr(dname, '/'); 152 | if(strncmp(opath, npath, bname-dname) != 0){ 153 | free(dname); 154 | return -EACCES; 155 | } 156 | *bname++ = '\0'; 157 | if((d = _9pstat(f)) == NULL){ 158 | free(dname); 159 | return -EIO; 160 | } 161 | d->name = bname; 162 | if(_9pwstat(f, d) == -1){ 163 | _9pclunk(f); 164 | free(dname); 165 | free(d); 166 | return -EACCES; 167 | } 168 | _9pclunk(f); 169 | free(dname); 170 | free(d); 171 | clearcache(opath); 172 | return 0; 173 | } 174 | 175 | int 176 | fsopen(const char *path, struct fuse_file_info *ffi) 177 | { 178 | FFid *f; 179 | 180 | if(iscachectl(path)) 181 | return 0; 182 | if((f = _9pwalk(path)) == NULL) 183 | return -ENOENT; 184 | f->mode = ffi->flags & O_ACCMODE; 185 | if(ffi->flags & O_TRUNC) 186 | f->mode |= OTRUNC; 187 | if(_9popen(f) == -1){ 188 | _9pclunk(f); 189 | return -EACCES; 190 | } 191 | ffi->fh = (u64int)f; 192 | return 0; 193 | } 194 | 195 | int 196 | fscreate(const char *path, mode_t perm, struct fuse_file_info *ffi) 197 | { 198 | FFid *f; 199 | char *dname, *bname; 200 | 201 | if(iscachectl(path)) 202 | return -EACCES; 203 | if((f = _9pwalk(path)) == NULL){ 204 | dname = estrdup(path); 205 | bname = breakpath(dname); 206 | if((f = _9pwalk(dname)) == NULL){ 207 | free(dname); 208 | return -ENOENT; 209 | } 210 | f->mode = ffi->flags & O_ACCMODE; 211 | f = _9pcreate(f, bname, perm, 0); 212 | free(dname); 213 | if(f == NULL) 214 | return -EACCES; 215 | }else{ 216 | if(ffi->flags | O_EXCL){ 217 | _9pclunk(f); 218 | return -EEXIST; 219 | } 220 | f->mode = ffi->flags & O_ACCMODE; 221 | if(_9popen(f) == -1){ 222 | _9pclunk(f); 223 | return -EIO; 224 | } 225 | } 226 | ffi->fh = (u64int)f; 227 | clearcache(path); 228 | return 0; 229 | } 230 | 231 | int 232 | fsunlink(const char *path) 233 | { 234 | FFid *f; 235 | 236 | if(iscachectl(path)) 237 | return 0; 238 | if((f = _9pwalk(path)) == NULL) 239 | return -ENOENT; 240 | if(_9premove(f) == -1) 241 | return -EACCES; 242 | clearcache(path); 243 | return 0; 244 | } 245 | 246 | int 247 | fsread(const char *path, char *buf, size_t size, off_t off, 248 | struct fuse_file_info *ffi) 249 | { 250 | FFid *f; 251 | int r; 252 | 253 | if(iscachectl(path)){ 254 | size = CACHECTLSIZE; 255 | if(off >= size) 256 | return 0; 257 | memcpy(buf, "cleared\n" + off, size - off); 258 | clearcache(path); 259 | return size; 260 | } 261 | f = (FFid*)ffi->fh; 262 | if(f->mode & O_WRONLY) 263 | return -EACCES; 264 | f->offset = off; 265 | if((r = _9pread(f, buf, size)) < 0) 266 | return -EIO; 267 | return r; 268 | } 269 | 270 | int 271 | fswrite(const char *path, const char *buf, size_t size, off_t off, 272 | struct fuse_file_info *ffi) 273 | { 274 | FFid *f; 275 | int r; 276 | 277 | if(iscachectl(path)){ 278 | clearcache(path); 279 | return size; 280 | } 281 | f = (FFid*)ffi->fh; 282 | if(f->mode & O_RDONLY) 283 | return -EACCES; 284 | f->offset = off; 285 | if((r = _9pwrite(f, (char*)buf, size)) < 0) 286 | return -EIO; 287 | clearcache(path); 288 | return r; 289 | } 290 | 291 | int 292 | fsopendir(const char *path, struct fuse_file_info *ffi) 293 | { 294 | FFid *f; 295 | FDir *d; 296 | 297 | if((d = lookupdir(path, GET)) != NULL){ 298 | ffi->fh = (u64int)NULL; 299 | return 0; 300 | } 301 | if((f = _9pwalk(path)) == NULL) 302 | return -ENOENT; 303 | f->mode = ffi->flags & O_ACCMODE; 304 | if(_9popen(f) == -1){ 305 | _9pclunk(f); 306 | return -EACCES; 307 | } 308 | if(!(f->qid.type & QTDIR)){ 309 | _9pclunk(f); 310 | return -ENOTDIR; 311 | } 312 | ffi->fh = (u64int)f; 313 | return 0; 314 | } 315 | 316 | int 317 | fsmkdir(const char *path, mode_t perm) 318 | { 319 | FFid *f; 320 | char *dname, *bname; 321 | 322 | if((f = _9pwalk(path)) != NULL){ 323 | _9pclunk(f); 324 | return -EEXIST; 325 | } 326 | dname = estrdup(path); 327 | bname = breakpath(dname); 328 | if((f = _9pwalk(dname)) == NULL){ 329 | free(dname); 330 | return -ENOENT; 331 | } 332 | if((f = _9pcreate(f, bname, perm, 1)) == NULL){ 333 | free(dname); 334 | return -EACCES; 335 | } 336 | _9pclunk(f); 337 | free(dname); 338 | clearcache(path); 339 | return 0; 340 | } 341 | 342 | int 343 | fsrmdir(const char *path) 344 | { 345 | FFid *f; 346 | 347 | if((f = _9pwalk(path)) == NULL) 348 | return -ENOENT; 349 | if((f->qid.type & QTDIR) == 0){ 350 | _9pclunk(f); 351 | return -ENOTDIR; 352 | } 353 | if(_9premove(f) == -1) 354 | return -EIO; 355 | clearcache(path); 356 | return 0; 357 | } 358 | 359 | int 360 | fsreaddir(const char *path, void *data, fuse_fill_dir_t ffd, 361 | off_t off, struct fuse_file_info *ffi) 362 | { 363 | FDir *f; 364 | Dir *d, *e; 365 | long n; 366 | struct stat s; 367 | 368 | ffd(data, ".", NULL, 0); 369 | ffd(data, "..", NULL, 0); 370 | ffd(data, CACHECTL, NULL, 0); 371 | if((f = lookupdir(path, GET)) != NULL){ 372 | d = f->dirs; 373 | n = f->ndirs; 374 | }else{ 375 | if((n = _9pdirread((FFid*)ffi->fh, &d)) < 0) 376 | return -EIO; 377 | } 378 | for(e = d; e < d+n; e++){ 379 | s.st_ino = e->qid.path; 380 | s.st_mode = e->mode & 0777; 381 | ffd(data, e->name, &s, 0); 382 | } 383 | return 0; 384 | } 385 | 386 | int 387 | fschmod(const char *path, mode_t perm) 388 | { 389 | FFid *f; 390 | Dir *d; 391 | 392 | if((f = _9pwalk(path)) == NULL) 393 | return -ENOENT; 394 | if((d = _9pstat(f)) == NULL){ 395 | _9pclunk(f); 396 | return -EIO; 397 | } 398 | d->mode = perm & 0777; 399 | if(_9pwstat(f, d) == -1){ 400 | _9pclunk(f); 401 | free(d); 402 | return -EACCES; 403 | } 404 | _9pclunk(f); 405 | free(d); 406 | clearcache(path); 407 | return 0; 408 | } 409 | 410 | struct fuse_operations fsops = { 411 | .getattr = fsgetattr, 412 | .truncate = fstruncate, 413 | .rename = fsrename, 414 | .open = fsopen, 415 | .create = fscreate, 416 | .unlink = fsunlink, 417 | .read = fsread, 418 | .write = fswrite, 419 | .opendir = fsopendir, 420 | .mkdir = fsmkdir, 421 | .rmdir = fsrmdir, 422 | .readdir = fsreaddir, 423 | .release = fsrelease, 424 | .releasedir = fsreleasedir, 425 | .chmod = fschmod 426 | }; 427 | 428 | int 429 | main(int argc, char *argv[]) 430 | { 431 | AuthInfo *ai; 432 | struct sockaddr_un uaddr; 433 | struct sockaddr *addr; 434 | struct addrinfo *ainfo; 435 | struct passwd *pw; 436 | char logstr[100], *fusearg[6], **fargp, port[10], user[30], *aname; 437 | int ch, doauth, uflag, n, alen, e; 438 | 439 | fargp = fusearg; 440 | *fargp++ = *argv; 441 | aname = NULL; 442 | doauth = 0; 443 | uflag = 0; 444 | strecpy(port, port+sizeof(port), "564"); 445 | if((pw = getpwuid(getuid())) == NULL) 446 | errx(1, "Could not get user"); 447 | strecpy(user, user+sizeof(user), pw->pw_name); 448 | while((ch = getopt(argc, argv, ":dnUap:u:A:")) != -1){ 449 | switch(ch){ 450 | case 'd': 451 | debug++; 452 | *fargp++ = "-d"; 453 | break; 454 | case 'n': 455 | break; 456 | case 'U': 457 | uflag++; 458 | break; 459 | case 'a': 460 | doauth++; 461 | break; 462 | case 'p': 463 | strecpy(port, port+sizeof(port), optarg); 464 | break; 465 | case 'u': 466 | strecpy(user, user+sizeof(user), optarg); 467 | break; 468 | case 'A': 469 | aname = strdup(optarg); 470 | break; 471 | default: 472 | usage(); 473 | break; 474 | } 475 | } 476 | argc -= optind; 477 | argv += optind; 478 | if(argc != 2) 479 | usage(); 480 | *fargp++ = "-s"; 481 | *fargp++ = argv[1]; 482 | if(debug){ 483 | snprintf(logstr, sizeof(logstr), "/tmp/9pfs-%d.log", getpid()); 484 | if((logfile = fopen(logstr, "w")) == NULL) 485 | err(1, "Could not open the log"); 486 | setlinebuf(logfile); 487 | } 488 | 489 | if(uflag){ 490 | uaddr.sun_family = AF_UNIX; 491 | n = sizeof(uaddr.sun_path); 492 | strecpy(uaddr.sun_path, uaddr.sun_path+n, argv[0]); 493 | addr = (struct sockaddr*)&uaddr; 494 | alen = sizeof(uaddr); 495 | }else{ 496 | if((e = getaddrinfo(argv[0], port, NULL, &ainfo)) != 0) 497 | errx(1, "%s", gai_strerror(e)); 498 | addr = ainfo->ai_addr; 499 | alen = ainfo->ai_addrlen; 500 | } 501 | srvfd = socket(addr->sa_family, SOCK_STREAM, 0); 502 | if(connect(srvfd, addr, alen) == -1) 503 | err(1, "Could not connect to 9p server"); 504 | if(uflag == 0) 505 | freeaddrinfo(ainfo); 506 | 507 | init9p(); 508 | msize = _9pversion(MSIZE); 509 | if(doauth){ 510 | authfid = _9pauth(AUTHFID, user, NULL); 511 | ai = auth_proxy(authfid, auth_getkey, "proto=p9any role=client"); 512 | if(ai == NULL) 513 | err(1, "Could not establish authentication"); 514 | auth_freeAI(ai); 515 | } 516 | rootfid = _9pattach(ROOTFID, doauth ? AUTHFID : NOFID, user, aname); 517 | if((rootdir = _9pstat(rootfid)) == NULL) 518 | errx(1, "Could not stat root"); 519 | DPRINT("About to fuse_main\n"); 520 | fuse_main(fargp - fusearg, fusearg, &fsops, NULL); 521 | exit(0); 522 | } 523 | 524 | void 525 | dir2stat(struct stat *s, Dir *d) 526 | { 527 | struct passwd *p; 528 | struct group *g; 529 | 530 | s->st_dev = d->dev; 531 | s->st_ino = d->qid.path; 532 | s->st_mode = d->mode & 0777; 533 | if(d->mode & DMDIR) 534 | s->st_mode |= S_IFDIR; 535 | else 536 | s->st_mode |= S_IFREG; 537 | s->st_nlink = 1; 538 | s->st_uid = (p = getpwnam(d->uid)) == NULL ? 0 : p->pw_uid; 539 | s->st_gid = (g = getgrnam(d->gid)) == NULL ? 0 : g->gr_gid; 540 | s->st_size = d->length; 541 | s->st_blksize = msize - IOHDRSZ; 542 | s->st_blocks = d->length / (msize - IOHDRSZ) + 1; 543 | s->st_atime = d->atime; 544 | s->st_mtime = s->st_ctime = d->mtime; 545 | s->st_rdev = 0; 546 | } 547 | 548 | void 549 | clearcache(const char *path) 550 | { 551 | char *dname; 552 | 553 | dname = estrdup(path); 554 | breakpath(dname); 555 | lookupdir(dname, DEL); 556 | free(dname); 557 | return; 558 | } 559 | 560 | Dir* 561 | iscached(const char *path) 562 | { 563 | FDir *fd; 564 | Dir *d, e; 565 | char *dname, *bname; 566 | 567 | if(strcmp(path, "/") == 0) 568 | return rootdir; 569 | dname = estrdup(path); 570 | bname = breakpath(dname); 571 | if((fd = lookupdir(dname, GET)) == NULL){ 572 | free(dname); 573 | return NULL; 574 | } 575 | e.name = bname; 576 | d = bsearch(&e, fd->dirs, fd->ndirs, sizeof(*fd->dirs), dircmp); 577 | free(dname); 578 | return d; 579 | } 580 | 581 | Dir* 582 | addtocache(const char *path) 583 | { 584 | FFid *f; 585 | Dir *d; 586 | char *dname; 587 | long n; 588 | 589 | DPRINT("addtocache %s\n", path); 590 | dname = estrdup(path); 591 | breakpath(dname); 592 | if((f = _9pwalk(dname)) == NULL){ 593 | free(dname); 594 | return NULL; 595 | } 596 | f->mode |= O_RDONLY; 597 | if(_9popen(f) == -1){ 598 | free(dname); 599 | return NULL; 600 | } 601 | DPRINT("addtocache about to dirread\n"); 602 | if((n = _9pdirread(f, &d)) < 0){ 603 | free(dname); 604 | return NULL; 605 | } 606 | free(dname); 607 | return iscached(path); 608 | } 609 | 610 | int 611 | iscachectl(const char *path) 612 | { 613 | char *s; 614 | 615 | s = strrchr(path, '/'); 616 | s++; 617 | if(strcmp(s, CACHECTL) == 0) 618 | return 1; 619 | return 0; 620 | } 621 | 622 | char* 623 | breakpath(char *dname) 624 | { 625 | char *bname; 626 | 627 | bname = strrchr(dname, '/'); 628 | *bname++ = '\0'; 629 | return bname; 630 | } 631 | 632 | void 633 | usage(void) 634 | { 635 | fprintf(stderr, "Usage: 9pfs [-anU] [-A aname] [-p port] [-u user] service mtpt\n"); 636 | exit(2); 637 | } 638 | --------------------------------------------------------------------------------