mkext.c 5.7 KB

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