shithub: ext4srv

Download patch

ref: bc36c6b77688a51db606c14b6d9f84b529e043d9
parent: 12a0ed55204affb277def2a73c1769bd76105ed4
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Thu Nov 12 07:23:35 EST 2020

better permission logic; add  -l hide  option to hide links; implement remove

--- a/common.h
+++ b/common.h
@@ -8,6 +8,7 @@
 
 struct Opts {
 	int cachewb;
+	int linkmode;
 };
 
 struct Part {
@@ -26,6 +27,10 @@
 	Groups groups;
 	int f;
 	uchar blkbuf[];
+};
+
+enum {
+	Lhide = 1,
 };
 
 Part *openpart(char *dev, Opts *opts);
--- a/ext4srv.c
+++ b/ext4srv.c
@@ -28,19 +28,29 @@
 	Root = 0,
 };
 
-static Opts opts = {
-	.cachewb = 0,
-};
+static Opts opts;
 
 static char *
-linkresolve(Aux *a, char *s)
+linkresolve(Aux *a, char *s, char **value)
 {
-	char *q;
-	char buf[4096+1];
+	char *q, buf[4096+1];
 	ulong sz;
 
-	if(ext4_readlink(s, buf, sizeof(buf)-1, &sz) == 0){
+	if(ext4_readlink(s, buf, sizeof(buf), &sz) == 0){
+		if(sz == sizeof(buf)){
+			werrstr("readlink: %s: path too long", s);
+			free(s);
+			return nil;
+		}
+		if(opts.linkmode == Lhide){
+			werrstr("linkresolve: %s: links are hidden", s);
+			free(s);
+			return nil;
+		}
+
 		buf[sz] = 0;
+		if(value != nil)
+			*value = strdup(buf);
 		cleanname(buf);
 		if(buf[0] == '/'){
 			free(s);
@@ -53,6 +63,8 @@
 			cleanname(s);
 			free(q);
 		}
+	}else if(value != nil){
+		*value = nil;
 	}
 
 	return s;
@@ -61,7 +73,7 @@
 static char *
 fullpath(Aux *a)
 {
-	return linkresolve(a, smprint("%M/%s", a->p, a->path));
+	return linkresolve(a, smprint("%M/%s", a->p, a->path), nil);
 }
 
 static int
@@ -68,10 +80,10 @@
 haveperm(Aux *a, int p)
 {
 	struct ext4_inode inode;
-	u32int ino;
+	u32int ino, id;
+	int m, fm, r;
 	Group *g;
 	char *s;
-	int m, fm, r;
 
 	switch(p & 3){
 	case OREAD:
@@ -90,10 +102,12 @@
 		return 0;
 	}
 
-	s = fullpath(a);
+	if((s = fullpath(a)) == nil)
+		return -1;
 	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){
-		fprint(2, "inode: %s: %s\n", s, errno2s(r));
-		return 0;
+		werrstr("%s: %s", s, errno2s(r));
+		free(s);
+		return -1;
 	}
 	free(s);
 
@@ -105,7 +119,8 @@
 		return 1;
 
 	/* owner */
-	if(a->uid == Root || ((g = findgroupid(&a->p->groups, ext4_inode_get_uid(&inode))) != nil && g->id == a->uid)){
+	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;
@@ -112,7 +127,8 @@
 	}
 
 	/* group */
-	if(a->uid == Root || ((g = findgroupid(&a->p->groups, ext4_inode_get_gid(&inode))) != nil && g->id == a->uid)){
+	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;
@@ -124,8 +140,8 @@
 static void
 rattach(Req *r)
 {
+	char err[ERRMAX];
 	Aux *a;
-	static char err[ERRMAX];
 
 	if((a = calloc(1, sizeof(*a))) == nil)
 		respond(r, "memory");
@@ -254,7 +270,8 @@
 dirfill(Dir *dir, Aux *a, char *path)
 {
 	struct ext4_inode inode;
-	u32int t, ino;
+	u32int t, ino, id;
+	char tmp[16];
 	char *s, *q;
 	Group *g;
 	int r;
@@ -269,9 +286,13 @@
 			path = "/";
 		s = smprint("%M%s%s/%s", a->p, *a->path ? "/" : "", a->path, path);
 	}
-	s = linkresolve(a, s);
-	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0)
-		fprint(2, "inode: %s: %s\n", s, errno2s(r));
+	if((s = linkresolve(a, s, nil)) == nil)
+		return -1;
+	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0){
+		werrstr("%s: %s", s, errno2s(r));
+		free(s);
+		return -1;
+	}
 
 	dir->mode = ext4_inode_get_mode(a->p->sb, &inode);
 	dir->qid.path = a->p->qidmask.path | ino;
@@ -293,11 +314,12 @@
 	dir->atime = ext4_inode_get_access_time(&inode);
 	dir->mtime = ext4_inode_get_modif_time(&inode);
 
-	if((g = findgroupid(&a->p->groups, ext4_inode_get_uid(&inode))) != nil)
-		dir->uid = estrdup9p(g->name);
-	if((g = findgroupid(&a->p->groups, ext4_inode_get_gid(&inode))) != nil)
-		dir->gid = estrdup9p(g->name);
+	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;
@@ -312,12 +334,16 @@
 	a = aux;
 	if(n == 0)
 		ext4_dir_entry_rewind(a->dir);
-	do{
-		if((e = ext4_dir_entry_next(a->dir)) == nil)
-			return -1;
-	}while(strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0);
 
-	return dirfill(dir, a, (char*)e->name);
+	for(;;){
+		do{
+			if((e = ext4_dir_entry_next(a->dir)) == nil)
+				return -1;
+		}while(strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0);
+
+		if(dirfill(dir, a, (char*)e->name) == 0)
+			return 0;
+	}
 }
 
 static void
@@ -373,7 +399,52 @@
 static void
 rremove(Req *r)
 {
-	respond(r, "nope");
+	struct ext4_inode inode;
+	const ext4_direntry *e;
+	u32int ino, t, empty;
+	char *s, *err;
+	ext4_dir dir;
+	Group *g;
+	int res;
+	Aux *a;
+
+	a = r->fid->aux;
+	err = nil;
+
+	/* do not resolve links here as most likely it's JUST the link we want to remove */
+	if((s = smprint("%M/%s", a->p, a->path)) == nil){
+		err = "memory";
+		goto end;
+	}
+	if((res = ext4_raw_inode_fill(s, &ino, &inode)) != 0){
+ext4error:
+		err = errno2s(res);
+		goto end;
+	}
+
+	if(a->uid == Root || ((g = findgroupid(&a->p->groups, ext4_inode_get_uid(&inode))) != nil && g->id == a->uid)){
+		t = ext4_inode_type(a->p->sb, &inode);
+		if((t & EXT4_INODE_MODE_DIRECTORY) != 0 && ext4_dir_open(&dir, s) == 0){
+			do{
+				e = ext4_dir_entry_next(&dir);
+				empty = e == nil;
+				if(empty)
+					break;
+			}while(strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0);
+			ext4_dir_close(&dir);
+			if(!empty)
+				err = "directory not empty";
+			else if((res = ext4_dir_rm(s)) != 0)
+				goto ext4error;
+		}else if((res = ext4_fremove(s)) != 0)
+			goto ext4error;
+	}else{
+		err = "permission denied";
+	}
+
+end:
+	free(s);
+	respond(r, err);
 }
 
 static void
@@ -380,11 +451,15 @@
 rstat(Req *r)
 {
 	Aux *a;
+	char err[ERRMAX];
 
 	a = r->fid->aux;
-	dirfill(&r->d, a, nil);
-
-	respond(r, nil);
+	if(dirfill(&r->d, a, nil) != 0){
+		rerrstr(err, sizeof(err));
+		respond(r, err);
+	}else{
+		respond(r, nil);
+	}
 }
 
 static void
@@ -396,22 +471,43 @@
 static char *
 rwalk1(Fid *fid, char *name, Qid *qid)
 {
-	Aux *a;
-	char *s, *q;
-	u32int ino, t;
+	static char errbuf[ERRMAX];
 	struct ext4_inode inode;
+	u32int ino, t;
+	Aux *a, dir;
+	char *s, *q;
 	int r;
 
 	a = fid->aux;
 
-	s = smprint("%M/%s", a->p, a->path);
-	s = linkresolve(a, s);
+	/* try walking to the real file first */
+	if((s = fullpath(a)) == nil){
+		/* else try link itself. might want to just remove it anyway */
+		if((s = smprint("%M/%s", a->p, a->path)) == nil){
+			r = ENOMEM;
+			goto error;
+		}
+	}
+	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0)
+		goto error;
+	t = ext4_inode_type(a->p->sb, &inode);
+	if((t & EXT4_INODE_MODE_DIRECTORY) == 0){
+		free(s);
+		return "not a directory";
+	}
+	dir = *a;
+	dir.path = s;
+	if(!haveperm(&dir, OEXEC)){
+		free(s);
+		return "permission denied";
+	}
 
 	q = s;
 	s = smprint("%s/%s", q, name);
 	cleanname(s);
 	free(q);
-	s = linkresolve(a, s);
+	if((s = linkresolve(a, s, nil)) == nil)
+		goto error;
 	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0)
 		goto error;
 	qid->type = 0;
@@ -536,10 +632,20 @@
 static void
 usage(void)
 {
-	fprint(2, "usage: %s [-C] [-s srvname]\n", argv0);
+	fprint(2, "usage: %s [-C] [-l hide] [-s srvname]\n", argv0);
 	threadexitsall("usage");
 }
 
+static int
+linkmode(char *m)
+{
+	if(strcmp(m, "hide") == 0)
+		return Lhide;
+
+	usage();
+	return -1;
+}
+
 void
 threadmain(int argc, char **argv)
 {
@@ -553,8 +659,12 @@
 	case 'C':
 		opts.cachewb = 1;
 		break;
+	case 'l':
+		opts.linkmode = linkmode(EARGF(usage()));
+		break;
 	case 's':
 		srv = EARGF(usage());
+		break;
 	}ARGEND
 
 	if(argc != 0)
--- a/group.c
+++ b/group.c
@@ -5,9 +5,9 @@
 int
 loadgroups(Groups *gs, char *raw)
 {
-	Group *g;
-	char *m, **memb, *s, *e, *a[5], *ide;
-	int line, n;
+	Group *g, *memb;
+	char *m, *s, *e, *a[5], *ide;
+	int line, n, k;
 	vlong id;
 
 	memset(gs, 0, sizeof(*gs));
@@ -38,7 +38,7 @@
 					goto error;
 				g->memb = memb;
 				memb += g->nmemb++;
-				*memb = m;
+				memb->name = m;
 				if((m = strchr(m, ',')) == nil)
 					break;
 			}
@@ -51,6 +51,12 @@
 			break;
 	}
 
+	g = gs->g;
+	for(n = 0; n < gs->ng; n++, g++){
+		for(k = 0; k < g->nmemb; k++)
+			findgroup(gs, g->memb[k].name, &g->memb[k].id);
+	}
+
 	return 0;
 error:
 	werrstr("togroups: %r");
@@ -104,4 +110,20 @@
 	}
 
 	return nil;
+}
+
+int
+ingroup(Group *g, u32int id)
+{
+	int i;
+
+	if(g->id == id)
+		return 1;
+
+	for(i = 0, g = g->memb; i < g->nmemb; i++, g++){
+		if(g->id == id)
+			return 1;
+	}
+
+	return 0;
 }
--- a/group.h
+++ b/group.h
@@ -4,7 +4,7 @@
 struct Group {
 	u32int id;
 	char *name;
-	char **memb;
+	Group *memb;
 	int nmemb;
 };
 
@@ -18,3 +18,4 @@
 void freegroups(Groups *gs);
 Group *findgroup(Groups *gs, char *name, u32int *id);
 Group *findgroupid(Groups *gs, u32int id);
+int ingroup(Group *g, u32int id);