ref: a79465e118a0469bbda928f86b9eef6b42d18593
dir: /sys/src/cmd/disk/mkext.c/
#include <u.h> #include <libc.h> #include <bio.h> enum{ LEN = 8*1024, NFLDS = 6, /* filename, modes, uid, gid, mtime, bytes */ }; 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 Biobufhdr 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 = EARGF(usage()); 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, mode, olderr); 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->mtime); 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 bytes) { 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"); }