blob: d576d24fe0867036d8a128abd589f5ea8948c626 [file] [log] [blame]
#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");
}