ref: d1d3a8d1fe4af711ca6628b457489b4a56e0908c
dir: /9pex.c/
#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.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;
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;
return 0;
error:
if(*err == NULL)
*err = strerror(errno);
if(f->dir != NULL)
closedir(f->dir);
else if(f->fd >= 0)
close(f->fd);
f->dir = NULL;
f->fd = -1;
return -1;
}
static char *
uid2str(uid_t uid, char **err)
{
struct passwd *p;
Id *newuids;
int i;
for(i = 0; i < numuids; i++){
if(uids[i].id == uid)
return uids[i].name;
}
if((p = getpwuid(uid)) == NULL){
*err = strerror(errno);
return NULL;
}
if((newuids = realloc(uids, sizeof(*uids)*(numuids+1))) == NULL){
*err = Enomem;
return NULL;
}
uids = newuids;
uids[numuids].id = uid;
uids[numuids].name = strdup(p->pw_name);
return uids[numuids++].name;
}
static char *
gid2str(gid_t gid, char **err)
{
struct group *g;
Id *newgids;
int i;
for(i = 0; i < numgids; i++){
if(gids[i].id == gid)
return gids[i].name;
}
if((g = getgrgid(gid)) == NULL){
*err = strerror(errno);
return NULL;
}
if((newgids = realloc(gids, sizeof(*gids)*(numgids+1))) == NULL){
*err = Enomem;
return NULL;
}
gids = newgids;
gids[numgids].id = gid;
gids[numgids].name = strdup(g->gr_name);
return gids[numgids++].name;
}
static int
stat2c9stat(char *name, struct stat *st, C9stat *stout, char **err)
{
int fmt;
memset(stout, 0, sizeof(*stout));
stout->size = st->st_size;
stat2qid(st, &stout->qid, NULL);
stout->name = name;
stout->atime = st->st_atim.tv_sec;
stout->mtime = st->st_ctim.tv_sec;
fmt = st->st_mode & S_IFMT;
if(fmt == S_IFDIR)
stout->mode |= C9stdir;
if(fmt == S_IFCHR || fmt == S_IFCHR || fmt == S_IFSOCK || fmt == S_IFIFO)
stout->mode |= C9stappend;
stout->mode |= st->st_mode & 0x1ff;
if((stout->uid = uid2str(st->st_uid, err)) == NULL)
return -1;
if((stout->gid = gid2str(st->st_gid, err)) == NULL)
return -1;
return 0;
}
static int
statfid(Fid *f, C9stat *stout, char **err)
{
struct stat st;
int r;
if(f->fd >= 0)
r = fstat(f->fd, &st);
else
r = stat(f->path, &st);
if(r != 0){
*err = strerror(errno);
return -1;
}
return stat2c9stat(f->name, &st, stout, err);
}
static uint8_t *
ctxread(C9ctx *c, uint32_t size, int *err)
{
uint32_t n;
int r;
used(c);
*err = 0;
for(n = 0; n < size; n += r){
errno = 0;
if((r = read(in, rdbuf+n, size-n)) <= 0){
if(r == EINTR)
continue;
if(r == 0){
eof = 1;
}else{
*err = 1;
perror("ctxread");
}
return NULL;
}
}
return rdbuf;
}
static uint8_t *
ctxbegin(C9ctx *c, uint32_t size)
{
uint8_t *b;
used(c);
if(wroff + size > wrbufsz){
if(wrsend() != 0 || wroff + size > wrbufsz)
return NULL;
}
b = wrbuf + wroff;
wroff += size;
return b;
}
static int
ctxend(C9ctx *c)
{
used(c);
wrend = wroff;
return 0;
}
__attribute__((format(printf, 1, 2)))
static void
ctxerror(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static int
s9do(C9error e, char **err)
{
if(e == 0){
*err = NULL;
return 0;
}
switch(e){
case C9Einit: *err = "c9: initialization failed"; break;
case C9Ever: *err = "c9: protocol version doesn't match"; break;
case C9Epkt: *err = "c9: incoming packet error"; break;
case C9Etag: *err = "c9: no free tags or bad tag"; break;
case C9Ebuf: *err = Enomem; break;
case C9Epath: *err = "c9: path is too long or just invalid"; break;
case C9Eflush: *err = "c9: limit of outstanding flushes reached"; break;
case C9Esize: *err = "c9: can't fit data in one message"; break;
case C9Estr: *err = "c9: bad string"; break;
default: *err = "c9: unknown error"; break;
}
return -1;
}
static int
readf(C9ctx *c, C9tag tag, Fid *f, uint64_t offset, uint32_t size, char **err)
{
struct stat st;
void *p;
struct dirent *e;
ssize_t n;
C9stat c9st[16], *c9stp[16];
long dirpos[16];
int i, num, res;
if(size > c->msize - 12) /* make sure it fits */
size = c->msize - 12;
if(f->dir == NULL){ /* a file */
if((p = malloc(size)) == NULL){
*err = Enomem;
return -1;
}
if((n = pread(f->fd, p, size, offset)) < 0){
*err = strerror(errno);
free(p);
return -1;
}
res = s9do(s9read(c, tag, p, n), err);
free(p);
if(res != 0)
return -1;
trace("<- Rread tag=%d count=%zd data=...\n", tag, n);
return 0;
}
/* dir */
if(offset != f->diroffset){
if(offset == 0){
rewinddir(f->dir);
f->diroffset = 0;
}else{
*err = Ebadoffset;
return -1;
}
}
res = 0;
num = 0;
for(i = 0; i < nelem(c9st); i++){
/* so we can rewind in case another stat doesn't fit */
dirpos[i] = telldir(f->dir);
errno = 0;
if((e = readdir(f->dir)) == NULL && errno != 0){
*err = strerror(errno);
res = -1;
goto done;
}
if(e == NULL) /* eof */
break;
if(e->d_name[0] == '.' &&
(e->d_name[1] == 0 || ((e->d_name[1] == '.' && e->d_name[2] == 0))))
continue;
if(fstatat(f->fd, e->d_name, &st, 0) != 0){
*err = strerror(errno);
if(fstatat(f->fd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0){
/* broken symlink, try to stat the link itself */
res = -1;
goto done;
}
}
if(stat2c9stat(e->d_name, &st, &c9st[i], err) != 0){
res = -1;
goto done;
}
c9st[i].name = strdup(c9st[i].name);
c9stp[num++] = &c9st[i];
}
i = num;
if(s9do(s9readdir(c, tag, c9stp, &num, &f->diroffset, size), err) != 0){
res = -1;
goto done;
}
trace("<- Rread tag=%d count=%"PRIu64" data=...\n", tag, f->diroffset - offset);
if(i != num)
seekdir(f->dir, dirpos[num]);
done:
for(i = 0; i < num; i++)
free(c9stp[i]->name);
return res;
}
static void
ctxt(C9ctx *c, C9t *t)
{
Fid *f;
C9qid *qids[C9maxpathel+1];
char *err, *err2;
C9stat st;
int i;
trace("-> %s tag=%d", t2s[t->type-Tversion], t->tag);
err = NULL;
if(hastag(t->tag)){
err = Eduptag;
}else{
switch(t->type){
case Tversion:
trace(" msize=%d\n", t->version.msize);
if(s9do(s9version(c), &err) == 0)
trace("<- Rversion msize=%d\n", c->msize);
break;
case Tauth:
trace(" afid=%d uname=\"%s\" aname=\"%s\"\n",
t->auth.afid, t->auth.uname, t->auth.aname);
err = Enoauth;
break;
case Tattach:
trace(" afid=%d fid=%d uname=\"%s\" aname=\"%s\"\n",
t->attach.afid, t->fid, t->attach.uname, t->attach.aname);
if(t->attach.afid != C9nofid){
err = Eunknownfid;
}else if((f = newfid(t->fid, rootpath, &err)) != NULL){
f->name = "/";
if(s9do(s9attach(c, t->tag, &f->qid), &err) == 0)
trace("<- Rattach\n");
}
break;
case Tflush:
trace(" oldtag=%d\n", t->flush.oldtag);
/* FIXME flush it for realz */
if(s9do(s9flush(c, t->tag), &err) == 0)
trace("<- Rflush tag=%d\n", t->tag);
break;
case Twalk:
trace(" fid=%d newfid=%d", t->fid, t->walk.newfid);
for(i = 0; t->walk.wname[i] != NULL; i++)
trace(" \"%s\"", t->walk.wname[i]);
trace("\n");
walk(t->fid, t->walk.newfid, t->walk.wname, qids, &err);
if(err == NULL && s9do(s9walk(c, t->tag, qids), &err) == 0){
trace("<- Rwalk tag=%d ", t->tag);
for(i = 0; qids[i] != NULL; i++)
trace("qid=[path=%"PRIu64" type=0x%02x version=%"PRIu32"] ",
qids[i]->path, qids[i]->type, qids[i]->version);
trace("\n");
}
break;
case Topen:
trace(" fid=%d mode=0x%02x\n", t->fid, t->open.mode);
if((f = findfid(t->fid, &err)) != NULL){
if(f->fd >= 0)
err = Ebotch;
else if(t->open.mode != C9read && t->open.mode != C9exec)
err = Eperm;
else if(t->open.mode != C9read && (f->qid.type & C9qtdir) != 0)
err = Eisdir;
else if(openfid(f, t->open.mode, &err) == 0 &&
s9do(s9open(c, t->tag, &f->qid, f->iounit), &err) == 0){
trace("<- Ropen tag=%d qid=[path=%"PRIu64" type=0x%02x version=%"PRIu32"] iounit=%d\n",
t->tag, f->qid.path, f->qid.type, f->qid.version, f->iounit);
}
}
break;
case Tcreate:
trace("...\n");
err = Enocreate;
break;
case Tread:
trace(" fid=%d offset=%"PRIu64" count=%"PRIu32"\n", t->fid, t->read.offset, t->read.size);
if((f = findfid(t->fid, &err)) != NULL){
if((f->dir == NULL && f->fd < 0) || (f->mode & 0xf) == C9write)
err = Ebotch;
else if(readf(c, t->tag, f, t->read.offset, t->read.size, &err) != 0)
trace("readf failed\n");
}
break;
case Twrite:
trace("...\n");
err = Enowrite;
break;
case Tclunk:
trace(" fid=%d\n", t->fid);
if(delfid(t->fid, &err) == 0 && s9do(s9clunk(c, t->tag), &err) == 0)
trace("<- Rclunk tag=%d\n", t->tag);
break;
case Tremove:
trace("\n");
err = Eperm;
break;
case Tstat:
trace(" fid=%d\n", t->fid);
if((f = findfid(t->fid, &err)) != NULL &&
statfid(f, &st, &err) == 0 &&\
s9do(s9stat(c, t->tag, &st), &err) == 0){
trace("<- Rstat tag=%d ...\n", t->tag);
}
break;
case Twstat:
trace("...\n");
err = Enowstat;
break;
}
}
if(err != NULL){
if(s9do(s9error(c, t->tag, err), &err2) == 0)
trace("<- Rerror tag=%d \"%s\"\n", t->tag, err);
else
fprintf(stderr, "s9error: %s\n", err2);
}
}
static void
sigdebug(int s)
{
Fid *f;
int i, n;
used(s);
n = 0;
for(i = 0; i < numfids; i++){
f = fids[i];
if(f == NULL)
continue;
fprintf(stderr, "fid %u ", f->fid);
if(f->dir != NULL)
fprintf(stderr, "open mode=dir ");
else if(f->fd >= 0){
fprintf(stderr, "open mode=%s%s%s ",
modes[(f->mode & 0xf)],
(f->mode & C9trunc) ? ",trunc" : "",
(f->mode & C9rclose) ? ",rclose" : "");
}
fprintf(stderr, "qid=[path=%"PRIu64" type=0x%02x version=%"PRIu32"] iounit=%d ",
f->qid.path,
f->qid.type,
f->qid.version,
f->iounit);
fprintf(stderr, " %s %s\n", f->path, f->name);
n++;
}
fprintf(stderr, "fids\t%d\n", n);
fprintf(stderr, "tags\t%d\n", numtags);
fprintf(stderr, "uids\t%d\n", numuids);
fprintf(stderr, "gids\t%d\n", numgids);
}
int
main(int argc, char **argv)
{
const char *dir;
char *err;
Fid *f;
struct parg_state ps;
struct sigaction sa;
int can, i, c, rdonly, block;
parg_init(&ps);
debug = 0;
dir = NULL;
while((c = parg_getopt(&ps, argc, argv, "deh")) >= 0){
switch(c){
case 1:
if(dir != NULL){
fprintf(stderr, "only one dir can be specified\n");
return 1;
}
dir = ps.optarg;
break;
case 'e':
rootescape++;
break;
case 'd':
debug++;
break;
case 'h':
fprintf(stderr, "usage: 9pex [-e] [-d] DIR\n");
return 0;
break;
case '?':
fprintf(stderr, "unknown option -%c\n", ps.optopt);
return 1;
break;
default:
fprintf(stderr, "unhandled option -%c\n", c);
return 1;
break;
}
}
if(dir == NULL){
fprintf(stderr, "no dir specified\n");
return 1;
}
if((rootpath = realpath(dir, NULL)) == NULL){
trace("%s: %s\n", dir, strerror(errno));
return 1;
}
rootlen = strlen(rootpath);
in = 0;
out = 1;
eof = 0;
fids = NULL;
numfids = 0;
tags = NULL;
numtags = 0;
uids = NULL;
numuids = 0;
gids = NULL;
numgids = 0;
memset(&ctx, 0, sizeof(ctx));
ctx.msize = 64*1024;
ctx.read = ctxread;
ctx.begin = ctxbegin;
ctx.end = ctxend;
ctx.t = ctxt;
ctx.error = ctxerror;
rdbuf = calloc(1, ctx.msize);
wrbufsz = ctx.msize;
wrbuf = calloc(1, wrbufsz);
wroff = wrend = 0;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_RESTART;
sigaction(SIGPIPE, &sa, NULL);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigdebug;
sa.sa_flags = SA_RESTART;
sigfillset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
err = NULL;
rdonly = block = 1; /* at first we wait until the client sends in data */
for(; !eof;){
if((can = canrw(rdonly, block)) < 0)
break;
if((can & Canrd) != 0){ /* if there is data, process it */
if(s9do(s9proc(&ctx), &err) != 0)
break;
/* give it a chance to receive all the data first */
rdonly = 1;
block = 0;
}else if(block == 0){ /* got all the data */
if(rdonly != 0){ /* wait until we can send OR we get more data */
rdonly = 0;
block = 1;
}
}else if(rdonly == 0 && (can & Canwr) != 0){ /* can send */
if(wrsend() != 0) /* send all the data */
break;
rdonly = 1; /* and go back to reading */
block = 1;
}
}
if(err != NULL)
trace("s9proc: %s\n", err);
for(i = 0; i < numfids; i++){
if((f = fids[i]) != NULL){
if(f->dir != NULL)
closedir(f->dir);
else if(f->fd >= 0)
close(f->fd);
free(f->path);
free(f);
}
}
for(i = 0; i < numuids; i++)
free(uids[i].name);
free(uids);
for(i = 0; i < numgids; i++)
free(gids[i].name);
free(gids);
memset(wrbuf, 0, ctx.msize);
free(wrbuf);
memset(rdbuf, 0, ctx.msize);
free(rdbuf);
free(fids);
free(rootpath);
return 0;
}