| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| |
| #define mkdir plan9mkdir |
| |
| 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 |
| |
| 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, 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"); |
| } |