mkext.c 5.6 KB

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