edit.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. * disk partition editor
  3. */
  4. #include <u.h>
  5. #include <libc.h>
  6. #include <bio.h>
  7. #include <ctype.h>
  8. #include <disk.h>
  9. #include "edit.h"
  10. char*
  11. getline(Edit *edit)
  12. {
  13. static int inited;
  14. static Biobuf bin;
  15. char *p;
  16. int n;
  17. if(!inited){
  18. Binit(&bin, 0, OREAD);
  19. inited = 1;
  20. }
  21. p = Brdline(&bin, '\n');
  22. n = Blinelen(&bin);
  23. if(p == nil || n < 1){
  24. if(edit->changed)
  25. fprint(2, "?warning: changes not written\n");
  26. exits(0);
  27. }
  28. p[n - 1] = '\0';
  29. while(isspace(*p))
  30. p++;
  31. return p;
  32. }
  33. Part*
  34. findpart(Edit *edit, char *name)
  35. {
  36. int i;
  37. for(i=0; i<edit->npart; i++)
  38. if(strcmp(edit->part[i]->name, name) == 0)
  39. return edit->part[i];
  40. return nil;
  41. }
  42. static char*
  43. okname(Edit *edit, char *name)
  44. {
  45. int i;
  46. static char msg[100];
  47. if(name[0] == '\0')
  48. return "partition has no name";
  49. // if(strlen(name) >= NAMELEN)
  50. // return "name too long";
  51. //
  52. for(i=0; i<edit->npart; i++) {
  53. if(strcmp(name, edit->part[i]->name) == 0) {
  54. sprint(msg, "already have partition with name \"%s\"", name);
  55. return msg;
  56. }
  57. }
  58. return nil;
  59. }
  60. char*
  61. addpart(Edit *edit, Part *p)
  62. {
  63. int i;
  64. static char msg[100];
  65. char *err;
  66. if(err = okname(edit, p->name))
  67. return err;
  68. for(i=0; i<edit->npart; i++) {
  69. if(p->start < edit->part[i]->end && edit->part[i]->start < p->end) {
  70. sprint(msg, "\"%s\" %lld-%lld overlaps with \"%s\" %lld-%lld",
  71. p->name, p->start, p->end,
  72. edit->part[i]->name, edit->part[i]->start, edit->part[i]->end);
  73. // return msg;
  74. }
  75. }
  76. if(edit->npart >= nelem(edit->part))
  77. return "too many partitions";
  78. edit->part[i=edit->npart++] = p;
  79. for(; i > 0 && p->start < edit->part[i-1]->start; i--) {
  80. edit->part[i] = edit->part[i-1];
  81. edit->part[i-1] = p;
  82. }
  83. if(p->changed)
  84. edit->changed = 1;
  85. return nil;
  86. }
  87. char*
  88. delpart(Edit *edit, Part *p)
  89. {
  90. int i;
  91. for(i=0; i<edit->npart; i++)
  92. if(edit->part[i] == p)
  93. break;
  94. assert(i < edit->npart);
  95. edit->npart--;
  96. for(; i<edit->npart; i++)
  97. edit->part[i] = edit->part[i+1];
  98. edit->changed = 1;
  99. return nil;
  100. }
  101. static char*
  102. editdot(Edit *edit, int argc, char **argv)
  103. {
  104. char *err;
  105. vlong ndot;
  106. if(argc == 1) {
  107. print("\t. %lld\n", edit->dot);
  108. return nil;
  109. }
  110. if(argc > 2)
  111. return "args";
  112. if(err = parseexpr(argv[1], edit->dot, edit->end, edit->end, &ndot))
  113. return err;
  114. edit->dot = ndot;
  115. return nil;
  116. }
  117. static char*
  118. editadd(Edit *edit, int argc, char **argv)
  119. {
  120. char *name, *err, *q;
  121. static char msg[100];
  122. vlong start, end, maxend;
  123. int i;
  124. if(argc < 2)
  125. return "args";
  126. name = estrdup(argv[1]);
  127. if((err = okname(edit, name)) || (err = edit->okname(edit, name)))
  128. return err;
  129. if(argc >= 3)
  130. q = argv[2];
  131. else {
  132. fprint(2, "start %s: ", edit->unit);
  133. q = getline(edit);
  134. }
  135. if(err = parseexpr(q, edit->dot, edit->end, edit->end, &start))
  136. return err;
  137. if(start < 0 || start >= edit->end)
  138. return "start out of range";
  139. for(i=0; i < edit->npart; i++) {
  140. if(edit->part[i]->start <= start && start < edit->part[i]->end) {
  141. sprint(msg, "start %s in partition \"%s\"", edit->unit, edit->part[i]->name);
  142. return msg;
  143. }
  144. }
  145. maxend = edit->end;
  146. for(i=0; i < edit->npart; i++)
  147. if(start < edit->part[i]->start && edit->part[i]->start < maxend)
  148. maxend = edit->part[i]->start;
  149. if(argc >= 4)
  150. q = argv[3];
  151. else {
  152. fprint(2, "end [%lld..%lld] ", start, maxend);
  153. q = getline(edit);
  154. }
  155. if(err = parseexpr(q, edit->dot, maxend, edit->end, &end))
  156. return err;
  157. if(start == end)
  158. return "size zero partition";
  159. if(end <= start || end > maxend)
  160. return "end out of range";
  161. if(argc > 4)
  162. return "args";
  163. if(err = edit->add(edit, name, start, end))
  164. return err;
  165. edit->dot = end;
  166. return nil;
  167. }
  168. static char*
  169. editdel(Edit *edit, int argc, char **argv)
  170. {
  171. Part *p;
  172. if(argc != 2)
  173. return "args";
  174. if((p = findpart(edit, argv[1])) == nil)
  175. return "no such partition";
  176. return edit->del(edit, p);
  177. }
  178. static char *helptext =
  179. ". [newdot] - display or set value of dot\n"
  180. "a name [start [end]] - add partition\n"
  181. "d name - delete partition\n"
  182. "h - print help message\n"
  183. "p - print partition table\n"
  184. "P - print commands to update sd(3) device\n"
  185. "w - write partition table\n"
  186. "q - quit\n";
  187. static char*
  188. edithelp(Edit *edit, int, char**)
  189. {
  190. print("%s", helptext);
  191. if(edit->help)
  192. return edit->help(edit);
  193. return nil;
  194. }
  195. static char*
  196. editprint(Edit *edit, int argc, char**)
  197. {
  198. vlong lastend;
  199. int i;
  200. Part **part;
  201. if(argc != 1)
  202. return "args";
  203. lastend = 0;
  204. part = edit->part;
  205. for(i=0; i<edit->npart; i++) {
  206. if(lastend < part[i]->start)
  207. edit->sum(edit, nil, lastend, part[i]->start);
  208. edit->sum(edit, part[i], part[i]->start, part[i]->end);
  209. lastend = part[i]->end;
  210. }
  211. if(lastend < edit->end)
  212. edit->sum(edit, nil, lastend, edit->end);
  213. return nil;
  214. }
  215. char*
  216. editwrite(Edit *edit, int argc, char**)
  217. {
  218. int i;
  219. char *err;
  220. if(argc != 1)
  221. return "args";
  222. if(edit->disk->rdonly)
  223. return "read only";
  224. err = edit->write(edit);
  225. if(err)
  226. return err;
  227. for(i=0; i<edit->npart; i++)
  228. edit->part[i]->changed = 0;
  229. edit->changed = 0;
  230. return nil;
  231. }
  232. static char*
  233. editquit(Edit *edit, int argc, char**)
  234. {
  235. static int warned;
  236. if(argc != 1) {
  237. warned = 0;
  238. return "args";
  239. }
  240. if(edit->changed && (!edit->warned || edit->lastcmd != 'q')) {
  241. edit->warned = 1;
  242. return "changes unwritten";
  243. }
  244. exits(0);
  245. return nil; /* not reached */
  246. }
  247. char*
  248. editctlprint(Edit *edit, int argc, char **)
  249. {
  250. if(argc != 1)
  251. return "args";
  252. if(edit->printctl)
  253. edit->printctl(edit, 1);
  254. else
  255. ctldiff(edit, 1);
  256. return nil;
  257. }
  258. typedef struct Cmd Cmd;
  259. struct Cmd {
  260. char c;
  261. char *(*fn)(Edit*, int ,char**);
  262. };
  263. Cmd cmds[] = {
  264. '.', editdot,
  265. 'a', editadd,
  266. 'd', editdel,
  267. '?', edithelp,
  268. 'h', edithelp,
  269. 'P', editctlprint,
  270. 'p', editprint,
  271. 'w', editwrite,
  272. 'q', editquit,
  273. };
  274. void
  275. runcmd(Edit *edit, char *cmd)
  276. {
  277. char *f[10], *err;
  278. int i, nf;
  279. while(*cmd && isspace(*cmd))
  280. cmd++;
  281. nf = tokenize(cmd, f, nelem(f));
  282. if(nf >= 10) {
  283. fprint(2, "?\n");
  284. return;
  285. }
  286. if(nf < 1)
  287. return;
  288. if(strlen(f[0]) != 1) {
  289. fprint(2, "?\n");
  290. return;
  291. }
  292. err = nil;
  293. for(i=0; i<nelem(cmds); i++) {
  294. if(cmds[i].c == f[0][0]) {
  295. err = cmds[i].fn(edit, nf, f);
  296. break;
  297. }
  298. }
  299. if(i == nelem(cmds)){
  300. if(edit->ext)
  301. err = edit->ext(edit, nf, f);
  302. else
  303. err = "unknown command";
  304. }
  305. if(err)
  306. fprint(2, "?%s\n", err);
  307. edit->lastcmd = f[0][0];
  308. }
  309. static Part*
  310. ctlmkpart(char *name, vlong start, vlong end, int changed)
  311. {
  312. Part *p;
  313. p = emalloc(sizeof(*p));
  314. p->name = estrdup(name);
  315. p->ctlname = estrdup(name);
  316. p->start = start;
  317. p->end = end;
  318. p->changed = changed;
  319. return p;
  320. }
  321. static void
  322. rdctlpart(Edit *edit)
  323. {
  324. int i, nline, nf;
  325. char *line[128];
  326. char buf[4096];
  327. vlong a, b;
  328. char *f[5];
  329. Disk *disk;
  330. disk = edit->disk;
  331. edit->nctlpart = 0;
  332. seek(disk->ctlfd, 0, 0);
  333. if(readn(disk->ctlfd, buf, sizeof buf) <= 0) {
  334. return;
  335. }
  336. nline = getfields(buf, line, nelem(line), 1, "\n");
  337. for(i=0; i<nline; i++){
  338. if(strncmp(line[i], "part ", 5) != 0)
  339. continue;
  340. nf = getfields(line[i], f, nelem(f), 1, " \t\r");
  341. if(nf != 4 || strcmp(f[0], "part") != 0)
  342. break;
  343. a = strtoll(f[2], 0, 0);
  344. b = strtoll(f[3], 0, 0);
  345. if(a >= b)
  346. break;
  347. /* only gather partitions contained in the disk partition we are editing */
  348. if(a < disk->offset || disk->offset+disk->secs < b)
  349. continue;
  350. a -= disk->offset;
  351. b -= disk->offset;
  352. /* the partition we are editing does not count */
  353. if(strcmp(f[1], disk->part) == 0)
  354. continue;
  355. if(edit->nctlpart >= nelem(edit->ctlpart)) {
  356. fprint(2, "?too many partitions in ctl file\n");
  357. exits("ctlpart");
  358. }
  359. edit->ctlpart[edit->nctlpart++] = ctlmkpart(f[1], a, b, 0);
  360. }
  361. }
  362. static vlong
  363. ctlstart(Part *p)
  364. {
  365. if(p->ctlstart)
  366. return p->ctlstart;
  367. return p->start;
  368. }
  369. static vlong
  370. ctlend(Part *p)
  371. {
  372. if(p->ctlend)
  373. return p->ctlend;
  374. return p->end;
  375. }
  376. static int
  377. areequiv(Part *p, Part *q)
  378. {
  379. if(p->ctlname[0]=='\0' || q->ctlname[0]=='\0')
  380. return 0;
  381. return strcmp(p->ctlname, q->ctlname) == 0
  382. && ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q);
  383. }
  384. static void
  385. unchange(Edit *edit, Part *p)
  386. {
  387. int i;
  388. Part *q;
  389. for(i=0; i<edit->nctlpart; i++) {
  390. q = edit->ctlpart[i];
  391. if(p->start <= q->start && q->end <= p->end) {
  392. q->changed = 0;
  393. }
  394. }
  395. assert(p->changed == 0);
  396. }
  397. int
  398. ctldiff(Edit *edit, int ctlfd)
  399. {
  400. int i, j, waserr;
  401. Part *p;
  402. vlong offset;
  403. rdctlpart(edit);
  404. /* everything is bogus until we prove otherwise */
  405. for(i=0; i<edit->nctlpart; i++)
  406. edit->ctlpart[i]->changed = 1;
  407. /*
  408. * partitions with same info have not changed,
  409. * and neither have partitions inside them.
  410. */
  411. for(i=0; i<edit->nctlpart; i++)
  412. for(j=0; j<edit->npart; j++)
  413. if(areequiv(edit->ctlpart[i], edit->part[j])) {
  414. unchange(edit, edit->ctlpart[i]);
  415. break;
  416. }
  417. waserr = 0;
  418. /*
  419. * delete all the changed partitions except data (we'll add them back if necessary)
  420. */
  421. for(i=0; i<edit->nctlpart; i++) {
  422. p = edit->ctlpart[i];
  423. if(p->changed)
  424. if(fprint(ctlfd, "delpart %s\n", p->ctlname)<0) {
  425. fprint(2, "delpart failed: %s: %r\n", p->ctlname);
  426. waserr = -1;
  427. }
  428. }
  429. /*
  430. * add all the partitions from the real list;
  431. * this is okay since adding a parition with
  432. * information identical to what is there is a no-op.
  433. */
  434. offset = edit->disk->offset;
  435. for(i=0; i<edit->npart; i++) {
  436. p = edit->part[i];
  437. if(p->ctlname[0]) {
  438. if(fprint(ctlfd, "part %s %lld %lld\n", p->ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) {
  439. fprint(2, "adding part failed: %s: %r\n", p->ctlname);
  440. waserr = -1;
  441. }
  442. }
  443. }
  444. return waserr;
  445. }
  446. void*
  447. emalloc(ulong sz)
  448. {
  449. void *v;
  450. v = malloc(sz);
  451. if(v == nil)
  452. sysfatal("malloc %lud fails\n", sz);
  453. memset(v, 0, sz);
  454. return v;
  455. }
  456. char*
  457. estrdup(char *s)
  458. {
  459. s = strdup(s);
  460. if(s == nil)
  461. sysfatal("strdup (%.10s) fails\n", s);
  462. return s;
  463. }