unvac.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "stdinc.h"
  10. #include <fcall.h> /* dirmodefmt */
  11. #include "vac.h"
  12. VacFs *fs;
  13. int tostdout;
  14. int diff;
  15. int nwant;
  16. char **want;
  17. int *found;
  18. int chatty;
  19. VtConn *conn;
  20. int errors;
  21. int settimes;
  22. int table;
  23. int mtimefmt(Fmt*);
  24. void unvac(VacFile*, char*, VacDir*);
  25. void
  26. usage(void)
  27. {
  28. fprint(2, "usage: unvac [-TVcdtv] [-h host] file.vac [file ...]\n");
  29. threadexitsall("usage");
  30. }
  31. struct
  32. {
  33. int64_t data;
  34. int64_t skipdata;
  35. } stats;
  36. void
  37. threadmain(int argc, char *argv[])
  38. {
  39. int i, printstats;
  40. char *host;
  41. VacFile *f;
  42. fmtinstall('H', encodefmt);
  43. fmtinstall('V', vtscorefmt);
  44. fmtinstall('F', vtfcallfmt);
  45. fmtinstall('t', mtimefmt);
  46. fmtinstall('M', dirmodefmt);
  47. host = nil;
  48. printstats = 0;
  49. ARGBEGIN{
  50. case 'T':
  51. settimes = 1;
  52. break;
  53. case 'V':
  54. chattyventi = 1;
  55. break;
  56. case 'c':
  57. tostdout++;
  58. break;
  59. case 'd':
  60. diff++;
  61. break;
  62. case 'h':
  63. host = EARGF(usage());
  64. break;
  65. case 's':
  66. printstats++;
  67. break;
  68. case 't':
  69. table++;
  70. break;
  71. case 'v':
  72. chatty++;
  73. break;
  74. default:
  75. usage();
  76. }ARGEND
  77. if(argc < 1)
  78. usage();
  79. if(tostdout && diff){
  80. fprint(2, "cannot use -c with -d\n");
  81. usage();
  82. }
  83. conn = vtdial(host);
  84. if(conn == nil)
  85. sysfatal("could not connect to server: %r");
  86. if(vtconnect(conn) < 0)
  87. sysfatal("vtconnect: %r");
  88. fs = vacfsopen(conn, argv[0], VtOREAD, 128);
  89. if(fs == nil)
  90. sysfatal("vacfsopen: %r");
  91. nwant = argc-1;
  92. want = argv+1;
  93. found = vtmallocz(nwant*sizeof found[0]);
  94. if((f = vacfsgetroot(fs)) == nil)
  95. sysfatal("vacfsgetroot: %r");
  96. unvac(f, nil, nil);
  97. for(i=0; i<nwant; i++){
  98. if(want[i] && !found[i]){
  99. fprint(2, "warning: didn't find %s\n", want[i]);
  100. errors++;
  101. }
  102. }
  103. if(errors)
  104. threadexitsall("errors");
  105. if(printstats)
  106. fprint(2, "%lld bytes read, %lld bytes skipped\n",
  107. stats.data, stats.skipdata);
  108. threadexitsall(0);
  109. }
  110. int
  111. writen(int fd, char *buf, int n)
  112. {
  113. int m;
  114. int oldn;
  115. oldn = n;
  116. while(n > 0){
  117. m = write(fd, buf, n);
  118. if(m <= 0)
  119. return -1;
  120. buf += m;
  121. n -= m;
  122. }
  123. return oldn;
  124. }
  125. int
  126. wantfile(char *name)
  127. {
  128. int i, namelen, n;
  129. if(nwant == 0)
  130. return 1;
  131. namelen = strlen(name);
  132. for(i=0; i<nwant; i++){
  133. if(want[i] == nil)
  134. continue;
  135. n = strlen(want[i]);
  136. if(n < namelen && name[n] == '/' && memcmp(name, want[i], n) == 0)
  137. return 1;
  138. if(namelen < n && want[i][namelen] == '/' && memcmp(want[i], name, namelen) == 0)
  139. return 1;
  140. if(n == namelen && memcmp(name, want[i], n) == 0){
  141. found[i] = 1;
  142. return 1;
  143. }
  144. }
  145. return 0;
  146. }
  147. void
  148. unvac(VacFile *f, char *name, VacDir *vdir)
  149. {
  150. static char buf[65536];
  151. int fd, n, m, bsize;
  152. uint32_t mode, mode9;
  153. char *newname;
  154. char *what;
  155. int64_t off;
  156. Dir d, *dp;
  157. VacDirEnum *vde;
  158. VacDir newvdir;
  159. VacFile *newf;
  160. if(vdir)
  161. mode = vdir->mode;
  162. else
  163. mode = vacfilegetmode(f);
  164. if(vdir){
  165. if(table){
  166. if(chatty){
  167. mode9 = vdir->mode&0777;
  168. if(mode&ModeDir)
  169. mode9 |= DMDIR;
  170. if(mode&ModeAppend)
  171. mode9 |= DMAPPEND;
  172. if(mode&ModeExclusive)
  173. mode9 |= DMEXCL;
  174. print("%M %-10s %-10s %11lld %t %s\n",
  175. mode9, vdir->uid, vdir->gid, vdir->size,
  176. vdir->mtime, name);
  177. }else
  178. print("%s%s\n", name, (mode&ModeDir) ? "/" : "");
  179. }
  180. else if(chatty)
  181. fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : "");
  182. }
  183. if(mode&(ModeDevice|ModeLink|ModeNamedPipe|ModeExclusive)){
  184. if(table)
  185. return;
  186. if(mode&ModeDevice)
  187. what = "device";
  188. else if(mode&ModeLink)
  189. what = "link";
  190. else if(mode&ModeNamedPipe)
  191. what = "named pipe";
  192. else if(mode&ModeExclusive)
  193. what = "lock";
  194. else
  195. what = "unknown type of file";
  196. fprint(2, "warning: ignoring %s %s\n", what, name);
  197. return;
  198. }
  199. if(mode&ModeDir){
  200. if((vde = vdeopen(f)) == nil){
  201. fprint(2, "vdeopen %s: %r", name);
  202. errors++;
  203. return;
  204. }
  205. if(!table && !tostdout && vdir){
  206. // create directory
  207. if((dp = dirstat(name)) == nil){
  208. if((fd = create(name, OREAD, DMDIR|(mode&0777))) < 0){
  209. fprint(2, "mkdir %s: %r\n", name);
  210. vdeclose(vde);
  211. }
  212. close(fd);
  213. }else{
  214. if(!(dp->mode&DMDIR)){
  215. fprint(2, "%s already exists and is not a directory\n", name);
  216. errors++;
  217. free(dp);
  218. vdeclose(vde);
  219. return;
  220. }
  221. free(dp);
  222. }
  223. }
  224. while(vderead(vde, &newvdir) > 0){
  225. if(name == nil)
  226. newname = newvdir.elem;
  227. else
  228. newname = smprint("%s/%s", name, newvdir.elem);
  229. if(wantfile(newname)){
  230. if((newf = vacfilewalk(f, newvdir.elem)) == nil){
  231. fprint(2, "walk %s: %r\n", name);
  232. errors++;
  233. }else if(newf == f){
  234. fprint(2, "walk loop: %s\n", newname);
  235. vacfiledecref(newf);
  236. }else{
  237. unvac(newf, newname, &newvdir);
  238. vacfiledecref(newf);
  239. }
  240. }
  241. if(newname != newvdir.elem)
  242. free(newname);
  243. vdcleanup(&newvdir);
  244. }
  245. vdeclose(vde);
  246. }else{
  247. if(!table){
  248. off = 0;
  249. if(tostdout)
  250. fd = dup(1, -1);
  251. else if(diff && (fd = open(name, ORDWR)) >= 0){
  252. bsize = vacfiledsize(f);
  253. while((n = readn(fd, buf, bsize)) > 0){
  254. if(sha1matches(f, off/bsize, (uint8_t*)buf, n)){
  255. off += n;
  256. stats.skipdata += n;
  257. continue;
  258. }
  259. seek(fd, off, 0);
  260. if((m = vacfileread(f, buf, n, off)) < 0)
  261. break;
  262. if(writen(fd, buf, m) != m){
  263. fprint(2, "write %s: %r\n", name);
  264. goto Err;
  265. }
  266. off += m;
  267. stats.data += m;
  268. if(m < n){
  269. nulldir(&d);
  270. d.length = off;
  271. if(dirfwstat(fd, &d) < 0){
  272. fprint(2, "dirfwstat %s: %r\n", name);
  273. goto Err;
  274. }
  275. break;
  276. }
  277. }
  278. }
  279. else if((fd = create(name, OWRITE, mode&0777)) < 0){
  280. fprint(2, "create %s: %r\n", name);
  281. errors++;
  282. return;
  283. }
  284. while((n = vacfileread(f, buf, sizeof buf, off)) > 0){
  285. if(writen(fd, buf, n) != n){
  286. fprint(2, "write %s: %r\n", name);
  287. Err:
  288. errors++;
  289. close(fd);
  290. remove(name);
  291. return;
  292. }
  293. off += n;
  294. stats.data += n;
  295. }
  296. close(fd);
  297. }
  298. }
  299. if(vdir && settimes && !tostdout){
  300. nulldir(&d);
  301. d.mtime = vdir->mtime;
  302. if(dirwstat(name, &d) < 0)
  303. fprint(2, "warning: setting mtime on %s: %r", name);
  304. }
  305. }
  306. int
  307. mtimefmt(Fmt *f)
  308. {
  309. Tm *tm;
  310. tm = localtime(va_arg(f->args, uint32_t));
  311. fmtprint(f, "%04d-%02d-%02d %02d:%02d",
  312. tm->year+1900, tm->mon+1, tm->mday,
  313. tm->hour, tm->min);
  314. return 0;
  315. }