tdisk/mkfs, disk/mkext: add from Plan 9 - plan9port - [fork] Plan 9 from user s… | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit d2173bb552d308d60a4e4a53cd3b8e0949b38dbc | |
parent f0add8ef24f83cb2085ef1c7534d16c57881e3f3 | |
Author: Russ Cox <[email protected]> | |
Date: Tue, 17 Jul 2012 19:10:45 -0400 | |
disk/mkfs, disk/mkext: add from Plan 9 | |
R=rsc, rsc | |
http://codereview.appspot.com/6405057 | |
Diffstat: | |
A bin/disk/.placeholder | 0 | |
A man/man8/mkfs.8 | 187 +++++++++++++++++++++++++++++… | |
A src/cmd/disk/mkext.c | 318 +++++++++++++++++++++++++++++… | |
A src/cmd/disk/mkfile | 11 +++++++++++ | |
A src/cmd/disk/mkfs.c | 840 +++++++++++++++++++++++++++++… | |
5 files changed, 1356 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/bin/disk/.placeholder b/bin/disk/.placeholder | |
diff --git a/man/man8/mkfs.8 b/man/man8/mkfs.8 | |
t@@ -0,0 +1,187 @@ | |
+.TH MKFS 8 | |
+.SH NAME | |
+mkfs, mkext \- archive or update a file system | |
+.SH SYNOPSIS | |
+.B disk/mkfs | |
+.RB [ -aprvxU ] | |
+.RB [ -d | |
+.IR root ] | |
+.RB [ -n | |
+.IR name ] | |
+.RB [ -s | |
+.IR source ] | |
+.RB [ -u | |
+.IR users ] | |
+.RB [ -z | |
+.IR n ] | |
+.I proto ... | |
+.PP | |
+.B disk/mkext | |
+.RB [ -d | |
+.IR name ] | |
+.RB [ -u ] | |
+.RB [ -h ] | |
+.RB [ -v ] | |
+.RB [ -x ] | |
+.RB [ -T ] | |
+.I file ... | |
+.SH DESCRIPTION | |
+.I Mkfs | |
+copies files from the file tree | |
+.I source | |
+(default | |
+.BR / ) | |
+to a | |
+.B kfs | |
+file system (see | |
+.IR kfs (4)). | |
+The kfs service is mounted on | |
+.I root | |
+(default | |
+.BR /n/kfs ), | |
+and | |
+.B /adm/users | |
+is copied to | |
+.IB root /adm/users\f1. | |
+The | |
+.I proto | |
+files are read | |
+(see | |
+.IR proto (2) | |
+for their format) | |
+and any files specified in them that are out of date are copied to | |
+.BR /n/kfs . | |
+.PP | |
+.I Mkfs | |
+copies only those files that are out of date. | |
+Such a file is first copied into a temporary | |
+file in the appropriate destination directory | |
+and then moved to the destination file. | |
+Files in the | |
+.I kfs | |
+file system that are not specified in the | |
+.I proto | |
+file | |
+are not updated and not removed. | |
+.PP | |
+The options to | |
+.I mkfs | |
+are: | |
+.TF "s source" | |
+.TP | |
+.B a | |
+Instead of writing to a | |
+.B kfs | |
+file system, write an archive file to standard output, suitable for | |
+.IR mkext . | |
+All files in | |
+.IR proto , | |
+not just those out of date, are archived. | |
+.TP | |
+.B x | |
+For use with | |
+.BR -a , | |
+this option writes a list of file names, dates, and sizes to standard output | |
+rather than producing an archive file. | |
+.TP | |
+.BI "d " root | |
+Copy files into the tree rooted at | |
+.I root | |
+(default | |
+.BR /n/kfs ). | |
+This option suppresses setting the | |
+.B uid | |
+and | |
+.B gid | |
+fields when copying files. | |
+Use | |
+.B -U | |
+to reenable it. | |
+.TP | |
+.BI "n " name | |
+Use | |
+.RI kfs. name | |
+as the name of the kfs service (default | |
+.BR kfs ). | |
+.TP | |
+.B p | |
+Update the permissions of a file even if it is up to date. | |
+.TP | |
+.B r | |
+Copy all files. | |
+.TP | |
+.BI "s " source | |
+Copy from files rooted at the tree | |
+.IR source . | |
+.TP | |
+.BI "u " users | |
+Copy file | |
+.I users | |
+into | |
+.B /adm/users | |
+in the new system. | |
+.TP | |
+.B v | |
+Print the names of all of the files as they are copied. | |
+.TP | |
+.BI "z " n | |
+Copy files assuming kfs block | |
+.I n | |
+(default 1024) | |
+bytes long. | |
+If a block contains only 0-valued bytes, it is not copied. | |
+.PD | |
+.PP | |
+.I Mkext | |
+unpacks archive files made by the | |
+.B -a | |
+option of | |
+.IR mkfs . | |
+Each file on the command line is unpacked in one pass through the archive. | |
+If the file is a directory, | |
+all files and subdirectories of that directory are also unpacked. | |
+When a file is unpacked, the entire path is created if it | |
+does not exist. | |
+If no files are specified, the entire archive is unpacked; | |
+in this case, missing intermediate directories are not created. | |
+The options are: | |
+.TP | |
+.B d | |
+specifies a directory (default | |
+.BR / ) | |
+to serve as the root of the unpacked file system. | |
+.TP | |
+.B u | |
+sets the owners of the files created to correspond to | |
+those in the archive and restores the modification times of the files. | |
+.TP | |
+.B T | |
+restores only the modification times of the files. | |
+.TP | |
+.B v | |
+prints the names and sizes of files as they are extracted. | |
+.TP | |
+.B h | |
+prints headers for the files on standard output | |
+instead of unpacking the files. | |
+.PD | |
+.SH EXAMPLES | |
+.PP | |
+Make an archive to establish a new file system: | |
+.IP | |
+.EX | |
+disk/mkfs -a -u files/adm.users -s dist proto > arch | |
+.EE | |
+.PP | |
+Unpack that archive onto a new file system: | |
+.IP | |
+.EX | |
+disk/mkext -u -d /n/newfs < arch | |
+.EE | |
+.SH SOURCE | |
+.B \*9/src/cmd/disk/mkfs.c | |
+.br | |
+.B \*9/src/cmd/disk/mkext.c | |
+.SH "SEE ALSO" | |
+.IR prep (8), | |
+.IR tar (1) | |
diff --git a/src/cmd/disk/mkext.c b/src/cmd/disk/mkext.c | |
t@@ -0,0 +1,318 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <bio.h> | |
+ | |
+enum{ | |
+ LEN = 8*1024, | |
+ NFLDS = 6, /* filename, modes, uid, gid, mtime, … | |
+}; | |
+ | |
+int selected(char*, int, char*[]); | |
+void mkdirs(char*, char*); | |
+void mkdir(char*, ulong, ulong, char*, char*); | |
+void extract(char*, ulong, ulong, char*, char*, uvlong); | |
+void seekpast(uvlong); | |
+void error(char*, ...); | |
+void warn(char*, ...); | |
+void usage(void); | |
+#pragma varargck argpos warn 1 | |
+#pragma varargck argpos error 1 | |
+ | |
+Biobuf bin; | |
+uchar binbuf[2*LEN]; | |
+char linebuf[LEN]; | |
+int uflag; | |
+int hflag; | |
+int vflag; | |
+int Tflag; | |
+ | |
+void | |
+main(int argc, char **argv) | |
+{ | |
+ Biobuf bout; | |
+ char *fields[NFLDS], name[2*LEN], *p, *namep; | |
+ char *uid, *gid; | |
+ ulong mode, mtime; | |
+ uvlong bytes; | |
+ | |
+ quotefmtinstall(); | |
+ namep = name; | |
+ ARGBEGIN{ | |
+ case 'd': | |
+ p = ARGF(); | |
+ if(strlen(p) >= LEN) | |
+ error("destination fs name too long\n"); | |
+ strcpy(name, p); | |
+ namep = name + strlen(name); | |
+ break; | |
+ case 'h': | |
+ hflag = 1; | |
+ Binit(&bout, 1, OWRITE); | |
+ break; | |
+ case 'u': | |
+ uflag = 1; | |
+ Tflag = 1; | |
+ break; | |
+ case 'T': | |
+ Tflag = 1; | |
+ break; | |
+ case 'v': | |
+ vflag = 1; | |
+ break; | |
+ default: | |
+ usage(); | |
+ }ARGEND | |
+ | |
+ Binits(&bin, 0, OREAD, binbuf, sizeof binbuf); | |
+ while(p = Brdline(&bin, '\n')){ | |
+ p[Blinelen(&bin)-1] = '\0'; | |
+ strcpy(linebuf, p); | |
+ p = linebuf; | |
+ if(strcmp(p, "end of archive") == 0){ | |
+ Bterm(&bout); | |
+ fprint(2, "done\n"); | |
+ exits(0); | |
+ } | |
+ if (gettokens(p, fields, NFLDS, " \t") != NFLDS){ | |
+ warn("too few fields in file header"); | |
+ continue; | |
+ } | |
+ p = unquotestrdup(fields[0]); | |
+ strcpy(namep, p); | |
+ free(p); | |
+ mode = strtoul(fields[1], 0, 8); | |
+ uid = fields[2]; | |
+ gid = fields[3]; | |
+ mtime = strtoul(fields[4], 0, 10); | |
+ bytes = strtoull(fields[5], 0, 10); | |
+ if(argc){ | |
+ if(!selected(namep, argc, argv)){ | |
+ if(bytes) | |
+ seekpast(bytes); | |
+ continue; | |
+ } | |
+ mkdirs(name, namep); | |
+ } | |
+ if(hflag){ | |
+ Bprint(&bout, "%q %luo %q %q %lud %llud\n", | |
+ name, mode, uid, gid, mtime, bytes); | |
+ if(bytes) | |
+ seekpast(bytes); | |
+ continue; | |
+ } | |
+ if(mode & DMDIR) | |
+ mkdir(name, mode, mtime, uid, gid); | |
+ else | |
+ extract(name, mode, mtime, uid, gid, bytes); | |
+ } | |
+ fprint(2, "premature end of archive\n"); | |
+ exits("premature end of archive"); | |
+} | |
+ | |
+int | |
+fileprefix(char *prefix, char *s) | |
+{ | |
+ while(*prefix) | |
+ if(*prefix++ != *s++) | |
+ return 0; | |
+ if(*s && *s != '/') | |
+ return 0; | |
+ return 1; | |
+} | |
+ | |
+int | |
+selected(char *s, int argc, char *argv[]) | |
+{ | |
+ int i; | |
+ | |
+ for(i=0; i<argc; i++) | |
+ if(fileprefix(argv[i], s)) | |
+ return 1; | |
+ return 0; | |
+} | |
+ | |
+void | |
+mkdirs(char *name, char *namep) | |
+{ | |
+ char buf[2*LEN], *p; | |
+ int fd; | |
+ | |
+ strcpy(buf, name); | |
+ for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){ | |
+ if(p[1] == '\0') | |
+ return; | |
+ *p = 0; | |
+ fd = create(buf, OREAD, 0775|DMDIR); | |
+ close(fd); | |
+ *p = '/'; | |
+ } | |
+} | |
+ | |
+void | |
+mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid) | |
+{ | |
+ Dir *d, xd; | |
+ int fd; | |
+ char *p; | |
+ char olderr[256]; | |
+ | |
+ fd = create(name, OREAD, mode); | |
+ if(fd < 0){ | |
+ rerrstr(olderr, sizeof(olderr)); | |
+ if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){ | |
+ free(d); | |
+ warn("can't make directory %q, mode %luo: %s", name, m… | |
+ return; | |
+ } | |
+ free(d); | |
+ } | |
+ close(fd); | |
+ | |
+ d = &xd; | |
+ nulldir(d); | |
+ p = utfrrune(name, L'/'); | |
+ if(p) | |
+ p++; | |
+ else | |
+ p = name; | |
+ d->name = p; | |
+ if(uflag){ | |
+ d->uid = uid; | |
+ d->gid = gid; | |
+ } | |
+ if(Tflag) | |
+ d->mtime = mtime; | |
+ d->mode = mode; | |
+ if(dirwstat(name, d) < 0) | |
+ warn("can't set modes for %q: %r", name); | |
+ | |
+ if(uflag||Tflag){ | |
+ if((d = dirstat(name)) == nil){ | |
+ warn("can't reread modes for %q: %r", name); | |
+ return; | |
+ } | |
+ if(Tflag && d->mtime != mtime) | |
+ warn("%q: time mismatch %lud %lud\n", name, mtime, d->… | |
+ if(uflag && strcmp(uid, d->uid)) | |
+ warn("%q: uid mismatch %q %q", name, uid, d->uid); | |
+ if(uflag && strcmp(gid, d->gid)) | |
+ warn("%q: gid mismatch %q %q", name, gid, d->gid); | |
+ } | |
+} | |
+ | |
+void | |
+extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong byte… | |
+{ | |
+ Dir d, *nd; | |
+ Biobuf *b; | |
+ char buf[LEN]; | |
+ char *p; | |
+ ulong n; | |
+ uvlong tot; | |
+ | |
+ if(vflag) | |
+ print("x %q %llud bytes\n", name, bytes); | |
+ | |
+ b = Bopen(name, OWRITE); | |
+ if(!b){ | |
+ warn("can't make file %q: %r", name); | |
+ seekpast(bytes); | |
+ return; | |
+ } | |
+ for(tot = 0; tot < bytes; tot += n){ | |
+ n = sizeof buf; | |
+ if(tot + n > bytes) | |
+ n = bytes - tot; | |
+ n = Bread(&bin, buf, n); | |
+ if(n <= 0) | |
+ error("premature eof reading %q", name); | |
+ if(Bwrite(b, buf, n) != n) | |
+ warn("error writing %q: %r", name); | |
+ } | |
+ | |
+ nulldir(&d); | |
+ p = utfrrune(name, '/'); | |
+ if(p) | |
+ p++; | |
+ else | |
+ p = name; | |
+ d.name = p; | |
+ if(uflag){ | |
+ d.uid = uid; | |
+ d.gid = gid; | |
+ } | |
+ if(Tflag) | |
+ d.mtime = mtime; | |
+ d.mode = mode; | |
+ Bflush(b); | |
+ if(dirfwstat(Bfildes(b), &d) < 0) | |
+ warn("can't set modes for %q: %r", name); | |
+ if(uflag||Tflag){ | |
+ if((nd = dirfstat(Bfildes(b))) == nil) | |
+ warn("can't reread modes for %q: %r", name); | |
+ else{ | |
+ if(Tflag && nd->mtime != mtime) | |
+ warn("%q: time mismatch %lud %lud\n", | |
+ name, mtime, nd->mtime); | |
+ if(uflag && strcmp(uid, nd->uid)) | |
+ warn("%q: uid mismatch %q %q", | |
+ name, uid, nd->uid); | |
+ if(uflag && strcmp(gid, nd->gid)) | |
+ warn("%q: gid mismatch %q %q", | |
+ name, gid, nd->gid); | |
+ free(nd); | |
+ } | |
+ } | |
+ Bterm(b); | |
+} | |
+ | |
+void | |
+seekpast(uvlong bytes) | |
+{ | |
+ char buf[LEN]; | |
+ long n; | |
+ uvlong tot; | |
+ | |
+ for(tot = 0; tot < bytes; tot += n){ | |
+ n = sizeof buf; | |
+ if(tot + n > bytes) | |
+ n = bytes - tot; | |
+ n = Bread(&bin, buf, n); | |
+ if(n < 0) | |
+ error("premature eof"); | |
+ } | |
+} | |
+ | |
+void | |
+error(char *fmt, ...) | |
+{ | |
+ char buf[1024]; | |
+ va_list arg; | |
+ | |
+ sprint(buf, "%q: ", argv0); | |
+ va_start(arg, fmt); | |
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); | |
+ va_end(arg); | |
+ fprint(2, "%s\n", buf); | |
+ exits(0); | |
+} | |
+ | |
+void | |
+warn(char *fmt, ...) | |
+{ | |
+ char buf[1024]; | |
+ va_list arg; | |
+ | |
+ sprint(buf, "%q: ", argv0); | |
+ va_start(arg, fmt); | |
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); | |
+ va_end(arg); | |
+ fprint(2, "%s\n", buf); | |
+} | |
+ | |
+void | |
+usage(void) | |
+{ | |
+ fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n"); | |
+ exits("usage"); | |
+} | |
diff --git a/src/cmd/disk/mkfile b/src/cmd/disk/mkfile | |
t@@ -0,0 +1,11 @@ | |
+<$PLAN9/src/mkhdr | |
+ | |
+TARG=\ | |
+ mkext\ | |
+ mkfs\ | |
+ | |
+BIN=$BIN/disk | |
+ | |
+<$PLAN9/src/mkmany | |
+ | |
+ | |
diff --git a/src/cmd/disk/mkfs.c b/src/cmd/disk/mkfs.c | |
t@@ -0,0 +1,840 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+#include <auth.h> | |
+#include <bio.h> | |
+ | |
+#define getmode plan9_getmode | |
+#define setuid plan9_setuid | |
+ | |
+enum{ | |
+ LEN = 8*1024, | |
+ HUNKS = 128, | |
+ | |
+ /* | |
+ * types of destination file sytems | |
+ */ | |
+ Kfs = 0, | |
+ Fs, | |
+ Archive, | |
+}; | |
+ | |
+typedef struct File File; | |
+ | |
+struct File{ | |
+ char *new; | |
+ char *elem; | |
+ char *old; | |
+ char *uid; | |
+ char *gid; | |
+ ulong mode; | |
+}; | |
+ | |
+void arch(Dir*); | |
+void copy(Dir*); | |
+int copyfile(File*, Dir*, int); | |
+void* emalloc(ulong); | |
+void error(char *, ...); | |
+void freefile(File*); | |
+File* getfile(File*); | |
+char* getmode(char*, ulong*); | |
+char* getname(char*, char**); | |
+char* getpath(char*); | |
+void kfscmd(char *); | |
+void mkdir(Dir*); | |
+int mkfile(File*); | |
+void mkfs(File*, int); | |
+char* mkpath(char*, char*); | |
+void mktree(File*, int); | |
+void mountkfs(char*); | |
+void printfile(File*); | |
+void setnames(File*); | |
+void setusers(void); | |
+void skipdir(void); | |
+char* strdup(char*); | |
+int uptodate(Dir*, char*); | |
+void usage(void); | |
+void warn(char *, ...); | |
+ | |
+Biobuf *b; | |
+Biobuf bout; /* stdout when writing archive */ | |
+uchar boutbuf[2*LEN]; | |
+char newfile[LEN]; | |
+char oldfile[LEN]; | |
+char *proto; | |
+char *cputype; | |
+char *users; | |
+char *oldroot; | |
+char *newroot; | |
+char *prog = "mkfs"; | |
+int lineno; | |
+char *buf; | |
+char *zbuf; | |
+int buflen = 1024-8; | |
+int indent; | |
+int verb; | |
+int modes; | |
+int ream; | |
+int debug; | |
+int xflag; | |
+int sfd; | |
+int fskind; /* Kfs, Fs, Archive */ | |
+int setuid; /* on Fs: set uid and gid? */ | |
+char *user; | |
+ | |
+void | |
+main(int argc, char **argv) | |
+{ | |
+ File file; | |
+ char *name; | |
+ int i, errs; | |
+ | |
+ quotefmtinstall(); | |
+ user = getuser(); | |
+ name = ""; | |
+ memset(&file, 0, sizeof file); | |
+ file.new = ""; | |
+ file.old = 0; | |
+ oldroot = ""; | |
+ newroot = "/n/kfs"; | |
+ users = 0; | |
+ fskind = Kfs; | |
+ ARGBEGIN{ | |
+ case 'a': | |
+ if(fskind != Kfs) { | |
+ fprint(2, "cannot use -a with -d\n"); | |
+ usage(); | |
+ } | |
+ fskind = Archive; | |
+ newroot = ""; | |
+ Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf); | |
+ break; | |
+ case 'd': | |
+ if(fskind != Kfs) { | |
+ fprint(2, "cannot use -d with -a\n"); | |
+ usage(); | |
+ } | |
+ fskind = Fs; | |
+ newroot = ARGF(); | |
+ break; | |
+ case 'D': | |
+ debug = 1; | |
+ break; | |
+ case 'n': | |
+ name = EARGF(usage()); | |
+ break; | |
+ case 'p': | |
+ modes = 1; | |
+ break; | |
+ case 'r': | |
+ ream = 1; | |
+ break; | |
+ case 's': | |
+ oldroot = ARGF(); | |
+ break; | |
+ case 'u': | |
+ users = ARGF(); | |
+ break; | |
+ case 'U': | |
+ setuid = 1; | |
+ break; | |
+ case 'v': | |
+ verb = 1; | |
+ break; | |
+ case 'x': | |
+ xflag = 1; | |
+ break; | |
+ case 'z': | |
+ buflen = atoi(ARGF())-8; | |
+ break; | |
+ default: | |
+ usage(); | |
+ }ARGEND | |
+ | |
+ if(!argc) | |
+ usage(); | |
+ | |
+ buf = emalloc(buflen); | |
+ zbuf = emalloc(buflen); | |
+ memset(zbuf, 0, buflen); | |
+ | |
+ mountkfs(name); | |
+ kfscmd("allow"); | |
+ proto = "users"; | |
+ setusers(); | |
+ cputype = getenv("cputype"); | |
+ if(cputype == 0) | |
+ cputype = "68020"; | |
+ | |
+ errs = 0; | |
+ for(i = 0; i < argc; i++){ | |
+ proto = argv[i]; | |
+ fprint(2, "processing %q\n", proto); | |
+ | |
+ b = Bopen(proto, OREAD); | |
+ if(!b){ | |
+ fprint(2, "%q: can't open %q: skipping\n", prog, proto… | |
+ errs++; | |
+ continue; | |
+ } | |
+ | |
+ lineno = 0; | |
+ indent = 0; | |
+ mkfs(&file, -1); | |
+ Bterm(b); | |
+ } | |
+ fprint(2, "file system made\n"); | |
+ kfscmd("disallow"); | |
+ kfscmd("sync"); | |
+ if(errs) | |
+ exits("skipped protos"); | |
+ if(fskind == Archive){ | |
+ Bprint(&bout, "end of archive\n"); | |
+ Bterm(&bout); | |
+ } | |
+ exits(0); | |
+} | |
+ | |
+void | |
+mkfs(File *me, int level) | |
+{ | |
+ File *child; | |
+ int rec; | |
+ | |
+ child = getfile(me); | |
+ if(!child) | |
+ return; | |
+ if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] … | |
+ rec = child->elem[0] == '+'; | |
+ free(child->new); | |
+ child->new = strdup(me->new); | |
+ setnames(child); | |
+ mktree(child, rec); | |
+ freefile(child); | |
+ child = getfile(me); | |
+ } | |
+ while(child && indent > level){ | |
+ if(mkfile(child)) | |
+ mkfs(child, indent); | |
+ freefile(child); | |
+ child = getfile(me); | |
+ } | |
+ if(child){ | |
+ freefile(child); | |
+ Bseek(b, -Blinelen(b), 1); | |
+ lineno--; | |
+ } | |
+} | |
+ | |
+void | |
+mktree(File *me, int rec) | |
+{ | |
+ File child; | |
+ Dir *d; | |
+ int i, n, fd; | |
+ | |
+ fd = open(oldfile, OREAD); | |
+ if(fd < 0){ | |
+ warn("can't open %q: %r", oldfile); | |
+ return; | |
+ } | |
+ | |
+ child = *me; | |
+ while((n = dirread(fd, &d)) > 0){ | |
+ for(i = 0; i < n; i++){ | |
+ child.new = mkpath(me->new, d[i].name); | |
+ if(me->old) | |
+ child.old = mkpath(me->old, d[i].name); | |
+ child.elem = d[i].name; | |
+ setnames(&child); | |
+ if(copyfile(&child, &d[i], 1) && rec) | |
+ mktree(&child, rec); | |
+ free(child.new); | |
+ if(child.old) | |
+ free(child.old); | |
+ } | |
+ } | |
+ close(fd); | |
+} | |
+ | |
+int | |
+mkfile(File *f) | |
+{ | |
+ Dir *dir; | |
+ | |
+ if((dir = dirstat(oldfile)) == nil){ | |
+ warn("can't stat file %q: %r", oldfile); | |
+ skipdir(); | |
+ return 0; | |
+ } | |
+ return copyfile(f, dir, 0); | |
+} | |
+ | |
+int | |
+copyfile(File *f, Dir *d, int permonly) | |
+{ | |
+ ulong mode; | |
+ Dir nd; | |
+ | |
+ if(xflag){ | |
+ Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length); | |
+ return (d->mode & DMDIR) != 0; | |
+ } | |
+ if(verb && (fskind == Archive || ream)) | |
+ fprint(2, "%q\n", f->new); | |
+ d->name = f->elem; | |
+ if(d->type != 'M' && d->type != 'Z'){ | |
+ d->uid = "sys"; | |
+ d->gid = "sys"; | |
+ mode = (d->mode >> 6) & 7; | |
+ d->mode |= mode | (mode << 3); | |
+ } | |
+ if(strcmp(f->uid, "-") != 0) | |
+ d->uid = f->uid; | |
+ if(strcmp(f->gid, "-") != 0) | |
+ d->gid = f->gid; | |
+ if(fskind == Fs && !setuid){ | |
+ d->uid = ""; | |
+ d->gid = ""; | |
+ } | |
+ if(f->mode != ~0){ | |
+ if(permonly) | |
+ d->mode = (d->mode & ~0666) | (f->mode & 0666); | |
+ else if((d->mode&DMDIR) != (f->mode&DMDIR)) | |
+ warn("inconsistent mode for %q", f->new); | |
+ else | |
+ d->mode = f->mode; | |
+ } | |
+ if(!uptodate(d, newfile)){ | |
+ if(verb && (fskind != Archive && ream == 0)) | |
+ fprint(2, "%q\n", f->new); | |
+ if(d->mode & DMDIR) | |
+ mkdir(d); | |
+ else | |
+ copy(d); | |
+ }else if(modes){ | |
+ nulldir(&nd); | |
+ nd.mode = d->mode; | |
+ nd.gid = d->gid; | |
+ nd.mtime = d->mtime; | |
+ if(verb && (fskind != Archive && ream == 0)) | |
+ fprint(2, "%q\n", f->new); | |
+ if(dirwstat(newfile, &nd) < 0) | |
+ warn("can't set modes for %q: %r", f->new); | |
+ nulldir(&nd); | |
+ nd.uid = d->uid; | |
+ dirwstat(newfile, &nd); | |
+ } | |
+ return (d->mode & DMDIR) != 0; | |
+} | |
+ | |
+/* | |
+ * check if file to is up to date with | |
+ * respect to the file represented by df | |
+ */ | |
+int | |
+uptodate(Dir *df, char *to) | |
+{ | |
+ int ret; | |
+ Dir *dt; | |
+ | |
+ if(fskind == Archive || ream || (dt = dirstat(to)) == nil) | |
+ return 0; | |
+ ret = dt->mtime >= df->mtime; | |
+ free(dt); | |
+ return ret; | |
+} | |
+ | |
+void | |
+copy(Dir *d) | |
+{ | |
+ char cptmp[LEN], *p; | |
+ int f, t, n, needwrite, nowarnyet = 1; | |
+ vlong tot, len; | |
+ Dir nd; | |
+ | |
+ f = open(oldfile, OREAD); | |
+ if(f < 0){ | |
+ warn("can't open %q: %r", oldfile); | |
+ return; | |
+ } | |
+ t = -1; | |
+ if(fskind == Archive) | |
+ arch(d); | |
+ else{ | |
+ strcpy(cptmp, newfile); | |
+ p = utfrrune(cptmp, L'/'); | |
+ if(!p) | |
+ error("internal temporary file error"); | |
+ strcpy(p+1, "__mkfstmp"); | |
+ t = create(cptmp, OWRITE, 0666); | |
+ if(t < 0){ | |
+ warn("can't create %q: %r", newfile); | |
+ close(f); | |
+ return; | |
+ } | |
+ } | |
+ | |
+ needwrite = 0; | |
+ for(tot = 0; tot < d->length; tot += n){ | |
+ len = d->length - tot; | |
+ /* don't read beyond d->length */ | |
+ if (len > buflen) | |
+ len = buflen; | |
+ n = read(f, buf, len); | |
+ if(n <= 0) { | |
+ if(n < 0 && nowarnyet) { | |
+ warn("can't read %q: %r", oldfile); | |
+ nowarnyet = 0; | |
+ } | |
+ /* | |
+ * don't quit: pad to d->length (in pieces) to agree | |
+ * with the length in the header, already emitted. | |
+ */ | |
+ memset(buf, 0, len); | |
+ n = len; | |
+ } | |
+ if(fskind == Archive){ | |
+ if(Bwrite(&bout, buf, n) != n) | |
+ error("write error: %r"); | |
+ }else if(memcmp(buf, zbuf, n) == 0){ | |
+ if(seek(t, n, 1) < 0) | |
+ error("can't write zeros to %q: %r", newfile); | |
+ needwrite = 1; | |
+ }else{ | |
+ if(write(t, buf, n) < n) | |
+ error("can't write %q: %r", newfile); | |
+ needwrite = 0; | |
+ } | |
+ } | |
+ close(f); | |
+ if(needwrite){ | |
+ if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1) | |
+ error("can't write zero at end of %q: %r", newfile); | |
+ } | |
+ if(tot != d->length){ | |
+ /* this should no longer happen */ | |
+ warn("wrong number of bytes written to %q (was %lld should be … | |
+ newfile, tot, d->length); | |
+ if(fskind == Archive){ | |
+ warn("seeking to proper position\n"); | |
+ /* does no good if stdout is a pipe */ | |
+ Bseek(&bout, d->length - tot, 1); | |
+ } | |
+ } | |
+ if(fskind == Archive) | |
+ return; | |
+ remove(newfile); | |
+ nulldir(&nd); | |
+ nd.mode = d->mode; | |
+ nd.gid = d->gid; | |
+ nd.mtime = d->mtime; | |
+ nd.name = d->name; | |
+ if(dirfwstat(t, &nd) < 0) | |
+ error("can't move tmp file to %q: %r", newfile); | |
+ nulldir(&nd); | |
+ nd.uid = d->uid; | |
+ dirfwstat(t, &nd); | |
+ close(t); | |
+} | |
+ | |
+void | |
+mkdir(Dir *d) | |
+{ | |
+ Dir *d1; | |
+ Dir nd; | |
+ int fd; | |
+ | |
+ if(fskind == Archive){ | |
+ arch(d); | |
+ return; | |
+ } | |
+ fd = create(newfile, OREAD, d->mode); | |
+ nulldir(&nd); | |
+ nd.mode = d->mode; | |
+ nd.gid = d->gid; | |
+ nd.mtime = d->mtime; | |
+ if(fd < 0){ | |
+ if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){ | |
+ free(d1); | |
+ error("can't create %q", newfile); | |
+ } | |
+ free(d1); | |
+ if(dirwstat(newfile, &nd) < 0) | |
+ warn("can't set modes for %q: %r", newfile); | |
+ nulldir(&nd); | |
+ nd.uid = d->uid; | |
+ dirwstat(newfile, &nd); | |
+ return; | |
+ } | |
+ if(dirfwstat(fd, &nd) < 0) | |
+ warn("can't set modes for %q: %r", newfile); | |
+ nulldir(&nd); | |
+ nd.uid = d->uid; | |
+ dirfwstat(fd, &nd); | |
+ close(fd); | |
+} | |
+ | |
+void | |
+arch(Dir *d) | |
+{ | |
+ Bprint(&bout, "%q %luo %q %q %lud %lld\n", | |
+ newfile, d->mode, d->uid, d->gid, d->mtime, d->length); | |
+} | |
+ | |
+char * | |
+mkpath(char *prefix, char *elem) | |
+{ | |
+ char *p; | |
+ int n; | |
+ | |
+ n = strlen(prefix) + strlen(elem) + 2; | |
+ p = emalloc(n); | |
+ sprint(p, "%s/%s", prefix, elem); | |
+ return p; | |
+} | |
+ | |
+char * | |
+strdup(char *s) | |
+{ | |
+ char *t; | |
+ | |
+ t = emalloc(strlen(s) + 1); | |
+ return strcpy(t, s); | |
+} | |
+ | |
+void | |
+setnames(File *f) | |
+{ | |
+ sprint(newfile, "%s%s", newroot, f->new); | |
+ if(f->old){ | |
+ if(f->old[0] == '/') | |
+ sprint(oldfile, "%s%s", oldroot, f->old); | |
+ else | |
+ strcpy(oldfile, f->old); | |
+ }else | |
+ sprint(oldfile, "%s%s", oldroot, f->new); | |
+ if(strlen(newfile) >= sizeof newfile | |
+ || strlen(oldfile) >= sizeof oldfile) | |
+ error("name overfile"); | |
+} | |
+ | |
+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 | |
+ */ | |
+void | |
+skipdir(void) | |
+{ | |
+ char *p, c; | |
+ int level; | |
+ | |
+ if(indent < 0 || b == nil) /* b is nil when copying adm/users */ | |
+ return; | |
+ level = indent; | |
+ for(;;){ | |
+ indent = 0; | |
+ p = Brdline(b, '\n'); | |
+ lineno++; | |
+ if(!p){ | |
+ indent = -1; | |
+ return; | |
+ } | |
+ while((c = *p++) != '\n') | |
+ if(c == ' ') | |
+ indent++; | |
+ else if(c == '\t') | |
+ indent += 8; | |
+ else | |
+ break; | |
+ if(indent <= level){ | |
+ Bseek(b, -Blinelen(b), 1); | |
+ lineno--; | |
+ return; | |
+ } | |
+ } | |
+} | |
+ | |
+File* | |
+getfile(File *old) | |
+{ | |
+ File *f; | |
+ char *elem; | |
+ char *p; | |
+ int c; | |
+ | |
+ if(indent < 0) | |
+ return 0; | |
+loop: | |
+ indent = 0; | |
+ p = Brdline(b, '\n'); | |
+ lineno++; | |
+ if(!p){ | |
+ indent = -1; | |
+ return 0; | |
+ } | |
+ while((c = *p++) != '\n') | |
+ if(c == ' ') | |
+ indent++; | |
+ else if(c == '\t') | |
+ indent += 8; | |
+ else | |
+ break; | |
+ if(c == '\n' || c == '#') | |
+ goto loop; | |
+ p--; | |
+ f = emalloc(sizeof *f); | |
+ p = getname(p, &elem); | |
+ if(debug) | |
+ fprint(2, "getfile: %q root %q\n", elem, old->new); | |
+ f->new = mkpath(old->new, elem); | |
+ f->elem = utfrrune(f->new, L'/') + 1; | |
+ p = getmode(p, &f->mode); | |
+ p = getname(p, &f->uid); | |
+ if(!*f->uid) | |
+ f->uid = "-"; | |
+ p = getname(p, &f->gid); | |
+ if(!*f->gid) | |
+ f->gid = "-"; | |
+ f->old = getpath(p); | |
+ if(f->old && strcmp(f->old, "-") == 0){ | |
+ free(f->old); | |
+ f->old = 0; | |
+ } | |
+ setnames(f); | |
+ | |
+ if(debug) | |
+ printfile(f); | |
+ | |
+ return f; | |
+} | |
+ | |
+char* | |
+getpath(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(n + 1); | |
+ memcpy(new, p, n); | |
+ new[n] = 0; | |
+ return new; | |
+} | |
+ | |
+char* | |
+getname(char *p, char **buf) | |
+{ | |
+ char *s, *start; | |
+ int c; | |
+ | |
+ while((c = *p) == ' ' || c == '\t') | |
+ p++; | |
+ | |
+ start = p; | |
+ while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0') | |
+ p++; | |
+ | |
+ *buf = malloc(p+1-start); | |
+ if(*buf == nil) | |
+ return nil; | |
+ memmove(*buf, start, p-start); | |
+ (*buf)[p-start] = '\0'; | |
+ | |
+ if(**buf == '$'){ | |
+ s = getenv(*buf+1); | |
+ if(s == 0){ | |
+ warn("can't read environment variable %q", *buf+1); | |
+ skipdir(); | |
+ free(*buf); | |
+ return nil; | |
+ } | |
+ free(*buf); | |
+ *buf = s; | |
+ } | |
+ return p; | |
+} | |
+ | |
+char* | |
+getmode(char *p, ulong *xmode) | |
+{ | |
+ char *buf, *s; | |
+ ulong m; | |
+ | |
+ *xmode = ~0; | |
+ p = getname(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("bad mode specification %q", buf); | |
+ free(buf); | |
+ return p; | |
+ } | |
+ *xmode = m | strtoul(s, 0, 8); | |
+ free(buf); | |
+ return p; | |
+} | |
+ | |
+void | |
+setusers(void) | |
+{ | |
+ File file; | |
+ int m; | |
+ | |
+ if(fskind != Kfs) | |
+ return; | |
+ m = modes; | |
+ modes = 1; | |
+ file.uid = "adm"; | |
+ file.gid = "adm"; | |
+ file.mode = DMDIR|0775; | |
+ file.new = "/adm"; | |
+ file.elem = "adm"; | |
+ file.old = 0; | |
+ setnames(&file); | |
+ strcpy(oldfile, file.new); /* Don't use root for /adm */ | |
+ mkfile(&file); | |
+ file.new = "/adm/users"; | |
+ file.old = users; | |
+ file.elem = "users"; | |
+ file.mode = 0664; | |
+ setnames(&file); | |
+ if (file.old) | |
+ strcpy(oldfile, file.old); /* Don't use root for /adm/u… | |
+ mkfile(&file); | |
+ kfscmd("user"); | |
+ mkfile(&file); | |
+ file.mode = DMDIR|0775; | |
+ file.new = "/adm"; | |
+ file.old = "/adm"; | |
+ file.elem = "adm"; | |
+ setnames(&file); | |
+ strcpy(oldfile, file.old); /* Don't use root for /adm */ | |
+ mkfile(&file); | |
+ modes = m; | |
+} | |
+ | |
+void | |
+mountkfs(char *name) | |
+{ | |
+ if(fskind != Kfs) | |
+ return; | |
+ sysfatal("no kfs: use -a or -d"); | |
+} | |
+ | |
+void | |
+kfscmd(char *cmd) | |
+{ | |
+ char buf[4*1024]; | |
+ int n; | |
+ | |
+ if(fskind != Kfs) | |
+ return; | |
+ if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){ | |
+ fprint(2, "%q: error writing %q: %r", prog, cmd); | |
+ return; | |
+ } | |
+ for(;;){ | |
+ n = read(sfd, buf, sizeof buf - 1); | |
+ if(n <= 0) | |
+ return; | |
+ buf[n] = '\0'; | |
+ if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0) | |
+ return; | |
+ if(strcmp(buf, "unknown command") == 0){ | |
+ fprint(2, "%q: command %q not recognized\n", prog, cmd… | |
+ return; | |
+ } | |
+ } | |
+} | |
+ | |
+void * | |
+emalloc(ulong n) | |
+{ | |
+ void *p; | |
+ | |
+ if((p = malloc(n)) == 0) | |
+ error("out of memory"); | |
+ return p; | |
+} | |
+ | |
+void | |
+error(char *fmt, ...) | |
+{ | |
+ char buf[1024]; | |
+ va_list arg; | |
+ | |
+ sprint(buf, "%q: %q:%d: ", prog, proto, lineno); | |
+ va_start(arg, fmt); | |
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); | |
+ va_end(arg); | |
+ fprint(2, "%s\n", buf); | |
+ kfscmd("disallow"); | |
+ kfscmd("sync"); | |
+ exits(0); | |
+} | |
+ | |
+void | |
+warn(char *fmt, ...) | |
+{ | |
+ char buf[1024]; | |
+ va_list arg; | |
+ | |
+ sprint(buf, "%q: %q:%d: ", prog, proto, lineno); | |
+ va_start(arg, fmt); | |
+ vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); | |
+ va_end(arg); | |
+ fprint(2, "%s\n", buf); | |
+} | |
+ | |
+void | |
+printfile(File *f) | |
+{ | |
+ if(f->old) | |
+ fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f-… | |
+ else | |
+ fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode); | |
+} | |
+ | |
+void | |
+usage(void) | |
+{ | |
+ fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u user… | |
+ exits("usage"); | |
+} |