aboutsummaryrefslogtreecommitdiff
path: root/src/libdisk/proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libdisk/proto.c')
-rw-r--r--src/libdisk/proto.c513
1 files changed, 513 insertions, 0 deletions
diff --git a/src/libdisk/proto.c b/src/libdisk/proto.c
new file mode 100644
index 00000000..8c94ddca
--- /dev/null
+++ b/src/libdisk/proto.c
@@ -0,0 +1,513 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <auth.h>
+#include <fcall.h>
+#include <disk.h>
+
+enum {
+ LEN = 8*1024,
+ HUNKS = 128,
+};
+
+#undef warn
+#define warn protowarn
+
+#undef getmode
+#define getmode protogetmode
+
+typedef struct File File;
+struct File{
+ char *new;
+ char *elem;
+ char *old;
+ char *uid;
+ char *gid;
+ ulong mode;
+};
+
+typedef void Mkfserr(char*, void*);
+typedef void Mkfsenum(char*, char*, Dir*, void*);
+
+typedef struct Name Name;
+struct Name {
+ int n;
+ char *s;
+};
+
+typedef struct Mkaux Mkaux;
+struct Mkaux {
+ Mkfserr *warn;
+ Mkfsenum *mkenum;
+ char *root;
+ char *proto;
+ jmp_buf jmp;
+ Biobuf *b;
+
+ Name oldfile;
+ Name fullname;
+ int lineno;
+ int indent;
+
+ void *a;
+};
+
+static void domkfs(Mkaux *mkaux, File *me, int level);
+
+static int copyfile(Mkaux*, File*, Dir*, int);
+static void freefile(File*);
+static File* getfile(Mkaux*, File*);
+static char* getmode(Mkaux*, char*, ulong*);
+static char* getname(Mkaux*, char*, char**);
+static char* getpath(Mkaux*, char*);
+static int mkfile(Mkaux*, File*);
+static char* mkpath(Mkaux*, char*, char*);
+static void mktree(Mkaux*, File*, int);
+static void setnames(Mkaux*, File*);
+static void skipdir(Mkaux*);
+static void warn(Mkaux*, char *, ...);
+
+//static void
+//mprint(char *new, char *old, Dir *d, void*)
+//{
+// print("%s %s %D\n", new, old, d);
+//}
+
+int
+rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a)
+{
+ Mkaux mx, *m;
+ File file;
+ int rv;
+
+ m = &mx;
+ memset(&mx, 0, sizeof mx);
+ if(root == nil)
+ root = "/";
+
+ m->root = root;
+ m->warn = mkerr;
+ m->mkenum = mkenum;
+ m->a = a;
+ m->proto = proto;
+ m->lineno = 0;
+ m->indent = 0;
+ if((m->b = Bopen(proto, OREAD)) == nil) {
+ werrstr("open '%s': %r", proto);
+ return -1;
+ }
+
+ memset(&file, 0, sizeof file);
+ file.new = "";
+ file.old = nil;
+
+ *(&rv) = 0;
+ if(setjmp(m->jmp) == 0)
+ domkfs(m, &file, -1);
+ else
+ rv = -1;
+ free(m->oldfile.s);
+ free(m->fullname.s);
+ return rv;
+}
+
+static void*
+emalloc(Mkaux *mkaux, ulong n)
+{
+ void *v;
+
+ v = malloc(n);
+ if(v == nil)
+ longjmp(mkaux->jmp, 1); /* memory leak */
+ memset(v, 0, n);
+ return v;
+}
+
+static char*
+estrdup(Mkaux *mkaux, char *s)
+{
+ s = strdup(s);
+ if(s == nil)
+ longjmp(mkaux->jmp, 1); /* memory leak */
+ return s;
+}
+
+static void
+domkfs(Mkaux *mkaux, File *me, int level)
+{
+ File *child;
+ int rec;
+
+ child = getfile(mkaux, me);
+ if(!child)
+ return;
+ if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
+ rec = child->elem[0] == '+';
+ free(child->new);
+ child->new = estrdup(mkaux, me->new);
+ setnames(mkaux, child);
+ mktree(mkaux, child, rec);
+ freefile(child);
+ child = getfile(mkaux, me);
+ }
+ while(child && mkaux->indent > level){
+ if(mkfile(mkaux, child))
+ domkfs(mkaux, child, mkaux->indent);
+ freefile(child);
+ child = getfile(mkaux, me);
+ }
+ if(child){
+ freefile(child);
+ Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
+ mkaux->lineno--;
+ }
+}
+
+static void
+mktree(Mkaux *mkaux, File *me, int rec)
+{
+ File child;
+ Dir *d;
+ int i, n, fd;
+
+ fd = open(mkaux->oldfile.s, OREAD);
+ if(fd < 0){
+ warn(mkaux, "can't open %s: %r", mkaux->oldfile.s);
+ return;
+ }
+
+ child = *me;
+ while((n = dirread(fd, &d)) > 0){
+ for(i = 0; i < n; i++){
+ child.new = mkpath(mkaux, me->new, d[i].name);
+ if(me->old)
+ child.old = mkpath(mkaux, me->old, d[i].name);
+ child.elem = d[i].name;
+ setnames(mkaux, &child);
+ if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec)
+ mktree(mkaux, &child, rec);
+ free(child.new);
+ if(child.old)
+ free(child.old);
+ }
+ }
+ close(fd);
+}
+
+static int
+mkfile(Mkaux *mkaux, File *f)
+{
+ Dir *d;
+
+ if((d = dirstat(mkaux->oldfile.s)) == nil){
+ warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s);
+ skipdir(mkaux);
+ return 0;
+ }
+ return copyfile(mkaux, f, d, 0);
+}
+
+enum {
+ SLOP = 30
+};
+
+static void
+setname(Mkaux *mkaux, Name *name, char *s1, char *s2)
+{
+ int l;
+
+ l = strlen(s1)+strlen(s2)+1;
+ if(name->n < l+SLOP/2) {
+ free(name->s);
+ name->s = emalloc(mkaux, l+SLOP);
+ name->n = l+SLOP;
+ }
+ snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2);
+}
+
+static int
+copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
+{
+ Dir *nd;
+ ulong xmode;
+ char *p;
+
+ setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new);
+ /*
+ * Extra stat here is inefficient but accounts for binds.
+ */
+ if((nd = dirstat(mkaux->fullname.s)) != nil)
+ d = nd;
+
+ d->name = f->elem;
+ if(d->type != 'M'){
+ d->uid = "sys";
+ d->gid = "sys";
+ xmode = (d->mode >> 6) & 7;
+ d->mode |= xmode | (xmode << 3);
+ }
+ if(strcmp(f->uid, "-") != 0)
+ d->uid = f->uid;
+ if(strcmp(f->gid, "-") != 0)
+ d->gid = f->gid;
+ if(f->mode != ~0){
+ if(permonly)
+ d->mode = (d->mode & ~0666) | (f->mode & 0666);
+ else if((d->mode&DMDIR) != (f->mode&DMDIR))
+ warn(mkaux, "inconsistent mode for %s", f->new);
+ else
+ d->mode = f->mode;
+ }
+
+ if(p = strrchr(f->new, '/'))
+ d->name = p+1;
+ else
+ d->name = f->new;
+
+ mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
+ xmode = d->mode;
+ free(nd);
+ return (xmode&DMDIR) != 0;
+}
+
+static char *
+mkpath(Mkaux *mkaux, char *prefix, char *elem)
+{
+ char *p;
+ int n;
+
+ n = strlen(prefix) + strlen(elem) + 2;
+ p = emalloc(mkaux, n);
+ strcpy(p, prefix);
+ strcat(p, "/");
+ strcat(p, elem);
+ return p;
+}
+
+static void
+setnames(Mkaux *mkaux, File *f)
+{
+
+ if(f->old){
+ if(f->old[0] == '/')
+ setname(mkaux, &mkaux->oldfile, f->old, "");
+ else
+ setname(mkaux, &mkaux->oldfile, mkaux->root, f->old);
+ } else
+ setname(mkaux, &mkaux->oldfile, mkaux->root, f->new);
+}
+
+static void
+freefile(File *f)
+{
+ if(f->old)
+ free(f->old);
+ if(f->new)
+ free(f->new);
+ free(f);
+}
+
+/*
+ * skip all files in the proto that
+ * could be in the current dir
+ */
+static void
+skipdir(Mkaux *mkaux)
+{
+ char *p, c;
+ int level;
+
+ if(mkaux->indent < 0)
+ return;
+ level = mkaux->indent;
+ for(;;){
+ mkaux->indent = 0;
+ p = Brdline(mkaux->b, '\n');
+ mkaux->lineno++;
+ if(!p){
+ mkaux->indent = -1;
+ return;
+ }
+ while((c = *p++) != '\n')
+ if(c == ' ')
+ mkaux->indent++;
+ else if(c == '\t')
+ mkaux->indent += 8;
+ else
+ break;
+ if(mkaux->indent <= level){
+ Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
+ mkaux->lineno--;
+ return;
+ }
+ }
+}
+
+static File*
+getfile(Mkaux *mkaux, File *old)
+{
+ File *f;
+ char *elem;
+ char *p;
+ int c;
+
+ if(mkaux->indent < 0)
+ return 0;
+loop:
+ mkaux->indent = 0;
+ p = Brdline(mkaux->b, '\n');
+ mkaux->lineno++;
+ if(!p){
+ mkaux->indent = -1;
+ return 0;
+ }
+ while((c = *p++) != '\n')
+ if(c == ' ')
+ mkaux->indent++;
+ else if(c == '\t')
+ mkaux->indent += 8;
+ else
+ break;
+ if(c == '\n' || c == '#')
+ goto loop;
+ p--;
+ f = emalloc(mkaux, sizeof *f);
+ p = getname(mkaux, p, &elem);
+ if(p == nil)
+ return nil;
+
+ f->new = mkpath(mkaux, old->new, elem);
+ free(elem);
+ f->elem = utfrrune(f->new, L'/') + 1;
+ p = getmode(mkaux, p, &f->mode);
+ p = getname(mkaux, p, &f->uid); /* LEAK */
+ if(p == nil)
+ return nil;
+
+ if(!*f->uid)
+ strcpy(f->uid, "-");
+ p = getname(mkaux, p, &f->gid); /* LEAK */
+ if(p == nil)
+ return nil;
+
+ if(!*f->gid)
+ strcpy(f->gid, "-");
+ f->old = getpath(mkaux, p);
+ if(f->old && strcmp(f->old, "-") == 0){
+ free(f->old);
+ f->old = 0;
+ }
+ setnames(mkaux, f);
+
+ return f;
+}
+
+static char*
+getpath(Mkaux *mkaux, char *p)
+{
+ char *q, *new;
+ int c, n;
+
+ while((c = *p) == ' ' || c == '\t')
+ p++;
+ q = p;
+ while((c = *q) != '\n' && c != ' ' && c != '\t')
+ q++;
+ if(q == p)
+ return 0;
+ n = q - p;
+ new = emalloc(mkaux, n + 1);
+ memcpy(new, p, n);
+ new[n] = 0;
+ return new;
+}
+
+static char*
+getname(Mkaux *mkaux, char *p, char **buf)
+{
+ char *s, *start;
+ int c;
+
+ while((c = *p) == ' ' || c == '\t')
+ p++;
+
+ start = p;
+ while((c = *p) != '\n' && c != ' ' && c != '\t')
+ p++;
+
+ *buf = malloc(p+2-start); /* +2: need at least 2 bytes; might strcpy "-" into buf */
+ if(*buf == nil)
+ return nil;
+ memmove(*buf, start, p-start);
+
+ (*buf)[p-start] = '\0';
+
+ if(**buf == '$'){
+ s = getenv(*buf+1);
+ if(s == 0){
+ warn(mkaux, "can't read environment variable %s", *buf+1);
+ skipdir(mkaux);
+ free(*buf);
+ return nil;
+ }
+ free(*buf);
+ *buf = s;
+ }
+ return p;
+}
+
+static char*
+getmode(Mkaux *mkaux, char *p, ulong *xmode)
+{
+ char *buf, *s;
+ ulong m;
+
+ *xmode = ~0;
+ p = getname(mkaux, p, &buf);
+ if(p == nil)
+ return nil;
+
+ s = buf;
+ if(!*s || strcmp(s, "-") == 0)
+ return p;
+ m = 0;
+ if(*s == 'd'){
+ m |= DMDIR;
+ s++;
+ }
+ if(*s == 'a'){
+ m |= DMAPPEND;
+ s++;
+ }
+ if(*s == 'l'){
+ m |= DMEXCL;
+ s++;
+ }
+ if(s[0] < '0' || s[0] > '7'
+ || s[1] < '0' || s[1] > '7'
+ || s[2] < '0' || s[2] > '7'
+ || s[3]){
+ warn(mkaux, "bad mode specification %s", buf);
+ free(buf);
+ return p;
+ }
+ *xmode = m | strtoul(s, 0, 8);
+ free(buf);
+ return p;
+}
+
+static void
+warn(Mkaux *mkaux, char *fmt, ...)
+{
+ char buf[256];
+ va_list va;
+
+ va_start(va, fmt);
+ vseprint(buf, buf+sizeof(buf), fmt, va);
+ va_end(va);
+
+ if(mkaux->warn)
+ mkaux->warn(buf, mkaux->a);
+ else
+ fprint(2, "warning: %s\n", buf);
+}