vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

commit 9ef7c7cabfc5044b0500c9a3c41d2c72996967f2
parent 74c7277ddba521a8bb4de9b5953aeb7b08c3266f
Author: Ron Minnich <rminnich@gmail.com>
Date:   Wed, 23 Sep 2009 17:57:31 -0400

9vx: make translation of EINTR match Eintr

http://codereview.appspot.com/122046

Diffstat:
src/9vx/devfs-posix.c | 10++++++++--
src/9vx/devfs-posix.c.orig | 951+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 959 insertions(+), 2 deletions(-)

diff --git a/src/9vx/devfs-posix.c b/src/9vx/devfs-posix.c @@ -59,13 +59,19 @@ struct UnixFd void oserrstr(void) { - kstrcpy(up->errstr, strerror(errno), ERRMAX); + if (errno == EINTR) + kstrcpy(up->errstr, Eintr, ERRMAX); + else + kstrcpy(up->errstr, strerror(errno), ERRMAX); } void oserror(void) { - error(strerror(errno)); + if (errno == EINTR) + error(Eintr); + else + error(strerror(errno)); } static Qid diff --git a/src/9vx/devfs-posix.c.orig b/src/9vx/devfs-posix.c.orig @@ -0,0 +1,951 @@ +#include "u.h" +#include <stdio.h> /* for remove, rename */ +#include <pwd.h> +#include <grp.h> /* going to regret this - getgrgid is a stack smasher */ +#include <sys/socket.h> +#include <sys/un.h> + +#if defined(__FreeBSD__) +#include <sys/disk.h> +#include <sys/disklabel.h> +#include <sys/ioctl.h> +#endif + +#if defined(__APPLE__) +#include <sys/disk.h> +#endif + +#if defined(__linux__) +#include <linux/hdreg.h> +#include <linux/fs.h> +#include <sys/ioctl.h> +#endif + +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +enum +{ + Trace = 0, + FsChar = 'Z', +}; + +extern Path *addelem(Path*, char*, Chan*); +char *localroot = "/home/rsc/plan9/4e"; + +static char *uidtoname(int); +static char *gidtoname(int); +static int nametouid(char*); +static int nametogid(char*); + +static vlong disksize(int, struct stat*); + +typedef struct UnixFd UnixFd; +struct UnixFd +{ + int fd; + int issocket; + int plan9; // rooted at localroot? + DIR* dir; + vlong diroffset; + QLock dirlock; + struct dirent *nextde; + Path *path; // relative to localroot +}; + +void +oserrstr(void) +{ + kstrcpy(up->errstr, strerror(errno), ERRMAX); +} + +void +oserror(void) +{ + error(strerror(errno)); +} + +static Qid +fsqid(struct stat *st) +{ + Qid q; + int dev; + static int nqdev; + static uchar *qdev; + + if(qdev == 0) + qdev = smalloc(65536U); + + q.type = 0; + if((st->st_mode&S_IFMT) == S_IFDIR) + q.type = QTDIR; + + dev = st->st_dev & 0xFFFFUL; + if(qdev[dev] == 0) + qdev[dev] = ++nqdev; + + q.path = (vlong)qdev[dev]<<48; + q.path ^= st->st_ino; + q.vers = st->st_mtime; + + return q; +} + +static Chan* +fsattach(char *spec) +{ + struct stat st; + Chan *c; + UnixFd *ufd; + int plan9, dev; + + dev = 1; + plan9 = 0; + if(spec && spec[0]){ + if(strcmp(spec, "plan9") == 0) { + plan9 = 1; + dev = 2; + } else{ + snprint(up->genbuf, sizeof up->genbuf, "no file system #%C%s", FsChar, spec); + error(up->genbuf); + } + } + + if(plan9){ + if(localroot == nil) + error("no #Zplan9 root without -r"); + if(stat(localroot, &st) < 0) + oserror(); + }else{ + if(stat("/", &st) < 0) + oserror(); + } + + c = devattach(FsChar, spec); + ufd = mallocz(sizeof(UnixFd), 1); + ufd->path = newpath("/"); + ufd->plan9 = plan9; + ufd->fd = -1; + + c->aux = ufd; + c->dev = dev; + c->qid = fsqid(&st); + + if(Trace) + print("fsattach /\n"); + + return c; +} + +static Chan* +fsclone(Chan *c, Chan *nc) +{ + UnixFd *ufd; + + ufd = mallocz(sizeof(UnixFd), 1); + *ufd = *(UnixFd*)c->aux; + if(ufd->path) + incref(&ufd->path->ref); + ufd->fd = -1; + nc->aux = ufd; + return nc; +} + +static char* +lastelem(char *s) +{ + char *t; + + if(s[0] == '/' && s[1] == 0) + return s; + t = strrchr(s, '/'); + if(t == nil) + return s; + return t+1; +} + +static char* +fspath(Chan *c, char *suffix) +{ + char *s, *t; + int len; + UnixFd *ufd; + + ufd = c->aux; + s = ufd->path->s; + if(ufd->plan9){ + len = strlen(localroot)+strlen(s)+1; + if(suffix) + len += 1+strlen(suffix); + t = smalloc(len); + strcpy(t, localroot); + strcat(t, s); + }else{ + len = strlen(s)+1; + if(suffix) + len += 1+strlen(suffix); + t = smalloc(len); + strcpy(t, s); + } + if(suffix){ + if(s[strlen(s)-1] != '/') + strcat(t, "/"); + strcat(t, suffix); + } + return t; +} + +static int +fswalk1(Chan *c, char *name) +{ + struct stat st; + char *path; + UnixFd *ufd; + + ufd = c->aux; + if(strcmp(name, "..") == 0 && strcmp(ufd->path->s, "/") == 0) + return 0; + + path = fspath(c, name); + if(stat(path, &st) < 0){ + if(Trace) + print("fswalk1 %s (%s)\n", path, strerror(errno)); + free(path); + return -1; + } + if(Trace) + print("fswalk1 %s\n", path); + free(path); + + c->qid = fsqid(&st); + return 0; +} + +static void +replacepath(Chan *c, Path *p) +{ + UnixFd *ufd; + + ufd = c->aux; + incref(&p->ref); + pathclose(ufd->path); + ufd->path = p; +} + +static Walkqid* +fswalk(Chan *c, Chan *nc, char **name, int nname) +{ + int i; + Path *path; + Walkqid *wq; + UnixFd *ufd; + + if(nc != nil) + panic("fswalk: nc != nil"); + wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid)); + nc = devclone(c); + fsclone(c, nc); + ufd = c->aux; + path = ufd->path; + incref(&path->ref); + + wq->clone = nc; + for(i=0; i<nname; i++){ + ufd = nc->aux; + replacepath(nc, path); + if(fswalk1(nc, name[i]) < 0){ + if(i == 0){ + pathclose(path); + cclose(nc); + free(wq); + error(Enonexist); + } + break; + } + path = addelem(path, name[i], nil); + wq->qid[i] = nc->qid; + } + replacepath(nc, path); + pathclose(path); + if(i != nname){ + cclose(nc); + wq->clone = nil; + } + wq->nqid = i; + return wq; +} + +static int +fsdirstat(char *path, int dev, Dir *d) +{ + int fd; + struct stat st; + + if(stat(path, &st) < 0 && lstat(path, &st) < 0) + return -1; + + d->name = lastelem(path); + d->uid = uidtoname(st.st_uid); + d->gid = gidtoname(st.st_gid); + d->muid = ""; + d->qid = fsqid(&st); + d->mode = (d->qid.type<<24) | (st.st_mode&0777); + d->atime = st.st_atime; + d->mtime = st.st_mtime; + d->length = st.st_size; + if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){ + d->length = disksize(fd, &st); + close(fd); + } + + // devmnt leaves 1-9 unused so that we can steal them. + // it is easier for all involved if #Z shows M as the file type instead of Z. + // dev is c->dev, either 1 (#Z) or 2 (#Zplan9). + d->type = 'M'; + d->dev = dev; + return 0; +} + +static int +fsstat(Chan *c, uchar *buf, int n) +{ + Dir d; + char *path; + UnixFd *ufd; + + ufd = c->aux; + if(Trace) + print("fsstat %s\n", ufd->path->s); + + if(n < BIT16SZ) + error(Eshortstat); + + path = fspath(c, nil); + if(fsdirstat(path, c->dev, &d) < 0){ + free(path); + oserror(); + } + if(strcmp(ufd->path->s, "/") == 0) + d.name = "/"; + n = convD2M(&d, buf, n); + free(path); + return n; +} + +static int +opensocket(UnixFd *ufd, char *path) +{ + int fd; + struct stat st; + struct sockaddr_un su; + + if(stat(path, &st) < 0) + return -1; + if(!S_ISSOCK(st.st_mode)) + return -1; + memset(&su, 0, sizeof su); + su.sun_family = AF_UNIX; + if(strlen(path)+1 > sizeof su.sun_path){ + errno = ENAMETOOLONG; + return -1; + } + strcpy(su.sun_path, path); + if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + if(connect(fd, (struct sockaddr*)&su, sizeof su) < 0){ + close(fd); + return -1; + } + ufd->fd = fd; + ufd->issocket = 1; + return 0; +} + +static Chan* +fsopen(Chan *c, int mode) +{ + char *path; + int m; + UnixFd *ufd; + + ufd = c->aux; + if(Trace) + print("fsopen %s %#x\n", ufd->path->s, mode); + + if(mode & ~(OTRUNC|ORCLOSE|3)) + error(Ebadarg); + + if((c->qid.type & QTDIR) && mode != OREAD) + error(Eperm); + + + if((c->qid.type&QTDIR) && mode != OREAD) + error(Eperm); + + c->mode = openmode(mode); + path = fspath(c, nil); + if(c->qid.type & QTDIR){ + ufd->dir = opendir(path); + if(ufd->dir == nil){ + free(path); + oserror(); + } + ufd->diroffset = 0; + ufd->nextde = nil; + }else{ + m = mode & 3; + if(m == OEXEC) + m = OREAD; + if(mode & OTRUNC) + m |= O_TRUNC; + if((ufd->fd = open(path, m)) < 0 && opensocket(ufd, path) < 0){ + free(path); + oserror(); + } + } + free(path); + c->flag |= COPEN; + return c; +} + +static void +fscreate(Chan *c, char *name, int mode, ulong perm) +{ + char *path, *path0; + int fd, mm; + UnixFd *ufd; + struct stat st; + + ufd = c->aux; + if(Trace) + print("fscreate %s %#x %#o\n", ufd->path->s, mode, perm); + + if(!(c->qid.type & QTDIR)) + error(Enotdir); + + if(mode & ~(OTRUNC|ORCLOSE|3)) + error(Ebadarg); + + if(perm & ~(DMDIR|0777)) + error(Ebadarg); + + path0 = fspath(c, nil); + path = fspath(c, name); + if(waserror()){ + free(path); + free(path0); + nexterror(); + } + + if(stat(path0, &st) < 0) + oserror(); + + if(perm & DMDIR){ + if(mode != OREAD) + error(Eperm); + if(mkdir(path, perm & 0777) < 0) + oserror(); + if((fd = open(path, 0)) < 0) + oserror(); + // Be like Plan 9 file servers: inherit mode bits + // and group from parent. + fchmod(fd, perm & st.st_mode & 0777); + fchown(fd, -1, st.st_gid); + if(fstat(fd, &st) < 0){ + close(fd); + oserror(); + } + close(fd); + if((ufd->dir = opendir(path)) == nil) + oserror(); + ufd->diroffset = 0; + ufd->nextde = nil; + }else{ + mm = mode & 3; + if(mode & OTRUNC) + mm |= O_TRUNC; + if((fd = open(path, mm|O_CREAT, 0666)) < 0) + oserror(); + // Be like Plan 9 file servers: inherit mode bits + // and group from parent. + fchmod(fd, perm & st.st_mode & 0777); + fchown(fd, -1, st.st_gid); + if(fstat(fd, &st) < 0){ + close(fd); + oserror(); + } + ufd->fd = fd; + } + free(path); + free(path0); + poperror(); + + ufd->path = addelem(ufd->path, name, nil); + c->qid = fsqid(&st); + c->offset = 0; + c->flag |= COPEN; + c->mode = openmode(mode); +} + +static void +fsclose(Chan *c) +{ + UnixFd *ufd; + char *path; + + ufd = c->aux; + if(Trace) + print("fsclose %s\n", ufd->path->s); + + if(c->flag & COPEN) { + if(c->flag & CRCLOSE) { + path = fspath(c, nil); + unlink(path); + free(path); + } + if(c->qid.type & QTDIR) + closedir(ufd->dir); + else + close(ufd->fd); + } + if(ufd->path) + pathclose(ufd->path); + free(ufd); +} + +static long fsdirread(Chan*, uchar*, long, vlong); + +static long +fsread(Chan *c, void *va, long n, vlong offset) +{ + int r; + UnixFd *ufd; + + if(c->qid.type & QTDIR) + return fsdirread(c, va, n, offset); + + ufd = c->aux; + if(ufd->issocket) + r = read(ufd->fd, va, n); + else + r = pread(ufd->fd, va, n, offset); + if(r < 0) + oserror(); + return r; +} + +static long +fswrite(Chan *c, void *va, long n, vlong offset) +{ + int r; + UnixFd *ufd; + + ufd = c->aux; + if(ufd->issocket) + r = write(ufd->fd, va, n); + else + r = pwrite(ufd->fd, va, n, offset); + if(r < 0) + oserror(); + return r; +} + +static int +fswstat(Chan *c, uchar *buf, int n) +{ + char *elem, *path, *npath, *strs, *t; + int nn; + Dir d; + UnixFd *ufd; + + if(n < 2) + error(Ebadstat); + + nn = GBIT16((uchar*)buf); + strs = smalloc(nn); + if(convM2D(buf, n, &d, strs) != n){ + free(strs); + error(Ebadstat); + } + + path = fspath(c, nil); + if(waserror()){ + free(path); + free(strs); + nexterror(); + } + + if(d.muid[0]) + error("cannot change muid"); + + if(d.uid[0] || d.gid[0]){ + int uid, gid; + + uid = -1; + gid = -1; + if(d.uid[0] && (uid = nametouid(d.uid)) < 0) + error("unknown uid"); + if(d.gid[0] && (gid = nametogid(d.gid)) < 0) + error("unknown gid"); + if(chown(path, uid, gid) < 0) + oserror(); + } + + ufd = c->aux; + elem = lastelem(path); + if(d.name[0] && strcmp(d.name, elem) != 0){ + if(strchr(d.name, '/')) + error(Ebadarg); + npath = smalloc(strlen(path)+strlen(d.name)+1); + strcpy(npath, path); + t = strrchr(npath, '/'); + strcpy(t+1, d.name); + if(rename(path, npath) < 0){ + free(npath); + oserror(); + } + free(npath); + } + + if(~d.mode != 0 && chmod(path, d.mode&0777) < 0) + oserror(); + + // TODO: Code to change uid, gid. + + poperror(); + return n; +} + +static int +isdots(char *name) +{ + if(name[0] != '.') + return 0; + if(name[1] == '\0') + return 1; + if(name[1] != '.') + return 0; + if(name[2] == '\0') + return 1; + return 0; +} + +static long +fsdirread(Chan *c, uchar *va, long count, vlong offset) +{ + char *path; + int n, total; + struct dirent *de; + UnixFd *ufd; + Dir d; + + ufd = c->aux; + qlock(&ufd->dirlock); + if(waserror()){ + qunlock(&ufd->dirlock); + nexterror(); + } + + if(ufd->diroffset != offset){ + if(offset != 0) + error(Ebadarg); + ufd->diroffset = 0; + ufd->nextde = nil; + rewinddir(ufd->dir); + } + + total = 0; + while(total+BIT16SZ < count) { + if(ufd->nextde){ + de = ufd->nextde; + ufd->nextde = nil; + } + else if((de = readdir(ufd->dir)) == nil) + break; + if(isdots(de->d_name)) + continue; + path = fspath(c, de->d_name); + if(fsdirstat(path, c->dev, &d) < 0){ + free(path); + continue; + } + n = convD2M(&d, (uchar*)va+total, count-total); + free(path); + if(n == BIT16SZ){ + ufd->nextde = de; + break; + } + total += n; + } + ufd->diroffset += total; + qunlock(&ufd->dirlock); + poperror(); + return total; +} + +static void +fsremove(Chan *c) +{ + char *path; + UnixFd *ufd; + + ufd = c->aux; + if(Trace) + print("fsremove %s\n", ufd->path->s); + + path = fspath(c, nil); + if(waserror()){ + free(path); + nexterror(); + } + if(c->qid.type & QTDIR){ + if(rmdir(path) < 0) + oserror(); + }else{ + if(remove(path) < 0) + oserror(); + } + free(path); + poperror(); +} + +Dev fsdevtab = { + FsChar, + "fs", + + devreset, + devinit, + devshutdown, + fsattach, + fswalk, + fsstat, + fsopen, + fscreate, + fsclose, + fsread, + devbread, + fswrite, + devbwrite, + fsremove, + fswstat, +}; + + +/* Uid management code adapted from u9fs */ + +/* + * we keep a table by numeric id. by name lookups happen infrequently + * while by-number lookups happen once for every directory entry read + * and every stat request. + */ +typedef struct User User; +struct User { + int id; + gid_t defaultgid; + char *name; + User *next; +}; + + +static User *utab[64]; +static User *gtab[64]; + +static User* +adduser(struct passwd *p) +{ + User *u; + + u = smalloc(sizeof(*u)); + u->id = p->pw_uid; + kstrdup(&u->name, p->pw_name); + u->next = utab[p->pw_uid%nelem(utab)]; + u->defaultgid = p->pw_gid; + utab[p->pw_uid%nelem(utab)] = u; + return u; +} + +static User* +addgroup(struct group *g) +{ + User *u; + + u = smalloc(sizeof(*u)); + u->id = g->gr_gid; + kstrdup(&u->name, g->gr_name); + u->next = gtab[g->gr_gid%nelem(gtab)]; + gtab[g->gr_gid%nelem(gtab)] = u; + return u; +} + +static User* +uname2user(char *name) +{ + int i; + User *u; + struct passwd *p; + + for(i=0; i<nelem(utab); i++) + for(u=utab[i]; u; u=u->next) + if(strcmp(u->name, name) == 0) + return u; + + if((p = getpwnam(name)) == nil) + return nil; + return adduser(p); +} + +static User* +uid2user(int id) +{ + User *u; + struct passwd *p; + + for(u=utab[id%nelem(utab)]; u; u=u->next) + if(u->id == id) + return u; + + if((p = getpwuid(id)) == nil) + return nil; + return adduser(p); +} + +static User* +gname2user(char *name) +{ + int i; + User *u; + struct group *g; + + for(i=0; i<nelem(gtab); i++) + for(u=gtab[i]; u; u=u->next) + if(strcmp(u->name, name) == 0) + return u; + + if((g = getgrnam(name)) == nil) + return nil; + return addgroup(g); +} + +static User* +gid2user(int id) +{ + User *u; + struct group *g; + + for(u=gtab[id%nelem(gtab)]; u; u=u->next) + if(u->id == id) + return u; + + if((g = getgrgid(id)) == nil) + return nil; + return addgroup(g); +} + +static char* +uidtoname(int uid) +{ + User *u; + + u = uid2user(uid); + if(u == nil) + return "?"; + return u->name; +} + +static char* +gidtoname(int gid) +{ + User *u; + + u = gid2user(gid); + if(u == nil) + return "?"; + return u->name; +} + +static int +nametouid(char *name) +{ + User *u; + + u = uname2user(name); + if(u == nil) + return -1; + return u->id; +} + +static int +nametogid(char *name) +{ + User *u; + + u = gname2user(name); + if(u == nil) + return -1; + return u->id; +} + +#if defined(__linux__) + +static vlong +disksize(int fd, struct stat *st) +{ + uvlong u64; + long l; + struct hd_geometry geo; + + memset(&geo, 0, sizeof geo); + l = 0; + u64 = 0; +#ifdef BLKGETSIZE64 + if(ioctl(fd, BLKGETSIZE64, &u64) >= 0) + return u64; +#endif + if(ioctl(fd, BLKGETSIZE, &l) >= 0) + return l*512; + if(ioctl(fd, HDIO_GETGEO, &geo) >= 0) + return (vlong)geo.heads*geo.sectors*geo.cylinders*512; + return 0; +} + +#elif defined(__FreeBSD__) && defined(DIOCGMEDIASIZE) + +static vlong +disksize(int fd, struct stat *st) +{ + off_t mediasize; + + if(ioctl(fd, DIOCGMEDIASIZE, &mediasize) >= 0) + return mediasize; + return 0; +} + +#elif defined(__APPLE__) + +static vlong +disksize(int fd, struct stat *st) +{ + uvlong bc; + unsigned int bs; + + bs = 0; + bc = 0; + ioctl(fd, DKIOCGETBLOCKSIZE, &bs); + ioctl(fd, DKIOCGETBLOCKCOUNT, &bc); + if(bs >0 && bc > 0) + return bc*bs; + return 0; +} + +#else + +static vlong +disksize(int fd, struct stat *st) +{ + return 0; +} + +#endif