#ifdef __linux__#define _GNU_SOURCE#else#define _DEFAULT_SOURCE#endif#define _FILE_OFFSET_BITS 64#include <dirent.h>#include <errno.h>#include <fcntl.h>#include <grp.h>#include <inttypes.h>#include <pwd.h>#include <signal.h>#include <stdarg.h>#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/select.h>#include <sys/stat.h>#include <sys/time.h>#include <unistd.h>#if defined(__APPLE__) || defined(__NetBSD__)#define st_atim st_atimespec#define st_ctim st_ctimespec#endif#include "c9/proto.h"#include "parg.h" #define max(a,b) ((a)>(b)?(a):(b))#define used(x) ((void)(x))#define nelem(x) (int)(sizeof(x)/sizeof((x)[0])) uint32_t crc32(const void *data, int len); typedef struct Fid Fid;typedef struct Id Id;typedef struct Tag Tag; enum{ Canrd = 1<<0, Canwr = 1<<1,}; struct Fid{ char *path; /* full path */ char *name; /* base name */ DIR *dir; /* set when it's an opened directory */ uint64_t diroffset; /* to read dirs correctly */ C9qid qid; C9fid fid; C9mode mode; /* mode in which the file was opened */ uint32_t iounit; int fd; /* set when it's an opened file */}; struct Tag{ C9tag tag;}; struct Id{ char *name; uint32_t id;}; static char *t2s[] ={ [Tversion-Tversion] = "Tversion", [Tauth-Tversion] = "Tauth", [Tattach-Tversion] = "Tattach", [Tflush-Tversion] = "Tflush", [Twalk-Tversion] = "Twalk", [Topen-Tversion] = "Topen", [Tcreate-Tversion] = "Tcreate", [Tread-Tversion] = "Tread", [Twrite-Tversion] = "Twrite", [Tclunk-Tversion] = "Tclunk", [Tremove-Tversion] = "Tremove", [Tstat-Tversion] = "Tstat", [Twstat-Tversion] = "Twstat",}; static char *modes[] ={ "read", "write", "rdwr", "exec",}; static char *Enoauth = "authentication not required";static char *Eunknownfid = "unknown fid";static char *Enowstat = "wstat prohibited";static char *Eperm = "permission denied";static char *Enowrite = "write prohibited";static char *Enomem = "out of memory";static char *Edupfid = "duplicate fid";static char *Ewalknodir = "walk in non-directory";static char *Enotfound = "file not found";static char *Eduptag = "duplicate tag";static char *Ebotch = "9P protocol botch";static char *Enocreate = "create prohibited";static char *Eisdir = "is a directory";static char *Ebadoffset = "bad offset"; static int in, out, eof;static C9ctx ctx;static int debug, rootescape, anon;static Fid **fids;static int numfids;static Tag **tags;static int numtags;static char *rootpath;static size_t rootlen;static C9qid walkqids[C9maxpathel];static uint8_t *rdbuf;static uint8_t *wrbuf;static uint32_t wroff, wrend, wrbufsz;static Id *uids, *gids;static int numuids, numgids; __attribute__((format(printf, 1, 2)))static voidtrace(const char *fmt, ...){ va_list ap; if(debug == 0) return; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);} static intcanrw(int rdonly, int block){ struct timeval t; fd_set r, w, e; int fl; FD_ZERO(&r); FD_SET(in, &r); FD_ZERO(&w); if(rdonly == 0) FD_SET(out, &w); FD_ZERO(&e); FD_SET(in, &e); FD_SET(out, &e); memset(&t, 0, sizeof(t)); t.tv_usec = 1000; for(;;){ errno = 0; if(select(max(in, out) + 1, &r, &w, &e, block ? NULL : &t) < 0 || FD_ISSET(in, &e) || FD_ISSET(out, &e)){ if(errno == EINTR) continue; return -1; } break; } fl = 0; if(FD_ISSET(in, &r)) fl |= Canrd; if(FD_ISSET(out, &w)) fl |= Canwr; return fl;} static intwrsend(void){ uint32_t n; int w; if(wrend == 0) return 0; for(n = 0; n < wrend; n += w){ errno = 0; if((w = write(out, wrbuf+n, wrend-n)) <= 0){ if(errno == EINTR) continue; if(errno != EPIPE) /* remote end closed */ perror("write"); return -1; } } if(debug >= 2) trace("<- %d bytes, %d left\n", wrend, wroff-wrend); memmove(wrbuf, wrbuf+wrend, wroff-wrend); wroff = wroff - wrend; return 0;} static inthastag(C9tag tag){ int i; for(i = 0; i < numtags; i++){ if(tags[i] != NULL && tags[i]->tag == tag) return 1; } return 0;} static intstatpath(char *path, struct stat *st, char **err){ if(stat(path, st) == 0) return 0; if(errno == EACCES) *err = Eperm; else if(errno == ENOMEM) *err = Enomem; else if(errno == ENOTDIR) *err = Ewalknodir; else if(errno == ENOENT) *err = Enotfound; else *err = strerror(errno); return -1;} #define copyadvance(p, data, size) do{ memmove(p, data, size); p += size; }while(0) static voidstat2qid(struct stat *st, C9qid *qid, uint32_t *iounit){ uint8_t b[sizeof(st->st_ctim) + sizeof(st->st_size) + sizeof(st->st_dev) + sizeof(st->st_ino)], *p; int fmt; qid->path = st->st_ino; qid->type = C9qtfile; p = b; copyadvance(p, &st->st_ctime, sizeof(st->st_ctim)); copyadvance(p, &st->st_size, sizeof(st->st_size)); copyadvance(p, &st->st_dev, sizeof(st->st_dev)); copyadvance(p, &st->st_ino, sizeof(st->st_ino)); qid->version = crc32(b, p-b); fmt = st->st_mode & S_IFMT; if(fmt == S_IFDIR) qid->type |= C9qtdir; if((st->st_mode & 0222) != 0 && (fmt == S_IFCHR || fmt == S_IFCHR || fmt == S_IFSOCK || fmt == S_IFIFO)) qid->type |= C9qtappend; if(iounit != NULL) *iounit = 0;} static Fid *newfid(C9fid fid, char *path, char **err){ Fid *f, **newfids; struct stat st; int i; for(i = 0; i < numfids; i++){ if(fids[i] != NULL && fids[i]->fid == fid){ *err = Edupfid; return NULL; } } if(statpath(path, &st, err) != 0) return NULL; if((f = calloc(1, sizeof(*f))) == NULL || (f->path = strdup(path)) == NULL){nomem: if(f != NULL){ free(f->path); free(f); } *err = Enomem; return NULL; } f->fd = -1; f->name = strrchr(f->path, '/'); if(f->name == NULL) f->name = f->path; else f->name++; if(f->name[0] == 0) fprintf(stderr, "%s -> empty file name\n", f->path); for(i = 0; i < numfids; i++){ if(fids[i] == NULL){ fids[i] = f; break; } } if(i >= numfids){ if((newfids = realloc(fids, (numfids+1)*sizeof(*fids))) == NULL) goto nomem; fids = newfids; fids[numfids++] = f; } f->fid = fid; stat2qid(&st, &f->qid, &f->iounit); return f;} static Fid *findfid(C9fid fid, char **err){ int i; for(i = 0; i < numfids; i++){ if(fids[i] != NULL && fids[i]->fid == fid){ return fids[i]; } } *err = Eunknownfid; return NULL;} static intdelfid(C9fid fid, char **err){ Fid *f; int i; for(i = 0; i < numfids; i++){ f = fids[i]; if(f != NULL && f->fid == fid){ if(f->dir != NULL) closedir(f->dir); else if(f->fd >= 0) close(f->fd); free(f->path); free(f); fids[i] = NULL; return 0; } } *err = Eunknownfid; return -1;} static inthasperm(struct stat *st, C9mode mode, char **err){ int m, stm, fmt; m = mode & 0xf; stm = st->st_mode & 0777; *err = Eperm; if(((stm & 0111) == 0 || (stm & 0444) == 0) && m == C9exec) /* executing needs rx */ return 0; if((stm & 0222) == 0 && (m == C9write || m == C9rdwr)) /* writing needs w */ return 0; if((stm & 0444) == 0 && m != C9write) /* reading needs r */ return 0; fmt = st->st_mode & S_IFMT; if(fmt == S_IFDIR && ((stm & 0111) == 0 || (stm & 0444) == 0)) /* dirs need rx */ return 0; *err = NULL; return 1;} static Fid *walk(C9fid fid, C9fid nfid, char *el[], C9qid *qids[], char **err){ Fid *f; char *path, *real, *p; struct stat st; int i, plen, ellen; if((f = findfid(fid, err)) == NULL) return NULL; if(el[0] == NULL){ /* nwname = 0 */ qids[0] = NULL; if(fid == nfid) return f; return newfid(nfid, f->path, err); } if((f->qid.type & C9qtdir) == 0){ /* has to be a dir */ *err = Ewalknodir; return NULL; } p = strdup(f->path); f = NULL; for(i = 0; el[i] != NULL; i++){ plen = strlen(p); ellen = strlen(el[i]); path = malloc(plen + 1 + ellen + 1); memmove(path, p, plen); path[plen] = '/'; memmove(path+plen+1, el[i], ellen); path[plen+1+ellen] = 0; if(!rootescape){ real = realpath(path, NULL); free(path); if(real == NULL) break; if(strlen(real) < rootlen){ /* don't escape root */ free(real); real = strdup(rootpath); } }else{ real = path; } free(p); p = real; if(statpath(p, &st, err) != 0) break; if(el[i+1] != NULL && !hasperm(&st, C9read, err)) break; qids[i] = &walkqids[i]; stat2qid(&st, qids[i], NULL); } qids[i] = NULL; if(el[i] == NULL){ /* could walk all the way */ f = newfid(nfid, p, err); if(f != NULL && f->name[0] == '/' && f->name[1] == 0) /* root */ f->name = "/"; }else if(i != 0){ /* didn't fail on the first one */ *err = NULL; } free(p); return f;} static intmode2unix(C9mode mode, int qidtype){ int omode; omode = O_NOFOLLOW; if((mode & 3) == C9read) omode |= O_RDONLY; else if((mode & 3) == C9write) omode |= O_WRONLY; else if((mode & 3) == C9rdwr) omode |= O_RDWR; if(mode & C9trunc) omode |= O_TRUNC;#ifdef __linux__ if(mode & C9rclose) omode |= O_TMPFILE;#endif if((qidtype & C9qtappend) != 0) omode |= O_APPEND; return omode;} static intopenfid(Fid *f, C9mode mode, char **err){ struct stat st; int omode; if(stat(f->path, &st) != 0 || !hasperm(&st, mode, err)) goto error; stat2qid(&st, &f->qid, &f->iounit); if((f->qid.type & C9qtdir) != 0){ if((f->dir = opendir(f->path)) == NULL){ *err = strerror(errno); return -1; } f->fd = dirfd(f->dir); }else{ omode = mode2unix(mode, f->qid.type); f->fd = open(f->path, omode); } if(f->fd < 0) goto error; f->mode = mode;Lines 1–500 of 1106. Load more
#ifdef __linux__ #define _GNU_SOURCE #else #define _DEFAULT_SOURCE #endif #define _FILE_OFFSET_BITS 64 #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <grp.h> #include <inttypes.h> #include <pwd.h> #include <signal.h> #include <stdarg.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> #if defined(__APPLE__) || defined(__NetBSD__) #define st_atim st_atimespec #define st_ctim st_ctimespec #endif #include "c9/proto.h" #include "parg.h" #define max(a,b) ((a)>(b)?(a):(b)) #define used(x) ((void)(x)) #define nelem(x) (int)(sizeof(x)/sizeof((x)[0])) uint32_t crc32(const void *data, int len); typedef struct Fid Fid; typedef struct Id Id; typedef struct Tag Tag; enum { Canrd = 1<<0, Canwr = 1<<1, }; struct Fid { char *path; /* full path */ char *name; /* base name */ DIR *dir; /* set when it's an opened directory */ uint64_t diroffset; /* to read dirs correctly */ C9qid qid; C9fid fid; C9mode mode; /* mode in which the file was opened */ uint32_t iounit; int fd; /* set when it's an opened file */ }; struct Tag { C9tag tag; }; struct Id { char *name; uint32_t id; }; static char *t2s[] = { [Tversion-Tversion] = "Tversion", [Tauth-Tversion] = "Tauth", [Tattach-Tversion] = "Tattach", [Tflush-Tversion] = "Tflush", [Twalk-Tversion] = "Twalk", [Topen-Tversion] = "Topen", [Tcreate-Tversion] = "Tcreate", [Tread-Tversion] = "Tread", [Twrite-Tversion] = "Twrite", [Tclunk-Tversion] = "Tclunk", [Tremove-Tversion] = "Tremove", [Tstat-Tversion] = "Tstat", [Twstat-Tversion] = "Twstat", }; static char *modes[] = { "read", "write", "rdwr", "exec", }; static char *Enoauth = "authentication not required"; static char *Eunknownfid = "unknown fid"; static char *Enowstat = "wstat prohibited"; static char *Eperm = "permission denied"; static char *Enowrite = "write prohibited"; static char *Enomem = "out of memory"; static char *Edupfid = "duplicate fid"; static char *Ewalknodir = "walk in non-directory"; static char *Enotfound = "file not found"; static char *Eduptag = "duplicate tag"; static char *Ebotch = "9P protocol botch"; static char *Enocreate = "create prohibited"; static char *Eisdir = "is a directory"; static char *Ebadoffset = "bad offset"; static int in, out, eof; static C9ctx ctx; static int debug, rootescape, anon; static Fid **fids; static int numfids; static Tag **tags; static int numtags; static char *rootpath; static size_t rootlen; static C9qid walkqids[C9maxpathel]; static uint8_t *rdbuf; static uint8_t *wrbuf; static uint32_t wroff, wrend, wrbufsz; static Id *uids, *gids; static int numuids, numgids; __attribute__((format(printf, 1, 2))) static void trace(const char *fmt, ...) { va_list ap; if(debug == 0) return; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } static int canrw(int rdonly, int block) { struct timeval t; fd_set r, w, e; int fl; FD_ZERO(&r); FD_SET(in, &r); FD_ZERO(&w); if(rdonly == 0) FD_SET(out, &w); FD_ZERO(&e); FD_SET(in, &e); FD_SET(out, &e); memset(&t, 0, sizeof(t)); t.tv_usec = 1000; for(;;){ errno = 0; if(select(max(in, out) + 1, &r, &w, &e, block ? NULL : &t) < 0 || FD_ISSET(in, &e) || FD_ISSET(out, &e)){ if(errno == EINTR) continue; return -1; } break; } fl = 0; if(FD_ISSET(in, &r)) fl |= Canrd; if(FD_ISSET(out, &w)) fl |= Canwr; return fl; } static int wrsend(void) { uint32_t n; int w; if(wrend == 0) return 0; for(n = 0; n < wrend; n += w){ errno = 0; if((w = write(out, wrbuf+n, wrend-n)) <= 0){ if(errno == EINTR) continue; if(errno != EPIPE) /* remote end closed */ perror("write"); return -1; } } if(debug >= 2) trace("<- %d bytes, %d left\n", wrend, wroff-wrend); memmove(wrbuf, wrbuf+wrend, wroff-wrend); wroff = wroff - wrend; return 0; } static int hastag(C9tag tag) { int i; for(i = 0; i < numtags; i++){ if(tags[i] != NULL && tags[i]->tag == tag) return 1; } return 0; } static int statpath(char *path, struct stat *st, char **err) { if(stat(path, st) == 0) return 0; if(errno == EACCES) *err = Eperm; else if(errno == ENOMEM) *err = Enomem; else if(errno == ENOTDIR) *err = Ewalknodir; else if(errno == ENOENT) *err = Enotfound; else *err = strerror(errno); return -1; } #define copyadvance(p, data, size) do{ memmove(p, data, size); p += size; }while(0) static void stat2qid(struct stat *st, C9qid *qid, uint32_t *iounit) { uint8_t b[sizeof(st->st_ctim) + sizeof(st->st_size) + sizeof(st->st_dev) + sizeof(st->st_ino)], *p; int fmt; qid->path = st->st_ino; qid->type = C9qtfile; p = b; copyadvance(p, &st->st_ctime, sizeof(st->st_ctim)); copyadvance(p, &st->st_size, sizeof(st->st_size)); copyadvance(p, &st->st_dev, sizeof(st->st_dev)); copyadvance(p, &st->st_ino, sizeof(st->st_ino)); qid->version = crc32(b, p-b); fmt = st->st_mode & S_IFMT; if(fmt == S_IFDIR) qid->type |= C9qtdir; if((st->st_mode & 0222) != 0 && (fmt == S_IFCHR || fmt == S_IFCHR || fmt == S_IFSOCK || fmt == S_IFIFO)) qid->type |= C9qtappend; if(iounit != NULL) *iounit = 0; } static Fid * newfid(C9fid fid, char *path, char **err) { Fid *f, **newfids; struct stat st; int i; for(i = 0; i < numfids; i++){ if(fids[i] != NULL && fids[i]->fid == fid){ *err = Edupfid; return NULL; } } if(statpath(path, &st, err) != 0) return NULL; if((f = calloc(1, sizeof(*f))) == NULL || (f->path = strdup(path)) == NULL){ nomem: if(f != NULL){ free(f->path); free(f); } *err = Enomem; return NULL; } f->fd = -1; f->name = strrchr(f->path, '/'); if(f->name == NULL) f->name = f->path; else f->name++; if(f->name[0] == 0) fprintf(stderr, "%s -> empty file name\n", f->path); for(i = 0; i < numfids; i++){ if(fids[i] == NULL){ fids[i] = f; break; } } if(i >= numfids){ if((newfids = realloc(fids, (numfids+1)*sizeof(*fids))) == NULL) goto nomem; fids = newfids; fids[numfids++] = f; } f->fid = fid; stat2qid(&st, &f->qid, &f->iounit); return f; } static Fid * findfid(C9fid fid, char **err) { int i; for(i = 0; i < numfids; i++){ if(fids[i] != NULL && fids[i]->fid == fid){ return fids[i]; } } *err = Eunknownfid; return NULL; } static int delfid(C9fid fid, char **err) { Fid *f; int i; for(i = 0; i < numfids; i++){ f = fids[i]; if(f != NULL && f->fid == fid){ if(f->dir != NULL) closedir(f->dir); else if(f->fd >= 0) close(f->fd); free(f->path); free(f); fids[i] = NULL; return 0; } } *err = Eunknownfid; return -1; } static int hasperm(struct stat *st, C9mode mode, char **err) { int m, stm, fmt; m = mode & 0xf; stm = st->st_mode & 0777; *err = Eperm; if(((stm & 0111) == 0 || (stm & 0444) == 0) && m == C9exec) /* executing needs rx */ return 0; if((stm & 0222) == 0 && (m == C9write || m == C9rdwr)) /* writing needs w */ return 0; if((stm & 0444) == 0 && m != C9write) /* reading needs r */ return 0; fmt = st->st_mode & S_IFMT; if(fmt == S_IFDIR && ((stm & 0111) == 0 || (stm & 0444) == 0)) /* dirs need rx */ return 0; *err = NULL; return 1; } static Fid * walk(C9fid fid, C9fid nfid, char *el[], C9qid *qids[], char **err) { Fid *f; char *path, *real, *p; struct stat st; int i, plen, ellen; if((f = findfid(fid, err)) == NULL) return NULL; if(el[0] == NULL){ /* nwname = 0 */ qids[0] = NULL; if(fid == nfid) return f; return newfid(nfid, f->path, err); } if((f->qid.type & C9qtdir) == 0){ /* has to be a dir */ *err = Ewalknodir; return NULL; } p = strdup(f->path); f = NULL; for(i = 0; el[i] != NULL; i++){ plen = strlen(p); ellen = strlen(el[i]); path = malloc(plen + 1 + ellen + 1); memmove(path, p, plen); path[plen] = '/'; memmove(path+plen+1, el[i], ellen); path[plen+1+ellen] = 0; if(!rootescape){ real = realpath(path, NULL); free(path); if(real == NULL) break; if(strlen(real) < rootlen){ /* don't escape root */ free(real); real = strdup(rootpath); } }else{ real = path; } free(p); p = real; if(statpath(p, &st, err) != 0) break; if(el[i+1] != NULL && !hasperm(&st, C9read, err)) break; qids[i] = &walkqids[i]; stat2qid(&st, qids[i], NULL); } qids[i] = NULL; if(el[i] == NULL){ /* could walk all the way */ f = newfid(nfid, p, err); if(f != NULL && f->name[0] == '/' && f->name[1] == 0) /* root */ f->name = "/"; }else if(i != 0){ /* didn't fail on the first one */ *err = NULL; } free(p); return f; } static int mode2unix(C9mode mode, int qidtype) { int omode; omode = O_NOFOLLOW; if((mode & 3) == C9read) omode |= O_RDONLY; else if((mode & 3) == C9write) omode |= O_WRONLY; else if((mode & 3) == C9rdwr) omode |= O_RDWR; if(mode & C9trunc) omode |= O_TRUNC; #ifdef __linux__ if(mode & C9rclose) omode |= O_TMPFILE; #endif if((qidtype & C9qtappend) != 0) omode |= O_APPEND; return omode; } static int openfid(Fid *f, C9mode mode, char **err) { struct stat st; int omode; if(stat(f->path, &st) != 0 || !hasperm(&st, mode, err)) goto error; stat2qid(&st, &f->qid, &f->iounit); if((f->qid.type & C9qtdir) != 0){ if((f->dir = opendir(f->path)) == NULL){ *err = strerror(errno); return -1; } f->fd = dirfd(f->dir); }else{ omode = mode2unix(mode, f->qid.type); f->fd = open(f->path, omode); } if(f->fd < 0) goto error; f->mode = mode;