parts.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*
  2. * read disk partition tables, intended for early use on systems
  3. * that don't use 9load. borrowed from 9load.
  4. */
  5. #include <u.h>
  6. #include <libc.h>
  7. #include <auth.h>
  8. #include <fcall.h>
  9. #include <bio.h>
  10. #include "../boot/boot.h"
  11. typedef struct Fs Fs;
  12. #include "/sys/src/boot/pc/dosfs.h"
  13. #define GSHORT(p) (((p)[1]<<8)|(p)[0])
  14. #define GLONG(p) ((GSHORT((p)+2)<<16)|GSHORT(p))
  15. #define trace 0
  16. enum {
  17. parttrace = 0,
  18. Npart = 64,
  19. SDnpart = Npart,
  20. Maxsec = 2048,
  21. Cdsec = 2048,
  22. Normsec = 512, /* disks */
  23. NAMELEN = 256, /* hack */
  24. };
  25. typedef struct SDpart SDpart;
  26. typedef struct SDunit SDunit;
  27. typedef struct SDpart {
  28. uvlong start;
  29. uvlong end;
  30. char name[NAMELEN];
  31. int valid;
  32. } SDpart;
  33. typedef struct SDunit {
  34. int ctl; /* fds */
  35. int data;
  36. char name[NAMELEN];
  37. uvlong sectors;
  38. ulong secsize;
  39. SDpart* part;
  40. int npart; /* of valid partitions */
  41. } SDunit;
  42. static uchar *mbrbuf, *partbuf;
  43. static void
  44. sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
  45. {
  46. SDpart *pp;
  47. int i, partno;
  48. if(parttrace)
  49. print("add %d %s %s %lld %lld\n", unit->npart, unit->name, name, start, end);
  50. /*
  51. * Check name not already used
  52. * and look for a free slot.
  53. */
  54. if(unit->part != nil){
  55. partno = -1;
  56. for(i = 0; i < SDnpart; i++){
  57. pp = &unit->part[i];
  58. if(!pp->valid){
  59. if(partno == -1)
  60. partno = i;
  61. break;
  62. }
  63. if(strcmp(name, pp->name) == 0){
  64. if(pp->start == start && pp->end == end){
  65. if(parttrace)
  66. print("already present\n");
  67. return;
  68. }
  69. }
  70. }
  71. }else{
  72. if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){
  73. if(parttrace)
  74. print("malloc failed\n");
  75. return;
  76. }
  77. partno = 0;
  78. }
  79. /*
  80. * Check there is a free slot and size and extent are valid.
  81. */
  82. if(partno == -1 || start > end || end > unit->sectors){
  83. print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n",
  84. unit->name, name, start, end, unit->sectors,
  85. partno==-1 ? "no free partitions" : "partition boundaries out of range");
  86. return;
  87. }
  88. pp = &unit->part[partno];
  89. pp->start = start;
  90. pp->end = end;
  91. strncpy(pp->name, name, NAMELEN);
  92. pp->valid = 1;
  93. unit->npart++;
  94. /* update devsd's in-memory partition table */
  95. if (fprint(unit->ctl, "part %s %lld %lld\n", name, start, end) < 0)
  96. fprint(2, "can't update %s's devsd partition table for %s: %r\n",
  97. unit->name, name);
  98. if (debugboot)
  99. print("part %s %lld %lld\n", name, start, end);
  100. }
  101. static long
  102. sdread(SDunit *unit, SDpart *pp, void* va, long len, vlong off)
  103. {
  104. long l, secsize;
  105. uvlong bno, nb;
  106. /*
  107. * Check the request is within partition bounds.
  108. */
  109. secsize = unit->secsize;
  110. if (secsize == 0)
  111. sysfatal("sdread: zero sector size");
  112. bno = off/secsize + pp->start;
  113. nb = (off+len+secsize-1)/secsize + pp->start - bno;
  114. if(bno+nb > pp->end)
  115. nb = pp->end - bno;
  116. if(bno >= pp->end || nb == 0)
  117. return 0;
  118. seek(unit->data, bno * secsize, 0);
  119. assert(va); /* "sdread" */
  120. l = read(unit->data, va, len);
  121. if (l < 0)
  122. return 0;
  123. return l;
  124. }
  125. static int
  126. sdreadblk(SDunit *unit, SDpart *part, void *a, vlong off, int mbr)
  127. {
  128. uchar *b;
  129. assert(a); /* sdreadblk */
  130. if(sdread(unit, part, a, unit->secsize, off) != unit->secsize){
  131. if(trace)
  132. print("%s: read %lud at %lld failed\n", unit->name,
  133. unit->secsize, (vlong)part->start*unit->secsize+off);
  134. return -1;
  135. }
  136. b = a;
  137. if(mbr && (b[0x1FE] != 0x55 || b[0x1FF] != 0xAA)){
  138. if(trace)
  139. print("%s: bad magic %.2ux %.2ux at %lld\n",
  140. unit->name, b[0x1FE], b[0x1FF],
  141. (vlong)part->start*unit->secsize+off);
  142. return -1;
  143. }
  144. return 0;
  145. }
  146. /*
  147. * read partition table. The partition table is just ascii strings.
  148. */
  149. #define MAGIC "plan9 partitions"
  150. static void
  151. oldp9part(SDunit *unit)
  152. {
  153. SDpart *pp;
  154. char *field[3], *line[Npart+1];
  155. ulong n;
  156. uvlong start, end;
  157. int i;
  158. /*
  159. * We have some partitions already.
  160. */
  161. pp = &unit->part[unit->npart];
  162. /*
  163. * We prefer partition tables on the second to last sector,
  164. * but some old disks use the last sector instead.
  165. */
  166. strcpy(pp->name, "partition");
  167. pp->start = unit->sectors - 2;
  168. pp->end = unit->sectors - 1;
  169. if(debugboot)
  170. print("oldp9part %s\n", unit->name);
  171. if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
  172. return;
  173. if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0) {
  174. /* not found on 2nd last sector; look on last sector */
  175. pp->start++;
  176. pp->end++;
  177. if(sdreadblk(unit, pp, partbuf, 0, 0) < 0)
  178. return;
  179. if(strncmp((char*)partbuf, MAGIC, sizeof(MAGIC)-1) != 0)
  180. return;
  181. print("%s: using old plan9 partition table on last sector\n", unit->name);
  182. }else
  183. print("%s: using old plan9 partition table on 2nd-to-last sector\n", unit->name);
  184. /* we found a partition table, so add a partition partition */
  185. unit->npart++;
  186. partbuf[unit->secsize-1] = '\0';
  187. /*
  188. * parse partition table
  189. */
  190. n = gettokens((char*)partbuf, line, Npart+1, "\n");
  191. if(n && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
  192. for(i = 1; i < n && unit->npart < SDnpart; i++){
  193. if(gettokens(line[i], field, 3, " ") != 3)
  194. break;
  195. start = strtoull(field[1], 0, 0);
  196. end = strtoull(field[2], 0, 0);
  197. if(start >= end || end > unit->sectors)
  198. break;
  199. sdaddpart(unit, field[0], start, end);
  200. }
  201. }
  202. }
  203. static SDpart*
  204. sdfindpart(SDunit *unit, char *name)
  205. {
  206. int i;
  207. if(parttrace)
  208. print("findpart %d %s %s: ", unit->npart, unit->name, name);
  209. for(i=0; i<unit->npart; i++) {
  210. if(parttrace)
  211. print("%s...", unit->part[i].name);
  212. if(strcmp(unit->part[i].name, name) == 0){
  213. if(parttrace)
  214. print("\n");
  215. return &unit->part[i];
  216. }
  217. }
  218. if(parttrace)
  219. print("not found\n");
  220. return nil;
  221. }
  222. /*
  223. * look for a plan 9 partition table on drive `unit' in the second
  224. * sector (sector 1) of partition `name'.
  225. * if found, add the partitions defined in the table.
  226. */
  227. static void
  228. p9part(SDunit *unit, char *name)
  229. {
  230. SDpart *p;
  231. char *field[4], *line[Npart+1];
  232. uvlong start, end;
  233. int i, n;
  234. if(debugboot)
  235. print("p9part %s %s\n", unit->name, name);
  236. p = sdfindpart(unit, name);
  237. if(p == nil)
  238. return;
  239. if(sdreadblk(unit, p, partbuf, unit->secsize, 0) < 0)
  240. return;
  241. partbuf[unit->secsize-1] = '\0';
  242. if(strncmp((char*)partbuf, "part ", 5) != 0)
  243. return;
  244. n = gettokens((char*)partbuf, line, Npart+1, "\n");
  245. if(n == 0)
  246. return;
  247. for(i = 0; i < n && unit->npart < SDnpart; i++){
  248. if(strncmp(line[i], "part ", 5) != 0)
  249. break;
  250. if(gettokens(line[i], field, 4, " ") != 4)
  251. break;
  252. start = strtoull(field[2], 0, 0);
  253. end = strtoull(field[3], 0, 0);
  254. if(start >= end || end > unit->sectors)
  255. break;
  256. sdaddpart(unit, field[1], p->start+start, p->start+end);
  257. }
  258. }
  259. static int
  260. isdos(int t)
  261. {
  262. return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
  263. }
  264. static int
  265. isextend(int t)
  266. {
  267. return t==EXTEND || t==EXTHUGE || t==LEXTEND;
  268. }
  269. /*
  270. * Fetch the first dos and all plan9 partitions out of the MBR partition table.
  271. * We return -1 if we did not find a plan9 partition.
  272. */
  273. static int
  274. mbrpart(SDunit *unit)
  275. {
  276. Dospart *dp;
  277. uvlong taboffset, start, end;
  278. uvlong firstxpart, nxtxpart;
  279. int havedos, i, nplan9;
  280. char name[10];
  281. taboffset = 0;
  282. dp = (Dospart*)&mbrbuf[0x1BE];
  283. {
  284. /* get the MBR (allowing for DMDDO) */
  285. if(sdreadblk(unit, &unit->part[0], mbrbuf,
  286. (vlong)taboffset * unit->secsize, 1) < 0)
  287. return -1;
  288. for(i=0; i<4; i++)
  289. if(dp[i].type == DMDDO) {
  290. if(trace)
  291. print("DMDDO partition found\n");
  292. taboffset = 63;
  293. if(sdreadblk(unit, &unit->part[0], mbrbuf,
  294. (vlong)taboffset * unit->secsize, 1) < 0)
  295. return -1;
  296. i = -1; /* start over */
  297. }
  298. }
  299. /*
  300. * Read the partitions, first from the MBR and then
  301. * from successive extended partition tables.
  302. */
  303. nplan9 = 0;
  304. havedos = 0;
  305. firstxpart = 0;
  306. for(;;) {
  307. if(sdreadblk(unit, &unit->part[0], mbrbuf,
  308. (vlong)taboffset * unit->secsize, 1) < 0)
  309. return -1;
  310. if(trace) {
  311. if(firstxpart)
  312. print("%s ext %llud ", unit->name, taboffset);
  313. else
  314. print("%s mbr ", unit->name);
  315. }
  316. nxtxpart = 0;
  317. for(i=0; i<4; i++) {
  318. if(trace)
  319. print("dp %d...", dp[i].type);
  320. start = taboffset+GLONG(dp[i].start);
  321. end = start+GLONG(dp[i].len);
  322. if(dp[i].type == PLAN9) {
  323. if(nplan9 == 0)
  324. strcpy(name, "plan9");
  325. else
  326. sprint(name, "plan9.%d", nplan9);
  327. sdaddpart(unit, name, start, end);
  328. p9part(unit, name);
  329. nplan9++;
  330. }
  331. /*
  332. * We used to take the active partition (and then the first
  333. * when none are active). We have to take the first here,
  334. * so that the partition we call ``dos'' agrees with the
  335. * partition disk/fdisk calls ``dos''.
  336. */
  337. if(havedos==0 && isdos(dp[i].type)){
  338. havedos = 1;
  339. sdaddpart(unit, "dos", start, end);
  340. }
  341. /* nxtxpart is relative to firstxpart (or 0), not taboffset */
  342. if(isextend(dp[i].type)){
  343. nxtxpart = start-taboffset+firstxpart;
  344. if(trace)
  345. print("link %llud...", nxtxpart);
  346. }
  347. }
  348. if(trace)
  349. print("\n");
  350. if(!nxtxpart)
  351. break;
  352. if(!firstxpart)
  353. firstxpart = nxtxpart;
  354. taboffset = nxtxpart;
  355. }
  356. return nplan9 ? 0 : -1;
  357. }
  358. /*
  359. * To facilitate booting from CDs, we create a partition for
  360. * the boot floppy image embedded in a bootable CD.
  361. */
  362. static int
  363. part9660(SDunit *unit)
  364. {
  365. uchar buf[Maxsec];
  366. ulong a, n;
  367. uchar *p;
  368. if(unit->secsize != Cdsec)
  369. return -1;
  370. if(sdread(unit, &unit->part[0], buf, Cdsec, 17*Cdsec) < 0)
  371. return -1;
  372. if(buf[0] || strcmp((char*)buf+1, "CD001\x01EL TORITO SPECIFICATION") != 0)
  373. return -1;
  374. p = buf+0x47;
  375. a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
  376. if(sdread(unit, &unit->part[0], buf, Cdsec, a*Cdsec) < 0)
  377. return -1;
  378. if(memcmp(buf, "\x01\x00\x00\x00", 4) != 0
  379. || memcmp(buf+30, "\x55\xAA", 2) != 0
  380. || buf[0x20] != 0x88)
  381. return -1;
  382. p = buf+0x28;
  383. a = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
  384. switch(buf[0x21]){
  385. case 0x01:
  386. n = 1200*1024;
  387. break;
  388. case 0x02:
  389. n = 1440*1024;
  390. break;
  391. case 0x03:
  392. n = 2880*1024;
  393. break;
  394. default:
  395. return -1;
  396. }
  397. n /= Cdsec;
  398. print("found partition %s!cdboot; %lud+%lud\n", unit->name, a, n);
  399. sdaddpart(unit, "cdboot", a, a+n);
  400. return 0;
  401. }
  402. enum {
  403. NEW = 1<<0,
  404. OLD = 1<<1
  405. };
  406. /*
  407. * read unit->data to look for partition tables.
  408. * if found, stash partitions in environment and write them to ctl too.
  409. */
  410. static void
  411. partition(SDunit *unit)
  412. {
  413. int type;
  414. char *p;
  415. if(unit->part == 0)
  416. return;
  417. if(part9660(unit) == 0)
  418. return;
  419. p = getenv("partition");
  420. if(p != nil && strncmp(p, "new", 3) == 0)
  421. type = NEW;
  422. else if(p != nil && strncmp(p, "old", 3) == 0)
  423. type = OLD;
  424. else
  425. type = NEW|OLD;
  426. if(mbrbuf == nil) {
  427. mbrbuf = malloc(Maxsec);
  428. partbuf = malloc(Maxsec);
  429. if(mbrbuf==nil || partbuf==nil) {
  430. free(mbrbuf);
  431. free(partbuf);
  432. partbuf = mbrbuf = nil;
  433. return;
  434. }
  435. }
  436. /*
  437. * there might be no mbr (e.g. on a very large device), so look for
  438. * a bare plan 9 partition table if mbrpart fails.
  439. */
  440. if((type & NEW) && mbrpart(unit) >= 0){
  441. /* nothing to do */
  442. }
  443. else if (type & NEW)
  444. p9part(unit, "data");
  445. else if(type & OLD)
  446. oldp9part(unit);
  447. }
  448. static void
  449. rdgeom(SDunit *unit)
  450. {
  451. char *line;
  452. char *flds[5];
  453. Biobuf bb;
  454. Biobuf *bp;
  455. static char geom[] = "geometry ";
  456. bp = &bb;
  457. seek(unit->ctl, 0, 0);
  458. Binit(bp, unit->ctl, OREAD);
  459. while((line = Brdline(bp, '\n')) != nil){
  460. line[Blinelen(bp) - 1] = '\0';
  461. if (strncmp(line, geom, sizeof geom - 1) == 0)
  462. break;
  463. }
  464. if (line != nil && tokenize(line, flds, nelem(flds)) >= 3) {
  465. unit->sectors = atoll(flds[1]);
  466. unit->secsize = atoll(flds[2]);
  467. }
  468. Bterm(bp);
  469. seek(unit->ctl, 0, 0);
  470. }
  471. static void
  472. setpartitions(char *name, int ctl, int data)
  473. {
  474. SDunit sdunit;
  475. SDunit *unit;
  476. SDpart *part0;
  477. unit = &sdunit;
  478. memset(unit, 0, sizeof *unit);
  479. unit->ctl = ctl;
  480. unit->data = data;
  481. unit->secsize = Normsec; /* default: won't work for CDs */
  482. unit->sectors = ~0ull;
  483. rdgeom(unit);
  484. strncpy(unit->name, name, sizeof unit->name);
  485. unit->part = mallocz(sizeof(SDpart) * SDnpart, 1);
  486. part0 = &unit->part[0];
  487. part0->end = unit->sectors - 1;
  488. strcpy(part0->name, "data");
  489. part0->valid = 1;
  490. unit->npart++;
  491. mbrbuf = malloc(Maxsec);
  492. partbuf = malloc(Maxsec);
  493. partition(unit);
  494. free(unit->part);
  495. }
  496. /*
  497. * read disk partition tables so that readnvram via factotum
  498. * can see them.
  499. */
  500. int
  501. readparts(void)
  502. {
  503. int i, n, ctl, data, fd;
  504. char *name, *ctlname, *dataname;
  505. Dir *dir;
  506. fd = open("/dev", OREAD);
  507. if(fd < 0)
  508. return -1;
  509. n = dirreadall(fd, &dir);
  510. close(fd);
  511. for(i = 0; i < n; i++) {
  512. name = dir[i].name;
  513. if (strncmp(name, "sd", 2) != 0)
  514. continue;
  515. ctlname = smprint("/dev/%s/ctl", name);
  516. dataname = smprint("/dev/%s/data", name);
  517. if (ctlname == nil || dataname == nil) {
  518. free(ctlname);
  519. free(dataname);
  520. continue;
  521. }
  522. ctl = open(ctlname, ORDWR);
  523. data = open(dataname, OREAD);
  524. free(ctlname);
  525. free(dataname);
  526. if (ctl >= 0 && data >= 0)
  527. setpartitions(dataname, ctl, data);
  528. close(ctl);
  529. close(data);
  530. }
  531. free(dir);
  532. return 0;
  533. }