334 lines
6.5 KiB
C
334 lines
6.5 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
|
|
* See /doc/license/gpl-2.0.txt for details about the licensing.
|
|
*/
|
|
/* Portions of this file are Copyright (C) 9front's team.
|
|
* See /doc/license/9front-mit for details about the licensing.
|
|
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
|
|
*/
|
|
#include <u.h>
|
|
#include <lib9.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*, uint32_t, uint32_t, char*, char*);
|
|
void extract(char*, uint32_t, uint32_t, char*, char*, uint64_t);
|
|
void seekpast(uint64_t);
|
|
void error(char*, ...);
|
|
void warn(char*, ...);
|
|
void usage(void);
|
|
#pragma varargck argpos warn 1
|
|
#pragma varargck argpos error 1
|
|
|
|
Biobufhdr bin;
|
|
uint8_t 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;
|
|
uint32_t mode, mtime;
|
|
uint64_t bytes;
|
|
|
|
quotefmtinstall();
|
|
namep = name;
|
|
ARGBEGIN{
|
|
case 'd':
|
|
p = ARGF();
|
|
if(strlen(p) >= LEN)
|
|
error("destination fs name too int32_t\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 = ocreate(buf, OREAD, 0775|DMDIR);
|
|
sys_close(fd);
|
|
*p = '/';
|
|
}
|
|
}
|
|
|
|
void
|
|
mkdir(char *name, uint32_t mode, uint32_t mtime, char *uid, char *gid)
|
|
{
|
|
Dir *d, xd;
|
|
int fd;
|
|
char *p;
|
|
char olderr[256];
|
|
|
|
fd = ocreate(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);
|
|
}
|
|
sys_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, uint32_t mode, uint32_t mtime, char *uid, char *gid, uint64_t bytes)
|
|
{
|
|
Dir d, *nd;
|
|
Biobuf *b;
|
|
char buf[LEN];
|
|
char *p;
|
|
uint32_t n;
|
|
uint64_t 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(uint64_t bytes)
|
|
{
|
|
char buf[LEN];
|
|
int32_t n;
|
|
uint64_t 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");
|
|
}
|