1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 5 enum{ 6 LEN = 8*1024, 7 NFLDS = 6, /* filename, modes, uid, gid, mtime, bytes */ 8 }; 9 10 int selected(char*, int, char*[]); 11 void mkdirs(char*, char*); 12 void mkdir(char*, ulong, ulong, char*, char*); 13 void extract(char*, ulong, ulong, char*, char*, uvlong); 14 void seekpast(uvlong); 15 void error(char*, ...); 16 void warn(char*, ...); 17 void usage(void); 18 #pragma varargck argpos warn 1 19 #pragma varargck argpos error 1 20 21 Biobufhdr bin; 22 uchar binbuf[2*LEN]; 23 char linebuf[LEN]; 24 int uflag; 25 int hflag; 26 int vflag; 27 int Tflag; 28 29 void 30 main(int argc, char **argv) 31 { 32 Biobuf bout; 33 char *fields[NFLDS], name[2*LEN], *p, *namep; 34 char *uid, *gid; 35 ulong mode, mtime; 36 uvlong bytes; 37 38 quotefmtinstall(); 39 namep = name; 40 ARGBEGIN{ 41 case 'd': 42 p = ARGF(); 43 if(strlen(p) >= LEN) 44 error("destination fs name too long\n"); 45 strcpy(name, p); 46 namep = name + strlen(name); 47 break; 48 case 'h': 49 hflag = 1; 50 Binit(&bout, 1, OWRITE); 51 break; 52 case 'u': 53 uflag = 1; 54 Tflag = 1; 55 break; 56 case 'T': 57 Tflag = 1; 58 break; 59 case 'v': 60 vflag = 1; 61 break; 62 default: 63 usage(); 64 }ARGEND 65 66 Binits(&bin, 0, OREAD, binbuf, sizeof binbuf); 67 while(p = Brdline(&bin, '\n')){ 68 p[Blinelen(&bin)-1] = '\0'; 69 strcpy(linebuf, p); 70 p = linebuf; 71 if(strcmp(p, "end of archive") == 0){ 72 Bterm(&bout); 73 fprint(2, "done\n"); 74 exits(0); 75 } 76 if (gettokens(p, fields, NFLDS, " \t") != NFLDS){ 77 warn("too few fields in file header"); 78 continue; 79 } 80 p = unquotestrdup(fields[0]); 81 strcpy(namep, p); 82 free(p); 83 mode = strtoul(fields[1], 0, 8); 84 uid = fields[2]; 85 gid = fields[3]; 86 mtime = strtoul(fields[4], 0, 10); 87 bytes = strtoull(fields[5], 0, 10); 88 if(argc){ 89 if(!selected(namep, argc, argv)){ 90 if(bytes) 91 seekpast(bytes); 92 continue; 93 } 94 mkdirs(name, namep); 95 } 96 if(hflag){ 97 Bprint(&bout, "%q %luo %q %q %lud %llud\n", 98 name, mode, uid, gid, mtime, bytes); 99 if(bytes) 100 seekpast(bytes); 101 continue; 102 } 103 if(mode & DMDIR) 104 mkdir(name, mode, mtime, uid, gid); 105 else 106 extract(name, mode, mtime, uid, gid, bytes); 107 } 108 fprint(2, "premature end of archive\n"); 109 exits("premature end of archive"); 110 } 111 112 int 113 fileprefix(char *prefix, char *s) 114 { 115 while(*prefix) 116 if(*prefix++ != *s++) 117 return 0; 118 if(*s && *s != '/') 119 return 0; 120 return 1; 121 } 122 123 int 124 selected(char *s, int argc, char *argv[]) 125 { 126 int i; 127 128 for(i=0; i<argc; i++) 129 if(fileprefix(argv[i], s)) 130 return 1; 131 return 0; 132 } 133 134 void 135 mkdirs(char *name, char *namep) 136 { 137 char buf[2*LEN], *p; 138 int fd; 139 140 strcpy(buf, name); 141 for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){ 142 if(p[1] == '\0') 143 return; 144 *p = 0; 145 fd = create(buf, OREAD, 0775|DMDIR); 146 close(fd); 147 *p = '/'; 148 } 149 } 150 151 void 152 mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid) 153 { 154 Dir *d, xd; 155 int fd; 156 char *p; 157 char olderr[256]; 158 159 fd = create(name, OREAD, mode); 160 if(fd < 0){ 161 rerrstr(olderr, sizeof(olderr)); 162 if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){ 163 free(d); 164 warn("can't make directory %q, mode %luo: %s", name, mode, olderr); 165 return; 166 } 167 free(d); 168 } 169 close(fd); 170 171 d = &xd; 172 nulldir(d); 173 p = utfrrune(name, L'/'); 174 if(p) 175 p++; 176 else 177 p = name; 178 d->name = p; 179 if(uflag){ 180 d->uid = uid; 181 d->gid = gid; 182 } 183 if(Tflag) 184 d->mtime = mtime; 185 d->mode = mode; 186 if(dirwstat(name, d) < 0) 187 warn("can't set modes for %q: %r", name); 188 189 if(uflag||Tflag){ 190 if((d = dirstat(name)) == nil){ 191 warn("can't reread modes for %q: %r", name); 192 return; 193 } 194 if(Tflag && d->mtime != mtime) 195 warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime); 196 if(uflag && strcmp(uid, d->uid)) 197 warn("%q: uid mismatch %q %q", name, uid, d->uid); 198 if(uflag && strcmp(gid, d->gid)) 199 warn("%q: gid mismatch %q %q", name, gid, d->gid); 200 } 201 } 202 203 void 204 extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong bytes) 205 { 206 Dir d, *nd; 207 Biobuf *b; 208 char buf[LEN]; 209 char *p; 210 ulong n; 211 uvlong tot; 212 213 if(vflag) 214 print("x %q %llud bytes\n", name, bytes); 215 216 b = Bopen(name, OWRITE); 217 if(!b){ 218 warn("can't make file %q: %r", name); 219 seekpast(bytes); 220 return; 221 } 222 for(tot = 0; tot < bytes; tot += n){ 223 n = sizeof buf; 224 if(tot + n > bytes) 225 n = bytes - tot; 226 n = Bread(&bin, buf, n); 227 if(n <= 0) 228 error("premature eof reading %q", name); 229 if(Bwrite(b, buf, n) != n) 230 warn("error writing %q: %r", name); 231 } 232 233 nulldir(&d); 234 p = utfrrune(name, '/'); 235 if(p) 236 p++; 237 else 238 p = name; 239 d.name = p; 240 if(uflag){ 241 d.uid = uid; 242 d.gid = gid; 243 } 244 if(Tflag) 245 d.mtime = mtime; 246 d.mode = mode; 247 Bflush(b); 248 if(dirfwstat(Bfildes(b), &d) < 0) 249 warn("can't set modes for %q: %r", name); 250 if(uflag||Tflag){ 251 if((nd = dirfstat(Bfildes(b))) == nil) 252 warn("can't reread modes for %q: %r", name); 253 else{ 254 if(Tflag && nd->mtime != mtime) 255 warn("%q: time mismatch %lud %lud\n", 256 name, mtime, nd->mtime); 257 if(uflag && strcmp(uid, nd->uid)) 258 warn("%q: uid mismatch %q %q", 259 name, uid, nd->uid); 260 if(uflag && strcmp(gid, nd->gid)) 261 warn("%q: gid mismatch %q %q", 262 name, gid, nd->gid); 263 free(nd); 264 } 265 } 266 Bterm(b); 267 } 268 269 void 270 seekpast(uvlong bytes) 271 { 272 char buf[LEN]; 273 long n; 274 uvlong tot; 275 276 for(tot = 0; tot < bytes; tot += n){ 277 n = sizeof buf; 278 if(tot + n > bytes) 279 n = bytes - tot; 280 n = Bread(&bin, buf, n); 281 if(n < 0) 282 error("premature eof"); 283 } 284 } 285 286 void 287 error(char *fmt, ...) 288 { 289 char buf[1024]; 290 va_list arg; 291 292 sprint(buf, "%q: ", argv0); 293 va_start(arg, fmt); 294 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); 295 va_end(arg); 296 fprint(2, "%s\n", buf); 297 exits(0); 298 } 299 300 void 301 warn(char *fmt, ...) 302 { 303 char buf[1024]; 304 va_list arg; 305 306 sprint(buf, "%q: ", argv0); 307 va_start(arg, fmt); 308 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); 309 va_end(arg); 310 fprint(2, "%s\n", buf); 311 } 312 313 void 314 usage(void) 315 { 316 fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n"); 317 exits("usage"); 318 } 319