ext2.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. /*
  2. * This is a set of functions that provides minimal filesystem
  3. * functionality to the Linux bootstrapper. All we can do is
  4. * open and read files... but that's all we need 8-)
  5. *
  6. * This file has been ported from the DEC 32-bit Linux version
  7. * by David Mosberger (davidm@cs.arizona.edu).
  8. */
  9. #include <linux/stat.h>
  10. #include <linux/types.h>
  11. #include <linux/version.h>
  12. #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
  13. # undef __KERNEL__
  14. # include <linux/ext2_fs.h>
  15. # define __KERNEL__
  16. # include <linux/fs.h>
  17. #else /* Linux 2.4.0 or later */
  18. typedef unsigned short umode_t;
  19. # undef __KERNEL__
  20. # include <linux/ext2_fs.h>
  21. # include <linux/fs.h>
  22. # define __KERNEL__
  23. #endif
  24. #include "bootfs.h"
  25. #include "cons.h"
  26. #include "disklabel.h"
  27. #include "utils.h"
  28. #include "string.h"
  29. #define MAX_OPEN_FILES 5
  30. extern struct bootfs ext2fs;
  31. static struct ext2_super_block sb;
  32. static struct ext2_group_desc *gds;
  33. static struct ext2_inode *root_inode = NULL;
  34. static int ngroups = 0;
  35. static int directlim; /* Maximum direct blkno */
  36. static int ind1lim; /* Maximum single-indir blkno */
  37. static int ind2lim; /* Maximum double-indir blkno */
  38. static int ptrs_per_blk; /* ptrs/indirect block */
  39. static char blkbuf[EXT2_MAX_BLOCK_SIZE];
  40. static int cached_iblkno = -1;
  41. static char iblkbuf[EXT2_MAX_BLOCK_SIZE];
  42. static int cached_diblkno = -1;
  43. static char diblkbuf[EXT2_MAX_BLOCK_SIZE];
  44. static long dev = -1;
  45. static long partition_offset;
  46. static struct inode_table_entry {
  47. struct ext2_inode inode;
  48. int inumber;
  49. int free;
  50. unsigned short old_mode;
  51. } inode_table[MAX_OPEN_FILES];
  52. /*
  53. * Initialize an ext2 partition starting at offset P_OFFSET; this is
  54. * sort-of the same idea as "mounting" it. Read in the relevant
  55. * control structures and make them available to the user. Returns 0
  56. * if successful, -1 on failure.
  57. */
  58. static int ext2_mount(long cons_dev, long p_offset, long quiet)
  59. {
  60. long sb_block = 1;
  61. long sb_offset;
  62. int i;
  63. dev = cons_dev;
  64. partition_offset = p_offset;
  65. /* initialize the inode table */
  66. for (i = 0; i < MAX_OPEN_FILES; i++) {
  67. inode_table[i].free = 1;
  68. inode_table[i].inumber = 0;
  69. }
  70. /* clear the root inode pointer (very important!) */
  71. root_inode = NULL;
  72. /* read in the first superblock */
  73. sb_offset = sb_block * EXT2_MIN_BLOCK_SIZE;
  74. if (cons_read(dev, &sb, sizeof(sb), partition_offset + sb_offset)
  75. != sizeof(sb))
  76. {
  77. printf("ext2 sb read failed\n");
  78. return -1;
  79. }
  80. if (sb.s_magic != EXT2_SUPER_MAGIC) {
  81. if (!quiet) {
  82. printf("ext2_init: bad magic 0x%x\n", sb.s_magic);
  83. }
  84. return -1;
  85. }
  86. ngroups = (sb.s_blocks_count -
  87. sb.s_first_data_block +
  88. EXT2_BLOCKS_PER_GROUP(&sb) - 1)
  89. / EXT2_BLOCKS_PER_GROUP(&sb);
  90. gds = (struct ext2_group_desc *)
  91. malloc((size_t)(ngroups * sizeof(struct ext2_group_desc)));
  92. ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb);
  93. /* read in the group descriptors (immediately follows superblock) */
  94. cons_read(dev, gds, ngroups * sizeof(struct ext2_group_desc),
  95. partition_offset +
  96. ext2fs.blocksize * (EXT2_MIN_BLOCK_SIZE/ext2fs.blocksize + 1));
  97. /*
  98. * Calculate direct/indirect block limits for this file system
  99. * (blocksize dependent):
  100. */
  101. ext2fs.blocksize = EXT2_BLOCK_SIZE(&sb);
  102. directlim = EXT2_NDIR_BLOCKS - 1;
  103. ptrs_per_blk = ext2fs.blocksize/sizeof(unsigned int);
  104. ind1lim = ptrs_per_blk + directlim;
  105. ind2lim = (ptrs_per_blk * ptrs_per_blk) + directlim;
  106. return 0;
  107. }
  108. /*
  109. * Read the specified inode from the disk and return it to the user.
  110. * Returns NULL if the inode can't be read...
  111. */
  112. static struct ext2_inode *ext2_iget(int ino)
  113. {
  114. int i;
  115. struct ext2_inode *ip;
  116. struct inode_table_entry *itp = 0;
  117. int group;
  118. long offset;
  119. ip = 0;
  120. for (i = 0; i < MAX_OPEN_FILES; i++) {
  121. #ifdef DEBUG_EXT2
  122. printf("ext2_iget: looping, entry %d inode %d free %d\n",
  123. i, inode_table[i].inumber, inode_table[i].free);
  124. #endif
  125. if (inode_table[i].free) {
  126. itp = &inode_table[i];
  127. ip = &itp->inode;
  128. break;
  129. }
  130. }
  131. if (!ip) {
  132. printf("ext2_iget: no free inodes\n");
  133. return NULL;
  134. }
  135. group = (ino-1) / sb.s_inodes_per_group;
  136. #ifdef DEBUG_EXT2
  137. printf("group is %d\n", group);
  138. #endif
  139. offset = partition_offset
  140. + ((long) gds[group].bg_inode_table * (long)ext2fs.blocksize)
  141. + (((ino - 1) % EXT2_INODES_PER_GROUP(&sb))
  142. * EXT2_INODE_SIZE(&sb));
  143. #ifdef DEBUG_EXT2
  144. printf("ext2_iget: reading %ld bytes at offset %ld "
  145. "(%ld + (%d * %d) + ((%d) %% %d) * %d) "
  146. "(inode %d -> table %d)\n",
  147. sizeof(struct ext2_inode), offset, partition_offset,
  148. gds[group].bg_inode_table, ext2fs.blocksize,
  149. ino - 1, EXT2_INODES_PER_GROUP(&sb), EXT2_INODE_SIZE(&sb),
  150. ino, (int) (itp - inode_table));
  151. #endif
  152. if (cons_read(dev, ip, sizeof(struct ext2_inode), offset)
  153. != sizeof(struct ext2_inode))
  154. {
  155. printf("ext2_iget: read error\n");
  156. return NULL;
  157. }
  158. itp->free = 0;
  159. itp->inumber = ino;
  160. itp->old_mode = ip->i_mode;
  161. return ip;
  162. }
  163. /*
  164. * Release our hold on an inode. Since this is a read-only application,
  165. * don't worry about putting back any changes...
  166. */
  167. static void ext2_iput(struct ext2_inode *ip)
  168. {
  169. struct inode_table_entry *itp;
  170. /* Find and free the inode table slot we used... */
  171. itp = (struct inode_table_entry *)ip;
  172. #ifdef DEBUG_EXT2
  173. printf("ext2_iput: inode %d table %d\n", itp->inumber,
  174. (int) (itp - inode_table));
  175. #endif
  176. itp->inumber = 0;
  177. itp->free = 1;
  178. }
  179. /*
  180. * Map a block offset into a file into an absolute block number.
  181. * (traverse the indirect blocks if necessary). Note: Double-indirect
  182. * blocks allow us to map over 64Mb on a 1k file system. Therefore, for
  183. * our purposes, we will NOT bother with triple indirect blocks.
  184. *
  185. * The "allocate" argument is set if we want to *allocate* a block
  186. * and we don't already have one allocated.
  187. */
  188. static int ext2_blkno(struct ext2_inode *ip, int blkoff)
  189. {
  190. unsigned int *lp;
  191. unsigned int *ilp;
  192. unsigned int *dlp;
  193. int blkno;
  194. int iblkno;
  195. int diblkno;
  196. unsigned long offset;
  197. ilp = (unsigned int *)iblkbuf;
  198. dlp = (unsigned int *)diblkbuf;
  199. lp = (unsigned int *)blkbuf;
  200. /* If it's a direct block, it's easy! */
  201. if (blkoff <= directlim) {
  202. return ip->i_block[blkoff];
  203. }
  204. /* Is it a single-indirect? */
  205. if (blkoff <= ind1lim) {
  206. iblkno = ip->i_block[EXT2_IND_BLOCK];
  207. if (iblkno == 0) {
  208. return 0;
  209. }
  210. /* Read the indirect block */
  211. if (cached_iblkno != iblkno) {
  212. offset = partition_offset + (long)iblkno * (long)ext2fs.blocksize;
  213. if (cons_read(dev, iblkbuf, ext2fs.blocksize, offset)
  214. != ext2fs.blocksize)
  215. {
  216. printf("ext2_blkno: error on iblk read\n");
  217. return 0;
  218. }
  219. cached_iblkno = iblkno;
  220. }
  221. blkno = ilp[blkoff-(directlim+1)];
  222. return blkno;
  223. }
  224. /* Is it a double-indirect? */
  225. if (blkoff <= ind2lim) {
  226. /* Find the double-indirect block */
  227. diblkno = ip->i_block[EXT2_DIND_BLOCK];
  228. if (diblkno == 0) {
  229. return 0;
  230. }
  231. /* Read in the double-indirect block */
  232. if (cached_diblkno != diblkno) {
  233. offset = partition_offset + (long) diblkno * (long) ext2fs.blocksize;
  234. if (cons_read(dev, diblkbuf, ext2fs.blocksize, offset)
  235. != ext2fs.blocksize)
  236. {
  237. printf("ext2_blkno: err reading dindr blk\n");
  238. return 0;
  239. }
  240. cached_diblkno = diblkno;
  241. }
  242. /* Find the single-indirect block pointer ... */
  243. iblkno = dlp[(blkoff - (ind1lim+1)) / ptrs_per_blk];
  244. if (iblkno == 0) {
  245. return 0;
  246. }
  247. /* Read the indirect block */
  248. if (cached_iblkno != iblkno) {
  249. offset = partition_offset + (long) iblkno * (long) ext2fs.blocksize;
  250. if (cons_read(dev, iblkbuf, ext2fs.blocksize, offset)
  251. != ext2fs.blocksize)
  252. {
  253. printf("ext2_blkno: err on iblk read\n");
  254. return 0;
  255. }
  256. cached_iblkno = iblkno;
  257. }
  258. /* Find the block itself. */
  259. blkno = ilp[(blkoff-(ind1lim+1)) % ptrs_per_blk];
  260. return blkno;
  261. }
  262. if (blkoff > ind2lim) {
  263. printf("ext2_blkno: block number too large: %d\n", blkoff);
  264. return 0;
  265. }
  266. return -1;
  267. }
  268. static int ext2_breadi(struct ext2_inode *ip, long blkno, long nblks,
  269. char *buffer)
  270. {
  271. long dev_blkno, ncontig, offset, nbytes, tot_bytes;
  272. tot_bytes = 0;
  273. if ((blkno+nblks)*ext2fs.blocksize > ip->i_size)
  274. nblks = (ip->i_size + ext2fs.blocksize) / ext2fs.blocksize - blkno;
  275. while (nblks) {
  276. /*
  277. * Contiguous reads are a lot faster, so we try to group
  278. * as many blocks as possible:
  279. */
  280. ncontig = 0; nbytes = 0;
  281. dev_blkno = ext2_blkno(ip, blkno);
  282. do {
  283. ++blkno; ++ncontig; --nblks;
  284. nbytes += ext2fs.blocksize;
  285. } while (nblks &&
  286. ext2_blkno(ip, blkno) == dev_blkno + ncontig);
  287. if (dev_blkno == 0) {
  288. /* This is a "hole" */
  289. memset(buffer, 0, nbytes);
  290. } else {
  291. /* Read it for real */
  292. offset = partition_offset + (long) dev_blkno* (long) ext2fs.blocksize;
  293. #ifdef DEBUG_EXT2
  294. printf("ext2_bread: reading %ld bytes at offset %ld\n",
  295. nbytes, offset);
  296. #endif
  297. if (cons_read(dev, buffer, nbytes, offset)
  298. != nbytes)
  299. {
  300. printf("ext2_bread: read error\n");
  301. return -1;
  302. }
  303. }
  304. buffer += nbytes;
  305. tot_bytes += nbytes;
  306. }
  307. return tot_bytes;
  308. }
  309. static struct ext2_dir_entry_2 *ext2_readdiri(struct ext2_inode *dir_inode,
  310. int rewind)
  311. {
  312. struct ext2_dir_entry_2 *dp;
  313. static int diroffset = 0, blockoffset = 0;
  314. /* Reading a different directory, invalidate previous state */
  315. if (rewind) {
  316. diroffset = 0;
  317. blockoffset = 0;
  318. /* read first block */
  319. if (ext2_breadi(dir_inode, 0, 1, blkbuf) < 0)
  320. return NULL;
  321. }
  322. #ifdef DEBUG_EXT2
  323. printf("ext2_readdiri: blkoffset %d diroffset %d len %d\n",
  324. blockoffset, diroffset, dir_inode->i_size);
  325. #endif
  326. if (blockoffset >= ext2fs.blocksize) {
  327. diroffset += ext2fs.blocksize;
  328. if (diroffset >= dir_inode->i_size)
  329. return NULL;
  330. #ifdef DEBUG_EXT2
  331. printf("ext2_readdiri: reading block at %d\n",
  332. diroffset);
  333. #endif
  334. /* assume that this will read the whole block */
  335. if (ext2_breadi(dir_inode,
  336. diroffset / ext2fs.blocksize,
  337. 1, blkbuf) < 0)
  338. return NULL;
  339. blockoffset = 0;
  340. }
  341. dp = (struct ext2_dir_entry_2 *) (blkbuf + blockoffset);
  342. blockoffset += dp->rec_len;
  343. #ifdef DEBUG_EXT2
  344. printf("ext2_readdiri: returning %p = %.*s\n", dp, dp->name_len, dp->name);
  345. #endif
  346. return dp;
  347. }
  348. static struct ext2_inode *ext2_namei(const char *name)
  349. {
  350. char namebuf[256];
  351. char *component;
  352. struct ext2_inode *dir_inode;
  353. struct ext2_dir_entry_2 *dp;
  354. int next_ino;
  355. /* squirrel away a copy of "namebuf" that we can modify: */
  356. strcpy(namebuf, name);
  357. /* start at the root: */
  358. if (!root_inode)
  359. root_inode = ext2_iget(EXT2_ROOT_INO);
  360. dir_inode = root_inode;
  361. if (!dir_inode)
  362. return NULL;
  363. component = strtok(namebuf, "/");
  364. while (component) {
  365. int component_length;
  366. int rewind = 0;
  367. /*
  368. * Search for the specified component in the current
  369. * directory inode.
  370. */
  371. next_ino = -1;
  372. component_length = strlen(component);
  373. /* rewind the first time through */
  374. while ((dp = ext2_readdiri(dir_inode, !rewind++))) {
  375. if ((dp->name_len == component_length) &&
  376. (strncmp(component, dp->name,
  377. component_length) == 0))
  378. {
  379. /* Found it! */
  380. #ifdef DEBUG_EXT2
  381. printf("ext2_namei: found entry %s\n",
  382. component);
  383. #endif
  384. next_ino = dp->inode;
  385. break;
  386. }
  387. #ifdef DEBUG_EXT2
  388. printf("ext2_namei: looping\n");
  389. #endif
  390. }
  391. #ifdef DEBUG_EXT2
  392. printf("ext2_namei: next_ino = %d\n", next_ino);
  393. #endif
  394. /*
  395. * At this point, we're done with this directory whether
  396. * we've succeeded or failed...
  397. */
  398. if (dir_inode != root_inode)
  399. ext2_iput(dir_inode);
  400. /*
  401. * If next_ino is negative, then we've failed (gone
  402. * all the way through without finding anything)
  403. */
  404. if (next_ino < 0) {
  405. return NULL;
  406. }
  407. /*
  408. * Otherwise, we can get this inode and find the next
  409. * component string...
  410. */
  411. dir_inode = ext2_iget(next_ino);
  412. if (!dir_inode)
  413. return NULL;
  414. component = strtok(NULL, "/");
  415. }
  416. /*
  417. * If we get here, then we got through all the components.
  418. * Whatever we got must match up with the last one.
  419. */
  420. return dir_inode;
  421. }
  422. /*
  423. * Read block number "blkno" from the specified file.
  424. */
  425. static int ext2_bread(int fd, long blkno, long nblks, char *buffer)
  426. {
  427. struct ext2_inode * ip;
  428. ip = &inode_table[fd].inode;
  429. return ext2_breadi(ip, blkno, nblks, buffer);
  430. }
  431. /*
  432. * Note: don't mix any kind of file lookup or other I/O with this or
  433. * you will lose horribly (as it reuses blkbuf)
  434. */
  435. static const char * ext2_readdir(int fd, int rewind)
  436. {
  437. struct ext2_inode * ip = &inode_table[fd].inode;
  438. struct ext2_dir_entry_2 * ent;
  439. if (!S_ISDIR(ip->i_mode)) {
  440. printf("fd %d (inode %d) is not a directory (mode %x)\n",
  441. fd, inode_table[fd].inumber, ip->i_mode);
  442. return NULL;
  443. }
  444. ent = ext2_readdiri(ip, rewind);
  445. if (ent) {
  446. ent->name[ent->name_len] = '\0';
  447. return ent->name;
  448. } else {
  449. return NULL;
  450. }
  451. }
  452. static int ext2_fstat(int fd, struct stat* buf)
  453. {
  454. struct ext2_inode * ip = &inode_table[fd].inode;
  455. if (fd >= MAX_OPEN_FILES)
  456. return -1;
  457. memset(buf, 0, sizeof(struct stat));
  458. /* fill in relevant fields */
  459. buf->st_ino = inode_table[fd].inumber;
  460. buf->st_mode = ip->i_mode;
  461. buf->st_flags = ip->i_flags;
  462. buf->st_nlink = ip->i_links_count;
  463. buf->st_uid = ip->i_uid;
  464. buf->st_gid = ip->i_gid;
  465. buf->st_size = ip->i_size;
  466. buf->st_blocks = ip->i_blocks;
  467. buf->st_atime = ip->i_atime;
  468. buf->st_mtime = ip->i_mtime;
  469. buf->st_ctime = ip->i_ctime;
  470. return 0; /* NOTHING CAN GO WROGN! */
  471. }
  472. static struct ext2_inode * ext2_follow_link(struct ext2_inode * from,
  473. const char * base)
  474. {
  475. char *linkto;
  476. if (from->i_blocks) {
  477. linkto = blkbuf;
  478. if (ext2_breadi(from, 0, 1, blkbuf) == -1)
  479. return NULL;
  480. #ifdef DEBUG_EXT2
  481. printf("long link!\n");
  482. #endif
  483. } else {
  484. linkto = (char*)from->i_block;
  485. }
  486. #ifdef DEBUG_EXT2
  487. printf("symlink to %s\n", linkto);
  488. #endif
  489. /* Resolve relative links */
  490. if (linkto[0] != '/') {
  491. char *end = strrchr(base, '/');
  492. if (end) {
  493. char fullname[(end - base + 1) + strlen(linkto) + 1];
  494. strncpy(fullname, base, end - base + 1);
  495. fullname[end - base + 1] = '\0';
  496. strcat(fullname, linkto);
  497. #ifdef DEBUG_EXT2
  498. printf("resolved to %s\n", fullname);
  499. #endif
  500. return ext2_namei(fullname);
  501. } else {
  502. /* Assume it's in the root */
  503. return ext2_namei(linkto);
  504. }
  505. } else {
  506. return ext2_namei(linkto);
  507. }
  508. }
  509. static int ext2_open(const char *filename)
  510. {
  511. /*
  512. * Unix-like open routine. Returns a small integer (actually
  513. * an index into the inode table...
  514. */
  515. struct ext2_inode * ip;
  516. ip = ext2_namei(filename);
  517. if (ip) {
  518. struct inode_table_entry *itp;
  519. while (S_ISLNK(ip->i_mode)) {
  520. ip = ext2_follow_link(ip, filename);
  521. if (!ip) return -1;
  522. }
  523. itp = (struct inode_table_entry *)ip;
  524. return itp - inode_table;
  525. } else
  526. return -1;
  527. }
  528. static void ext2_close(int fd)
  529. {
  530. /* blah, hack, don't close the root inode ever */
  531. if (&inode_table[fd].inode != root_inode)
  532. ext2_iput(&inode_table[fd].inode);
  533. }
  534. struct bootfs ext2fs = {
  535. FS_EXT2, 0,
  536. ext2_mount,
  537. ext2_open, ext2_bread, ext2_close,
  538. ext2_readdir, ext2_fstat
  539. };