unvac.c 6.1 KB

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