parts.c 12 KB

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