/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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; voidmain(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");} intfileprefix(char *prefix, char *s){ while(*prefix) if(*prefix++ != *s++) return 0; if(*s && *s != '/') return 0; return 1;} intselected(char *s, int argc, char *argv[]){ int i; for(i=0; i<argc; i++) if(fileprefix(argv[i], s)) return 1; return 0;} voidmkdirs(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 = '/'; }} voidmkdir(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); }} voidextract(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);} voidseekpast(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"); }} voiderror(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);} voidwarn(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);} voidusage(void){ fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n"); exits("usage");}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#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 = 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");
}