unvac.c 6.5 KB

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