#include <ext4.h>#include <ext4_inode.h>#include <fcall.h>#include <thread.h>#include <9p.h>#include <bio.h>#include "group.h"#include "common.h" #define MIN(a,b) ((a)<(b)?(a):(b)) int mainstacksize = 65536; typedef struct Aux Aux; struct Aux { Part *p; u32int uid; char *path; int doff; union { ext4_file *file; ext4_dir *dir; }; int type;}; enum { Adir, Afile,}; static Opts opts = { .group = nil, .cachewb = 0, .asroot = 0, .linkmode = Lhide, .fstype = -1, .blksz = 1024, .label = "", .inodesz = 256, .ninode = 0,};static u32int Root;static u8int zero[65536];static char Eperm[] = "permission denied";static char *srvname; static char *linkresolve(Aux *a, char *s, char **value){ char *q, buf[4096+1]; usize sz; int res; res = 0; if(opts.linkmode == Lresolve && (res = ext4_readlink(s, buf, sizeof(buf), &sz)) == 0){ if(sz == sizeof(buf)){ werrstr("readlink: %s: path too long", s); free(s); return nil; } buf[sz] = 0; if(value != nil) *value = strdup(buf); cleanname(buf); if(buf[0] == '/'){ free(s); s = smprint("%M%s", a->p, buf); }else{ q = strrchr(s, '/'); *q = 0; q = s; s = smprint("%s/%s", q, buf); free(q); cleanname(strchr(s+1, '/')); } }else{ if(res != 0) werrstr("readlink: %s: %s", s, errno2s(res)); if(value != nil) *value = nil; } return s;} static char *fullpath(Aux *a){ return linkresolve(a, smprint("%M/%s", a->p, a->path), nil);} static inthaveperm(Aux *a, int p, struct ext4_inode *inodeout){ struct ext4_inode inode; u32int ino, id; int m, fm, r; Group *g; char *s; switch(p & 3){ case OREAD: p = AREAD; break; case OWRITE: p = AWRITE; break; case ORDWR: p = AREAD|AWRITE; break; case OEXEC: p = AEXEC; break; default: return 0; } if(p & OTRUNC) p |= AWRITE; if((s = fullpath(a)) == nil) return -1; if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){ werrstr("%s: %s", s, errno2s(r)); free(s); return -1; } free(s); if(inodeout != nil) memmove(inodeout, &inode, sizeof(inode)); fm = ext4_inode_get_mode(a->p->sb, &inode); /* other */ m = fm & 7; if((p & m) == p) return 1; /* owner */ id = ext4_inode_get_uid(&inode); if(a->uid == Root || ((g = findgroupid(&a->p->groups, id)) != nil && ingroup(g, a->uid))){ m |= (fm >> 6) & 7; if((p & m) == p) return 1; } /* group */ id = ext4_inode_get_gid(&inode); if(a->uid == Root || ((g = findgroupid(&a->p->groups, id)) != nil && ingroup(g, a->uid))){ m |= (fm >> 3) & 7; if((p & m) == p) return 1; } return 0;} static voidrattach(Req *r){ char err[ERRMAX]; Aux *a; if((a = calloc(1, sizeof(*a))) == nil) respond(r, "memory"); else if((a->p = openpart(r->ifcall.aname, &opts)) == nil){ free(a); rerrstr(err, sizeof(err)); respond(r, err); }else{ if(opts.asroot || findgroup(&a->p->groups, r->ifcall.uname, &a->uid) == nil) a->uid = Root; incref(a->p); a->type = Adir; a->path = strdup(""); r->ofcall.qid = a->p->qidmask; r->fid->qid = a->p->qidmask; r->fid->aux = a; respond(r, nil); }} static u32inttoext4mode(u32int mode, u32int perm, int creat){ u32int e; e = 0; mode &= ~OCEXEC; if(mode & OTRUNC) e |= O_TRUNC; mode &= 3; if(mode == OWRITE) e |= O_WRONLY; else if(mode == ORDWR) e |= O_RDWR; if(creat) e |= O_CREAT; if(perm & DMEXCL) e |= O_EXCL; if(perm & DMAPPEND) e |= O_APPEND; return e;} static voidropen(Req *r){ char *path; int res; Aux *a; a = r->fid->aux; switch(a->type){ case Adir: if(r->ifcall.mode != OREAD || !haveperm(a, r->ifcall.mode, nil)){ respond(r, Eperm); return; } if(a->dir != nil){ respond(r, "double open"); return; } if((a->dir = malloc(sizeof(*a->dir))) == nil) goto Nomem; if((path = smprint("%M/%s", a->p, a->path)) == nil){ free(a->dir); a->dir = nil; goto Nomem; } res = ext4_dir_open(a->dir, path); free(path); if(res != 0){ free(a->dir); a->dir = nil; respond(r, errno2s(res)); return; } break; case Afile: if(!haveperm(a, r->ifcall.mode, nil)){ respond(r, Eperm); return; } if(a->file != nil){ respond(r, "double open"); return; } if((a->file = malloc(sizeof(*a->file))) == nil) goto Nomem; if((path = smprint("%M/%s", a->p, a->path)) == nil){ free(a->file); a->file = nil; goto Nomem; } res = ext4_fopen2(a->file, path, toext4mode(r->ifcall.mode, 0, 0)); free(path); if(res != 0){ free(a->file); a->file = nil; respond(r, errno2s(res)); return; } break; Nomem: respond(r, "memory"); return; } r->ofcall.iounit = 0; respond(r, nil);} static voidrcreate(Req *r){ u32int perm, dirperm, t; struct ext4_inode inode; char *s, *err, *q; int mkdir, res; long tm; Aux *a; a = r->fid->aux; err = nil; s = nil; if(a->file != nil || a->dir != nil){ err = "double create"; goto error; } if(!haveperm(a, OWRITE, &inode)){ err = Eperm; goto error; } /* first make sure this is a directory */ t = ext4_inode_type(a->p->sb, &inode); if(t != EXT4_INODE_MODE_DIRECTORY){ err = "create in non-directory"; goto error; } if((s = fullpath(a)) == nil) goto error; ext4_mode_get(s, &dirperm); /* check if the entry already exists */ if((q = smprint("%s/%s", s, r->ifcall.name)) == nil){Nomem: err = "memory"; goto error; } free(s); s = q; cleanname(s); if(ext4_inode_exist(s, EXT4_DE_UNKNOWN) == 0){ err = "already exists"; goto error; } mkdir = r->ifcall.perm & DMDIR; perm = mkdir ? 0666 : 0777; perm = r->ifcall.perm & (~perm | (dirperm & perm)); if(mkdir){ a->type = Adir; if((res = ext4_dir_mk(s)) != 0) goto ext4error; if((a->dir = malloc(sizeof(*a->dir))) == nil) goto Nomem; if((res = ext4_dir_open(a->dir, s)) != 0){ free(a->dir); a->dir = nil; goto ext4errorrm; } }else{ a->type = Afile; if((a->file = malloc(sizeof(*a->file))) == nil) goto Nomem; if((res = ext4_fopen2(a->file, s, toext4mode(r->ifcall.mode, perm, 1))) != 0){ free(a->file); a->file = nil; goto ext4error; } } if((res = ext4_mode_set(s, perm)) != 0) goto ext4errorrm; ext4_owner_set(s, a->uid, a->uid); tm = time(nil); ext4_mtime_set(s, tm); ext4_ctime_set(s, tm); r->fid->qid.path = a->p->qidmask.path | a->file->inode; r->fid->qid.vers = 0; r->fid->qid.type = 0; r->ofcall.qid = r->fid->qid; free(a->path); a->path = strdup(strchr(s+1, '/')+1); free(s); r->ofcall.iounit = 0; respond(r, nil); return; ext4errorrm: if(mkdir) ext4_dir_rm(s); else ext4_fremove(s);ext4error: err = errno2s(res);error: free(s); respond(r, err);} static intdirfill(Dir *dir, Aux *a, char *path){ struct ext4_inode inode; u32int t, ino, id; char tmp[16]; char *s, *q; Group *g; int r; memset(dir, 0, sizeof(*dir)); if(path == nil){ path = a->path; s = smprint("%M/%s", a->p, a->path); }else{ if(*a->path == 0 && *path == 0) path = "/"; s = smprint("%M%s%s/%s", a->p, *a->path ? "/" : "", a->path, path); } if((s = linkresolve(a, s, nil)) == nil) return -1; if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){ werrstr("inode: %s: %s", s, errno2s(r)); free(s); return -1; } t = ext4_inode_type(a->p->sb, &inode); if(opts.linkmode == Lhide && t == EXT4_INODE_MODE_SOFTLINK){ free(s); return -1; } dir->mode = ext4_inode_get_mode(a->p->sb, &inode) & 0x1ff; dir->qid.path = a->p->qidmask.path | ino; dir->qid.vers = ext4_inode_get_generation(&inode); dir->qid.type = 0; if(t == EXT4_INODE_MODE_DIRECTORY){ dir->qid.type |= QTDIR; dir->mode |= DMDIR; }else dir->length = ext4_inode_get_size(a->p->sb, &inode); if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND){ dir->qid.type |= QTAPPEND; dir->mode |= DMAPPEND; } if((q = strrchr(path, '/')) != nil) path = q+1; dir->name = estrdup9p(path); dir->atime = ext4_inode_get_access_time(&inode); dir->mtime = ext4_inode_get_modif_time(&inode); sprint(tmp, "%ud", id = ext4_inode_get_uid(&inode)); dir->uid = estrdup9p((g = findgroupid(&a->p->groups, id)) != nil ? g->name : tmp); sprint(tmp, "%ud", id = ext4_inode_get_gid(&inode)); dir->gid = estrdup9p((g = findgroupid(&a->p->groups, id)) != nil ? g->name : tmp); free(s); return 0;} static intdirgen(int n, Dir *dir, void *aux){ const ext4_direntry *e; Aux *a; a = aux; if(n == 0 || n != a->doff){ ext4_dir_entry_rewind(a->dir); a->doff = 0; } for(;;){ do{ if((e = ext4_dir_entry_next(a->dir)) == nil) return -1; }while(e->name == nil || strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0); if(opts.linkmode == Lhide && e->inode_type == EXT4_DE_SYMLINK) continue; if(a->doff++ != n) continue; if(dirfill(dir, a, (char*)e->name) == 0) return 0; a->doff--; }} static voidrread(Req *r){ usize n; int res; Aux *a; a = r->fid->aux; if(a->type == Adir && a->dir != nil){ dirread9p(r, dirgen, a); }else if(a->type == Afile && a->file != nil){ if(ext4_fseek(a->file, r->ifcall.offset, SEEK_SET) != 0) n = 0;Lines 1–500 of 1060. Load more
#include <ext4.h> #include <ext4_inode.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include <bio.h> #include "group.h" #include "common.h" #define MIN(a,b) ((a)<(b)?(a):(b)) int mainstacksize = 65536; typedef struct Aux Aux; struct Aux { Part *p; u32int uid; char *path; int doff; union { ext4_file *file; ext4_dir *dir; }; int type; }; enum { Adir, Afile, }; static Opts opts = { .group = nil, .cachewb = 0, .asroot = 0, .linkmode = Lhide, .fstype = -1, .blksz = 1024, .label = "", .inodesz = 256, .ninode = 0, }; static u32int Root; static u8int zero[65536]; static char Eperm[] = "permission denied"; static char *srvname; static char * linkresolve(Aux *a, char *s, char **value) { char *q, buf[4096+1]; usize sz; int res; res = 0; if(opts.linkmode == Lresolve && (res = ext4_readlink(s, buf, sizeof(buf), &sz)) == 0){ if(sz == sizeof(buf)){ werrstr("readlink: %s: path too long", s); free(s); return nil; } buf[sz] = 0; if(value != nil) *value = strdup(buf); cleanname(buf); if(buf[0] == '/'){ free(s); s = smprint("%M%s", a->p, buf); }else{ q = strrchr(s, '/'); *q = 0; q = s; s = smprint("%s/%s", q, buf); free(q); cleanname(strchr(s+1, '/')); } }else{ if(res != 0) werrstr("readlink: %s: %s", s, errno2s(res)); if(value != nil) *value = nil; } return s; } static char * fullpath(Aux *a) { return linkresolve(a, smprint("%M/%s", a->p, a->path), nil); } static int haveperm(Aux *a, int p, struct ext4_inode *inodeout) { struct ext4_inode inode; u32int ino, id; int m, fm, r; Group *g; char *s; switch(p & 3){ case OREAD: p = AREAD; break; case OWRITE: p = AWRITE; break; case ORDWR: p = AREAD|AWRITE; break; case OEXEC: p = AEXEC; break; default: return 0; } if(p & OTRUNC) p |= AWRITE; if((s = fullpath(a)) == nil) return -1; if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){ werrstr("%s: %s", s, errno2s(r)); free(s); return -1; } free(s); if(inodeout != nil) memmove(inodeout, &inode, sizeof(inode)); fm = ext4_inode_get_mode(a->p->sb, &inode); /* other */ m = fm & 7; if((p & m) == p) return 1; /* owner */ id = ext4_inode_get_uid(&inode); if(a->uid == Root || ((g = findgroupid(&a->p->groups, id)) != nil && ingroup(g, a->uid))){ m |= (fm >> 6) & 7; if((p & m) == p) return 1; } /* group */ id = ext4_inode_get_gid(&inode); if(a->uid == Root || ((g = findgroupid(&a->p->groups, id)) != nil && ingroup(g, a->uid))){ m |= (fm >> 3) & 7; if((p & m) == p) return 1; } return 0; } static void rattach(Req *r) { char err[ERRMAX]; Aux *a; if((a = calloc(1, sizeof(*a))) == nil) respond(r, "memory"); else if((a->p = openpart(r->ifcall.aname, &opts)) == nil){ free(a); rerrstr(err, sizeof(err)); respond(r, err); }else{ if(opts.asroot || findgroup(&a->p->groups, r->ifcall.uname, &a->uid) == nil) a->uid = Root; incref(a->p); a->type = Adir; a->path = strdup(""); r->ofcall.qid = a->p->qidmask; r->fid->qid = a->p->qidmask; r->fid->aux = a; respond(r, nil); } } static u32int toext4mode(u32int mode, u32int perm, int creat) { u32int e; e = 0; mode &= ~OCEXEC; if(mode & OTRUNC) e |= O_TRUNC; mode &= 3; if(mode == OWRITE) e |= O_WRONLY; else if(mode == ORDWR) e |= O_RDWR; if(creat) e |= O_CREAT; if(perm & DMEXCL) e |= O_EXCL; if(perm & DMAPPEND) e |= O_APPEND; return e; } static void ropen(Req *r) { char *path; int res; Aux *a; a = r->fid->aux; switch(a->type){ case Adir: if(r->ifcall.mode != OREAD || !haveperm(a, r->ifcall.mode, nil)){ respond(r, Eperm); return; } if(a->dir != nil){ respond(r, "double open"); return; } if((a->dir = malloc(sizeof(*a->dir))) == nil) goto Nomem; if((path = smprint("%M/%s", a->p, a->path)) == nil){ free(a->dir); a->dir = nil; goto Nomem; } res = ext4_dir_open(a->dir, path); free(path); if(res != 0){ free(a->dir); a->dir = nil; respond(r, errno2s(res)); return; } break; case Afile: if(!haveperm(a, r->ifcall.mode, nil)){ respond(r, Eperm); return; } if(a->file != nil){ respond(r, "double open"); return; } if((a->file = malloc(sizeof(*a->file))) == nil) goto Nomem; if((path = smprint("%M/%s", a->p, a->path)) == nil){ free(a->file); a->file = nil; goto Nomem; } res = ext4_fopen2(a->file, path, toext4mode(r->ifcall.mode, 0, 0)); free(path); if(res != 0){ free(a->file); a->file = nil; respond(r, errno2s(res)); return; } break; Nomem: respond(r, "memory"); return; } r->ofcall.iounit = 0; respond(r, nil); } static void rcreate(Req *r) { u32int perm, dirperm, t; struct ext4_inode inode; char *s, *err, *q; int mkdir, res; long tm; Aux *a; a = r->fid->aux; err = nil; s = nil; if(a->file != nil || a->dir != nil){ err = "double create"; goto error; } if(!haveperm(a, OWRITE, &inode)){ err = Eperm; goto error; } /* first make sure this is a directory */ t = ext4_inode_type(a->p->sb, &inode); if(t != EXT4_INODE_MODE_DIRECTORY){ err = "create in non-directory"; goto error; } if((s = fullpath(a)) == nil) goto error; ext4_mode_get(s, &dirperm); /* check if the entry already exists */ if((q = smprint("%s/%s", s, r->ifcall.name)) == nil){ Nomem: err = "memory"; goto error; } free(s); s = q; cleanname(s); if(ext4_inode_exist(s, EXT4_DE_UNKNOWN) == 0){ err = "already exists"; goto error; } mkdir = r->ifcall.perm & DMDIR; perm = mkdir ? 0666 : 0777; perm = r->ifcall.perm & (~perm | (dirperm & perm)); if(mkdir){ a->type = Adir; if((res = ext4_dir_mk(s)) != 0) goto ext4error; if((a->dir = malloc(sizeof(*a->dir))) == nil) goto Nomem; if((res = ext4_dir_open(a->dir, s)) != 0){ free(a->dir); a->dir = nil; goto ext4errorrm; } }else{ a->type = Afile; if((a->file = malloc(sizeof(*a->file))) == nil) goto Nomem; if((res = ext4_fopen2(a->file, s, toext4mode(r->ifcall.mode, perm, 1))) != 0){ free(a->file); a->file = nil; goto ext4error; } } if((res = ext4_mode_set(s, perm)) != 0) goto ext4errorrm; ext4_owner_set(s, a->uid, a->uid); tm = time(nil); ext4_mtime_set(s, tm); ext4_ctime_set(s, tm); r->fid->qid.path = a->p->qidmask.path | a->file->inode; r->fid->qid.vers = 0; r->fid->qid.type = 0; r->ofcall.qid = r->fid->qid; free(a->path); a->path = strdup(strchr(s+1, '/')+1); free(s); r->ofcall.iounit = 0; respond(r, nil); return; ext4errorrm: if(mkdir) ext4_dir_rm(s); else ext4_fremove(s); ext4error: err = errno2s(res); error: free(s); respond(r, err); } static int dirfill(Dir *dir, Aux *a, char *path) { struct ext4_inode inode; u32int t, ino, id; char tmp[16]; char *s, *q; Group *g; int r; memset(dir, 0, sizeof(*dir)); if(path == nil){ path = a->path; s = smprint("%M/%s", a->p, a->path); }else{ if(*a->path == 0 && *path == 0) path = "/"; s = smprint("%M%s%s/%s", a->p, *a->path ? "/" : "", a->path, path); } if((s = linkresolve(a, s, nil)) == nil) return -1; if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){ werrstr("inode: %s: %s", s, errno2s(r)); free(s); return -1; } t = ext4_inode_type(a->p->sb, &inode); if(opts.linkmode == Lhide && t == EXT4_INODE_MODE_SOFTLINK){ free(s); return -1; } dir->mode = ext4_inode_get_mode(a->p->sb, &inode) & 0x1ff; dir->qid.path = a->p->qidmask.path | ino; dir->qid.vers = ext4_inode_get_generation(&inode); dir->qid.type = 0; if(t == EXT4_INODE_MODE_DIRECTORY){ dir->qid.type |= QTDIR; dir->mode |= DMDIR; }else dir->length = ext4_inode_get_size(a->p->sb, &inode); if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND){ dir->qid.type |= QTAPPEND; dir->mode |= DMAPPEND; } if((q = strrchr(path, '/')) != nil) path = q+1; dir->name = estrdup9p(path); dir->atime = ext4_inode_get_access_time(&inode); dir->mtime = ext4_inode_get_modif_time(&inode); sprint(tmp, "%ud", id = ext4_inode_get_uid(&inode)); dir->uid = estrdup9p((g = findgroupid(&a->p->groups, id)) != nil ? g->name : tmp); sprint(tmp, "%ud", id = ext4_inode_get_gid(&inode)); dir->gid = estrdup9p((g = findgroupid(&a->p->groups, id)) != nil ? g->name : tmp); free(s); return 0; } static int dirgen(int n, Dir *dir, void *aux) { const ext4_direntry *e; Aux *a; a = aux; if(n == 0 || n != a->doff){ ext4_dir_entry_rewind(a->dir); a->doff = 0; } for(;;){ do{ if((e = ext4_dir_entry_next(a->dir)) == nil) return -1; }while(e->name == nil || strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0); if(opts.linkmode == Lhide && e->inode_type == EXT4_DE_SYMLINK) continue; if(a->doff++ != n) continue; if(dirfill(dir, a, (char*)e->name) == 0) return 0; a->doff--; } } static void rread(Req *r) { usize n; int res; Aux *a; a = r->fid->aux; if(a->type == Adir && a->dir != nil){ dirread9p(r, dirgen, a); }else if(a->type == Afile && a->file != nil){ if(ext4_fseek(a->file, r->ifcall.offset, SEEK_SET) != 0) n = 0;