ref: 6ee1f2deb589851d71b7ea4eca3ef21135bd1569
parent: 7da959bcd38cc76cb2d3cf4d6fee44c84bfbecf8
author: Jacob Moody <moody@posixcafe.org>
date: Mon Nov 24 01:32:08 EST 2025
add first pass at fs, read only and buggy .. walks do not work
--- /dev/null
+++ b/fs.c
@@ -1,0 +1,442 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include <mp.h>
+#include <libsec.h>
+#include <bio.h>
+#include "s3.h"
+#include "xml.h"
+#include "cmd.h"
+
+typedef struct Xfid Xfid;
+typedef struct Tab Tab;
+typedef struct Child Child;
+
+struct Child {+ char name[128];
+ uchar type;
+};
+
+struct Xfid {+ char path[512];
+ int nc;
+ Child *c;
+};
+
+struct Tab {+ char key[512];
+ uvlong val;
+ Tab *next;
+};
+
+Tab table[256];
+uvlong nextqid;
+S3 s3;
+
+static ulong
+shash(char *s)
+{+ ulong hash;
+
+ hash = 7;
+ for(; *s; s++)
+ hash = hash*31 + *s;
+ return hash;
+}
+
+static uvlong
+findoradd(char *s)
+{+ Tab *e, **ep;
+
+ e = &table[shash(s)%nelem(table)];
+ ep = &e;
+ if(e->key == nil)
+ goto Add;
+ for(; e != nil; e = e->next){+ ep = &e->next;
+ if(strcmp(e->key, s) == 0)
+ return e->val;
+ }
+ *ep = mallocz(sizeof *e, 1);
+Add:
+ snprint((*ep)->key, sizeof (*ep)->key, "%s", s);
+ (*ep)->val = nextqid++;
+ return (*ep)->val;
+}
+
+static void
+fsattach(Req *r)
+{+ Xfid *x;
+
+ r->fid->aux = mallocz(sizeof(Xfid), 1);
+ x = r->fid->aux;
+ x->path[0] = '/', x->path[1] = '\0';
+ r->fid->qid = (Qid){findoradd(x->path), 0, QTDIR};+ r->ofcall.qid = r->fid->qid;
+ respond(r, nil);
+}
+
+static int
+yoinkfiles(Xelem *x, char *dst[], int ndst, int trim)
+{+ int n;
+ Xelem *key;
+ Xelem *i;
+
+ /* Sanity check */
+ for(i = x; i != nil; i = i->next){+ if(strcmp(i->n, "ListBucketResult") == 0)
+ goto Valid;
+ }
+ werrstr("xml result did not include ListBucketResult");+ return -1;
+
+Valid:
+ x = xmlget(x, "Contents", nil);
+ if(x == nil)
+ return 0;
+
+ for(n = 0; x != nil && ndst > 0; x = x->next, ndst--){+ key = xmlget(x, "Key", nil);
+ if(key == nil)
+ continue;
+ dst[n++] = strdup(key->v + trim);
+ }
+ return n;
+}
+
+static int
+s3ls(char *url, char *dst[], int ndst, int trim)
+{+ char buf[2048];
+ Hcon con;
+ int n;
+ long count;
+ Xelem *raw;
+ Bstr *b;
+
+ if(s3get(&s3, &con, url) < 0){+ werrstr("file does not exist: %r");+ return -1;
+ }
+ count = read(con.body, buf, sizeof buf);
+ if(count <= 0){+ werrstr("read error on list body");+ return -1;
+ }
+ b = Bstropen(buf, count);
+ raw = xmlread(b, 0);
+ if(raw == nil){+ werrstr("s3 did not give us valid xml");+ return -1;
+ }
+ Bterm(b);
+ n = yoinkfiles(raw, dst, ndst, trim);
+ xmlfree(raw);
+
+ return n;
+}
+
+static void
+packwalk(Req *r, uchar qtype, char *prefix, char *path)
+{+ char key[512];
+ char *p;
+ int i;
+ int skip;
+
+ for(skip = 0, p = prefix+1; p = strchr(p, '/'); p++)
+ skip++;
+ for(i = 0, p = path; p = strchr(p, '/'); p++){+ if(skip--)
+ continue;
+ snprint(key, sizeof key, "%s%.*s", prefix, (int)(p-path)+1, path);
+ r->ofcall.wqid[i++] = (Qid){findoradd(key), 0, QTDIR};+ }
+ if(qtype == QTDIR)
+ snprint(key, sizeof key, "%s%s/", prefix, path);
+ else
+ snprint(key, sizeof key, "%s%s", prefix, path);
+ r->ofcall.wqid[i++] = (Qid){findoradd(key), 0, qtype};+ r->ofcall.nwqid = i;
+}
+
+static void
+fswalk(Req *r)
+{+ char path[512], url[512];
+ char *files[128];
+ char *p, *e;
+ int i, n, len;
+ Xfid *x, *dst;
+
+ e = path + sizeof path;
+ p = path;
+ x = r->fid->aux;
+
+ if(r->ifcall.newfid != r->ifcall.fid){+ r->newfid->aux = mallocz(sizeof(Xfid), 1);
+ dst = r->newfid->aux;
+ memcpy(dst->path, x->path, sizeof x->path);
+ } else
+ dst = x;
+ if(r->ifcall.nwname == 0)
+ goto Done;
+ //Our paths start at /, s3's do not
+ snprint(path, sizeof path, "%s", dst->path+1);
+ for(i = 0; i < r->ifcall.nwname-1; i++)
+ p = seprint(p, e, "%s%s", r->ifcall.wname[i], "/");
+ snprint(url, sizeof url, "?list-type=2&prefix=%U", path);
+ n = s3ls(url, files, nelem(files), strlen(path));
+ if(n < 0){+ responderror(r);
+ return;
+ }
+ if(n == 0){+ respond(r, "no Key's in result");
+ return;
+ }
+
+ /*
+ * s3 doesn't really have dirs and files,
+ * it just has files which can have / in the name.
+ * Common uses still use / as a path seperator, so we can do that.
+ * However, all we get back from a list are keys, so we need to fabricate
+ * directories on the fly.
+ */
+ p = r->ifcall.wname[r->ifcall.nwname-1];
+ len = strlen(p);
+ for(i = 0; i < n; i++){+ if(strcmp(files[i], p) == 0){+ //Exact match, a file.
+ packwalk(r, QTFILE, x->path, path);
+ snprint(dst->path, sizeof dst->path, "/%s%s", path, files[i]);
+ goto Done;
+ }
+ if(strncmp(files[i], p, len) == 0 && files[i][len] == '/'){+ //There are keys under, we're a directory
+ packwalk(r, QTDIR, x->path, path);
+ snprint(dst->path, sizeof dst->path, "/%s%.*s/", path, len, files[i]);
+ goto Done;
+ }
+ }
+ // No results
+ respond(r, "file does not exist");
+ return;
+
+Done:
+ respond(r, nil);
+}
+
+static void
+fsopen(Req *r)
+{+ respond(r, nil);
+}
+
+static void
+fsclose(Req *r)
+{+ Xfid *x;
+
+ x = r->fid->aux;
+ if(x->c){+ //Only cache list results until file is closed
+ free(x->c);
+ x->c = nil;
+ }
+ respond(r, nil);
+}
+
+static void
+filldir(Dir *d, uchar type, char *path)
+{+ char *p;
+
+ d->qid = (Qid){findoradd(path), 0, type};+ d->mode = 0555;
+ d->atime = 0;
+ d->mtime = 0;
+ d->length = 0;
+ if(path[0] == '/' && path[1] == '\0')
+ d->name = estrdup9p("/");+ else {+ p = strrchr(path, '/');
+ if(type == QTDIR){+ d->mode |= DMDIR;
+ p--;
+ while(*p != '/')
+ p--;
+ d->name = estrdup9p(p+1);
+ p = strchr(d->name, '/');
+ *p = '\0';
+
+ } else
+ d->name = estrdup9p(p+1);
+ }
+ d->uid = estrdup9p("sys");+ d->gid = estrdup9p("sys");+ d->muid = estrdup9p("sys");+}
+
+static int
+dirgen(int n, Dir *d, void *aux)
+{+ Xfid *x;
+
+ x = aux;
+ if(n >= x->nc)
+ return -1;
+ filldir(d, x->c[n].type, x->c[n].name);
+ return 0;
+}
+
+static void
+fsread(Req *r)
+{+ Hcon con;
+ int i, ret;
+ long n, wr;
+ char *files[128];
+ char url[512];
+ Xfid *x;
+ char *p;
+ uchar type;
+ char err[ERRMAX];
+
+ x = r->fid->aux;
+ switch(r->fid->qid.type){+ case QTFILE:
+ wr = 0;
+ ret = s3getrange(&s3, &con, x->path+1, r->ifcall.offset, r->ifcall.count);
+ if(ret < 0){+ rerrstr(err, ERRMAX);
+ if(strstr(err, "416 Range Not Satisfiable") == err)
+ goto Doneread;
+ responderror(r);
+ return;
+ }
+ for(;;){+ n = read(con.body, r->ofcall.data+wr, r->ifcall.count-wr);
+ if(n == 0)
+ break;
+ if(n < 0){+ respond(r, "read error: %r");
+ return;
+ }
+ wr += n;
+ }
+Doneread:
+ r->ofcall.count = wr;
+ respond(r, nil);
+ return;
+ case QTDIR:
+ if(x->c == nil){+ snprint(url, sizeof url, "?list-type=2&prefix=%U", x->path+1);
+ n = s3ls(url, files, nelem(files), strlen(x->path+1));
+ if(n < 0){+ responderror(r);
+ return;
+ }
+ x->c = mallocz(sizeof *x->c * n, 1);
+ x->nc = 0;
+ for(i = 0; i < n; i++){+ p = strchr(files[i], '/');
+ if(p){+ type = QTDIR;
+ *p = '\0';
+ } else
+ type = QTFILE;
+ snprint(x->c[x->nc].name, 128, "%s%s%s", x->path, files[i], type == QTDIR ? "/" : "");
+ x->c[x->nc++].type = type;
+ }
+ }
+ dirread9p(r, dirgen, x);
+ respond(r, nil);
+ return;
+ }
+}
+
+static void
+fsstat(Req *r)
+{+ Xfid *x;
+
+ x = r->fid->aux;
+ filldir(&r->d, r->fid->qid.type, x->path);
+ respond(r, nil);
+}
+
+static void
+fsdestroy(Fid *fid)
+{+ Xfid *x;
+
+ if(fid->aux){+ x = fid->aux;
+ free(x->c);
+ }
+}
+
+Srv fs = {+ .attach=fsattach,
+ .walk=fswalk,
+ .read=fsread,
+ .stat=fsstat,
+ .destroyfid=fsdestroy,
+};
+
+_Noreturn void
+usage(void)
+{+ fprint(2, "Usage: %s [-Dabr] [-m mntpt] [-s srv] bucket\n", argv0);
+ exits("usage");+}
+
+void
+main(int argc, char **argv)
+{+ int mflag;
+ char *mntpt, *srvname;
+
+ s3fmtinstall();
+ tmfmtinstall();
+ fmtinstall('H', encodefmt);+ parseargs(&s3, argc, argv);
+
+ mflag = MREPL;
+ mntpt = srvname = nil;
+ ARGBEGIN{+ case 'D':
+ chatty9p++;
+ break;
+ case 'a':
+ mflag = MAFTER;
+ break;
+ case 'b':
+ mflag = MBEFORE;
+ break;
+ case 'm':
+ mntpt = EARGF(usage());
+ break;
+ case 'r':
+ mflag = MREPL;
+ break;
+ case 's':
+ srvname = EARGF(usage());
+ break;
+ }ARGEND
+ if(argc == 0)
+ usage();
+
+ s3.bucket = argv[0];
+ if(mntpt == nil && srvname == nil)
+ mntpt = smprint("/n/%s", argv[0]);+ postmountsrv(&fs, srvname, mntpt, mflag);
+ exits(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -10,6 +10,7 @@
ls\
cp\
write\
+ fs\
HFILES=\
xml.h\
@@ -27,3 +28,5 @@
$O.write: write.$O s3.$O cmd.$O xml.$O
$O.cp: cp.$O s3.$O cmd.$O
+
+$O.fs: fs.$O s3.$O xml.$O cmd.$O
--
⑨