proto.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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 <u.h>
  10. #include <libc.h>
  11. #include <bio.h>
  12. #include <auth.h>
  13. #include <fcall.h>
  14. #include <disk.h>
  15. enum {
  16. LEN = 8*1024,
  17. HUNKS = 128,
  18. };
  19. typedef struct File File;
  20. struct File{
  21. char *new;
  22. char *elem;
  23. char *old;
  24. char *uid;
  25. char *gid;
  26. uint32_t mode;
  27. };
  28. typedef void Mkfserr(char*, void*);
  29. typedef void Mkfsenum(char*, char*, Dir*, void*);
  30. typedef struct Name Name;
  31. struct Name {
  32. int n;
  33. char *s;
  34. };
  35. typedef struct Mkaux Mkaux;
  36. struct Mkaux {
  37. Mkfserr *warn;
  38. Mkfsenum *mkenum;
  39. char *root;
  40. char *proto;
  41. jmp_buf jmp;
  42. Biobuf *b;
  43. Name oldfile;
  44. Name fullname;
  45. int lineno;
  46. int indent;
  47. void *a;
  48. };
  49. static void domkfs(Mkaux *mkaux, File *me, int level);
  50. static int copyfile(Mkaux*, File*, Dir*, int);
  51. static void freefile(File*);
  52. static File* getfile(Mkaux*, File*);
  53. static char* getmode(Mkaux*, char*, uint32_t*);
  54. static char* getname(Mkaux*, char*, char**);
  55. static char* getpath(Mkaux*, char*);
  56. static int mkfile(Mkaux*, File*);
  57. static char* mkpath(Mkaux*, char*, char*);
  58. static void mktree(Mkaux*, File*, int);
  59. static void setnames(Mkaux*, File*);
  60. static void skipdir(Mkaux*);
  61. static void warn(Mkaux*, char *, ...);
  62. //static void
  63. //mprint(char *new, char *old, Dir *d, void*)
  64. //{
  65. // print("%s %s %D\n", new, old, d);
  66. //}
  67. int
  68. rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr,
  69. void *a)
  70. {
  71. Mkaux mx, *m;
  72. File file;
  73. int rv;
  74. m = &mx;
  75. memset(&mx, 0, sizeof mx);
  76. if(root == nil)
  77. root = "/";
  78. m->root = root;
  79. m->warn = mkerr;
  80. m->mkenum = mkenum;
  81. m->a = a;
  82. m->proto = proto;
  83. m->lineno = 0;
  84. m->indent = 0;
  85. if((m->b = Bopen(proto, OREAD)) == nil) {
  86. werrstr("open '%s': %r", proto);
  87. return -1;
  88. }
  89. memset(&file, 0, sizeof file);
  90. file.new = "";
  91. file.old = nil;
  92. rv = 0;
  93. if(setjmp(m->jmp) == 0)
  94. domkfs(m, &file, -1);
  95. else
  96. rv = -1;
  97. free(m->oldfile.s);
  98. free(m->fullname.s);
  99. return rv;
  100. }
  101. static void*
  102. emalloc(Mkaux *mkaux, uint32_t n)
  103. {
  104. void *v;
  105. v = malloc(n);
  106. if(v == nil)
  107. longjmp(mkaux->jmp, 1); /* memory leak */
  108. memset(v, 0, n);
  109. return v;
  110. }
  111. static char*
  112. estrdup(Mkaux *mkaux, char *s)
  113. {
  114. s = strdup(s);
  115. if(s == nil)
  116. longjmp(mkaux->jmp, 1); /* memory leak */
  117. return s;
  118. }
  119. static void
  120. domkfs(Mkaux *mkaux, File *me, int level)
  121. {
  122. File *child;
  123. int rec;
  124. child = getfile(mkaux, me);
  125. if(!child)
  126. return;
  127. if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){
  128. rec = child->elem[0] == '+';
  129. free(child->new);
  130. child->new = estrdup(mkaux, me->new);
  131. setnames(mkaux, child);
  132. mktree(mkaux, child, rec);
  133. freefile(child);
  134. child = getfile(mkaux, me);
  135. }
  136. while(child && mkaux->indent > level){
  137. if(mkfile(mkaux, child))
  138. domkfs(mkaux, child, mkaux->indent);
  139. freefile(child);
  140. child = getfile(mkaux, me);
  141. }
  142. if(child){
  143. freefile(child);
  144. Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
  145. mkaux->lineno--;
  146. }
  147. }
  148. static void
  149. mktree(Mkaux *mkaux, File *me, int rec)
  150. {
  151. File child;
  152. Dir *d;
  153. int i, n, fd;
  154. fd = open(mkaux->oldfile.s, OREAD);
  155. if(fd < 0){
  156. warn(mkaux, "can't open %s: %r", mkaux->oldfile.s);
  157. return;
  158. }
  159. child = *me;
  160. while((n = dirread(fd, &d)) > 0){
  161. for(i = 0; i < n; i++){
  162. child.new = mkpath(mkaux, me->new, d[i].name);
  163. if(me->old)
  164. child.old = mkpath(mkaux, me->old, d[i].name);
  165. child.elem = d[i].name;
  166. setnames(mkaux, &child);
  167. if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec)
  168. mktree(mkaux, &child, rec);
  169. free(child.new);
  170. if(child.old)
  171. free(child.old);
  172. }
  173. }
  174. close(fd);
  175. }
  176. static int
  177. mkfile(Mkaux *mkaux, File *f)
  178. {
  179. Dir *d;
  180. if((d = dirstat(mkaux->oldfile.s)) == nil){
  181. warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s);
  182. skipdir(mkaux);
  183. return 0;
  184. }
  185. return copyfile(mkaux, f, d, 0);
  186. }
  187. enum {
  188. SLOP = 30
  189. };
  190. static void
  191. setname(Mkaux *mkaux, Name *name, char *s1, char *s2)
  192. {
  193. int l;
  194. l = strlen(s1)+strlen(s2)+1;
  195. if(name->n < l+SLOP/2) {
  196. free(name->s);
  197. name->s = emalloc(mkaux, l+SLOP);
  198. name->n = l+SLOP;
  199. }
  200. snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2);
  201. }
  202. static int
  203. copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly)
  204. {
  205. Dir *nd;
  206. uint32_t xmode;
  207. char *p;
  208. setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new);
  209. /*
  210. * Extra stat here is inefficient but accounts for binds.
  211. */
  212. if((nd = dirstat(mkaux->fullname.s)) != nil)
  213. d = nd;
  214. d->name = f->elem;
  215. if(d->type != 'M'){
  216. d->uid = "sys";
  217. d->gid = "sys";
  218. xmode = (d->mode >> 6) & 7;
  219. d->mode |= xmode | (xmode << 3);
  220. }
  221. if(strcmp(f->uid, "-") != 0)
  222. d->uid = f->uid;
  223. if(strcmp(f->gid, "-") != 0)
  224. d->gid = f->gid;
  225. if(f->mode != ~0){
  226. if(permonly)
  227. d->mode = (d->mode & ~0666) | (f->mode & 0666);
  228. else if((d->mode&DMDIR) != (f->mode&DMDIR))
  229. warn(mkaux, "inconsistent mode for %s", f->new);
  230. else
  231. d->mode = f->mode;
  232. }
  233. if((p = strrchr(f->new, '/')) != nil)
  234. d->name = p+1;
  235. else
  236. d->name = f->new;
  237. mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a);
  238. xmode = d->mode;
  239. free(nd);
  240. return (xmode&DMDIR) != 0;
  241. }
  242. static char *
  243. mkpath(Mkaux *mkaux, char *prefix, char *elem)
  244. {
  245. char *p;
  246. int n;
  247. n = strlen(prefix) + strlen(elem) + 2;
  248. p = emalloc(mkaux, n);
  249. strcpy(p, prefix);
  250. strcat(p, "/");
  251. strcat(p, elem);
  252. return p;
  253. }
  254. static void
  255. setnames(Mkaux *mkaux, File *f)
  256. {
  257. if(f->old){
  258. if(f->old[0] == '/')
  259. setname(mkaux, &mkaux->oldfile, f->old, "");
  260. else
  261. setname(mkaux, &mkaux->oldfile, mkaux->root, f->old);
  262. } else
  263. setname(mkaux, &mkaux->oldfile, mkaux->root, f->new);
  264. }
  265. static void
  266. freefile(File *f)
  267. {
  268. if(f->old)
  269. free(f->old);
  270. if(f->new)
  271. free(f->new);
  272. free(f);
  273. }
  274. /*
  275. * skip all files in the proto that
  276. * could be in the current dir
  277. */
  278. static void
  279. skipdir(Mkaux *mkaux)
  280. {
  281. char *p, c;
  282. int level;
  283. if(mkaux->indent < 0)
  284. return;
  285. level = mkaux->indent;
  286. for(;;){
  287. mkaux->indent = 0;
  288. p = Brdline(mkaux->b, '\n');
  289. mkaux->lineno++;
  290. if(!p){
  291. mkaux->indent = -1;
  292. return;
  293. }
  294. while((c = *p++) != '\n')
  295. if(c == ' ')
  296. mkaux->indent++;
  297. else if(c == '\t')
  298. mkaux->indent += 8;
  299. else
  300. break;
  301. if(mkaux->indent <= level){
  302. Bseek(mkaux->b, -Blinelen(mkaux->b), 1);
  303. mkaux->lineno--;
  304. return;
  305. }
  306. }
  307. }
  308. static File*
  309. getfile(Mkaux *mkaux, File *old)
  310. {
  311. File *f;
  312. char *elem;
  313. char *p;
  314. int c;
  315. if(mkaux->indent < 0)
  316. return 0;
  317. loop:
  318. mkaux->indent = 0;
  319. p = Brdline(mkaux->b, '\n');
  320. mkaux->lineno++;
  321. if(!p){
  322. mkaux->indent = -1;
  323. return 0;
  324. }
  325. while((c = *p++) != '\n')
  326. if(c == ' ')
  327. mkaux->indent++;
  328. else if(c == '\t')
  329. mkaux->indent += 8;
  330. else
  331. break;
  332. if(c == '\n' || c == '#')
  333. goto loop;
  334. p--;
  335. f = emalloc(mkaux, sizeof *f);
  336. p = getname(mkaux, p, &elem);
  337. if(p == nil)
  338. return nil;
  339. f->new = mkpath(mkaux, old->new, elem);
  340. free(elem);
  341. f->elem = utfrrune(f->new, L'/') + 1;
  342. p = getmode(mkaux, p, &f->mode);
  343. p = getname(mkaux, p, &f->uid); /* LEAK */
  344. if(p == nil)
  345. return nil;
  346. if(!*f->uid)
  347. strcpy(f->uid, "-");
  348. p = getname(mkaux, p, &f->gid); /* LEAK */
  349. if(p == nil)
  350. return nil;
  351. if(!*f->gid)
  352. strcpy(f->gid, "-");
  353. f->old = getpath(mkaux, p);
  354. if(f->old && strcmp(f->old, "-") == 0){
  355. free(f->old);
  356. f->old = 0;
  357. }
  358. setnames(mkaux, f);
  359. return f;
  360. }
  361. static char*
  362. getpath(Mkaux *mkaux, char *p)
  363. {
  364. char *q, *new;
  365. int c, n;
  366. while((c = *p) == ' ' || c == '\t')
  367. p++;
  368. q = p;
  369. while((c = *q) != '\n' && c != ' ' && c != '\t')
  370. q++;
  371. if(q == p)
  372. return 0;
  373. n = q - p;
  374. new = emalloc(mkaux, n + 1);
  375. memcpy(new, p, n);
  376. new[n] = 0;
  377. return new;
  378. }
  379. static char*
  380. getname(Mkaux *mkaux, char *p, char **buf)
  381. {
  382. char *s, *start;
  383. int c;
  384. while((c = *p) == ' ' || c == '\t')
  385. p++;
  386. start = p;
  387. while((c = *p) != '\n' && c != ' ' && c != '\t')
  388. p++;
  389. *buf = malloc(p+2-start); /* +2: need at least 2 bytes; might strcpy "-" into buf */
  390. if(*buf == nil)
  391. return nil;
  392. memmove(*buf, start, p-start);
  393. (*buf)[p-start] = '\0';
  394. if(**buf == '$'){
  395. s = getenv(*buf+1);
  396. if(s == 0){
  397. warn(mkaux, "can't read environment variable %s", *buf+1);
  398. skipdir(mkaux);
  399. free(*buf);
  400. return nil;
  401. }
  402. free(*buf);
  403. *buf = s;
  404. }
  405. return p;
  406. }
  407. static char*
  408. getmode(Mkaux *mkaux, char *p, uint32_t *xmode)
  409. {
  410. char *buf, *s;
  411. uint32_t m;
  412. *xmode = ~0;
  413. p = getname(mkaux, p, &buf);
  414. if(p == nil)
  415. return nil;
  416. s = buf;
  417. if(!*s || strcmp(s, "-") == 0)
  418. return p;
  419. m = 0;
  420. if(*s == 'd'){
  421. m |= DMDIR;
  422. s++;
  423. }
  424. if(*s == 'a'){
  425. m |= DMAPPEND;
  426. s++;
  427. }
  428. if(*s == 'l'){
  429. m |= DMEXCL;
  430. s++;
  431. }
  432. if(s[0] < '0' || s[0] > '7'
  433. || s[1] < '0' || s[1] > '7'
  434. || s[2] < '0' || s[2] > '7'
  435. || s[3]){
  436. warn(mkaux, "bad mode specification %s", buf);
  437. free(buf);
  438. return p;
  439. }
  440. *xmode = m | strtoul(s, 0, 8);
  441. free(buf);
  442. return p;
  443. }
  444. static void
  445. warn(Mkaux *mkaux, char *fmt, ...)
  446. {
  447. char buf[256];
  448. va_list va;
  449. va_start(va, fmt);
  450. vseprint(buf, buf+sizeof(buf), fmt, va);
  451. va_end(va);
  452. if(mkaux->warn)
  453. mkaux->warn(buf, mkaux->a);
  454. else
  455. fprint(2, "warning: %s\n", buf);
  456. }