/filesystems/unixfs/ancientfs/ancientfs_dump.c

http://macfuse.googlecode.com/ · C · 641 lines · 512 code · 117 blank · 12 comment · 120 complexity · d6ca85aea98912afb6886c8f386bc572 MD5 · raw file

  1. /*
  2. * Ancient UNIX File Systems for MacFUSE
  3. * Amit Singh
  4. * http://osxbook.com
  5. */
  6. #include "ancientfs_dump.h"
  7. #include "unixfs_common.h"
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <unistd.h>
  14. #include <sys/ioctl.h>
  15. #include <sys/stat.h>
  16. #include <assert.h>
  17. #if UCB_NKB == 1
  18. DECL_UNIXFS("UNIX dump1024/restor", dump1024);
  19. #else
  20. DECL_UNIXFS("UNIX dump/restor", dump);
  21. #endif
  22. static int ancientfs_dump_readheader(int fd, struct spcl* spcl);
  23. static int
  24. ancientfs_dump_readheader(int fd, struct spcl* spcl)
  25. {
  26. ssize_t ret;
  27. if ((ret = read(fd, (char*)spcl, BSIZE)) != BSIZE) {
  28. if (ret == 0) /* EOF */
  29. return 1;
  30. return -1;
  31. }
  32. if (ancientfs_dump_cksum((uint16_t*)spcl, unixfs->s_endian,
  33. unixfs->s_flags) != 0)
  34. return -1;
  35. spcl->c_magic = fs16_to_host(unixfs->s_endian, spcl->c_magic);
  36. spcl->c_type = fs16_to_host(unixfs->s_endian, spcl->c_type);
  37. spcl->c_date = fs32_to_host(unixfs->s_endian, spcl->c_date);
  38. spcl->c_ddate = fs32_to_host(unixfs->s_endian, spcl->c_ddate);
  39. spcl->c_volume = fs16_to_host(unixfs->s_endian, spcl->c_volume);
  40. spcl->c_tapea = fs32_to_host(unixfs->s_endian, spcl->c_tapea);
  41. spcl->c_inumber = fs16_to_host(unixfs->s_endian, spcl->c_inumber);
  42. spcl->c_checksum = fs16_to_host(unixfs->s_endian, spcl->c_checksum);
  43. spcl->c_count = fs16_to_host(unixfs->s_endian, spcl->c_count);
  44. struct dinode* di = &spcl->c_dinode;
  45. di->di_mode = fs16_to_host(unixfs->s_endian, di->di_mode);
  46. di->di_nlink = fs16_to_host(unixfs->s_endian, di->di_nlink);
  47. di->di_uid = fs16_to_host(unixfs->s_endian, di->di_uid);
  48. di->di_gid = fs16_to_host(unixfs->s_endian, di->di_gid);
  49. di->di_size = fs32_to_host(unixfs->s_endian, di->di_size);
  50. di->di_atime = fs32_to_host(unixfs->s_endian, di->di_atime);
  51. di->di_mtime = fs32_to_host(unixfs->s_endian, di->di_mtime);
  52. di->di_ctime = fs32_to_host(unixfs->s_endian, di->di_ctime);
  53. return 0;
  54. }
  55. static void*
  56. unixfs_internal_init(const char* dmg, uint32_t flags, fs_endian_t fse,
  57. char** fsname, char** volname)
  58. {
  59. int fd = -1;
  60. if ((fd = open(dmg, O_RDONLY)) < 0) {
  61. perror("open");
  62. return NULL;
  63. }
  64. int err, i;
  65. struct stat stbuf;
  66. struct super_block* sb = (struct super_block*)0;
  67. struct filsys* fs = (struct filsys*)0;
  68. assert(sizeof(struct spcl) == BSIZE);
  69. if ((err = fstat(fd, &stbuf)) != 0) {
  70. perror("fstat");
  71. goto out;
  72. }
  73. if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
  74. err = EINVAL;
  75. fprintf(stderr, "%s is not a tape dump image file\n", dmg);
  76. goto out;
  77. }
  78. if ((stbuf.st_size % TAPE_BSIZE) && !(flags & UNIXFS_FORCE)) {
  79. err = EINVAL;
  80. fprintf(stderr, "%s is not a multiple of tape block size\n", dmg);
  81. goto out;
  82. }
  83. if (S_ISREG(stbuf.st_mode) && (stbuf.st_size < TAPE_BSIZE)) {
  84. err = EINVAL;
  85. fprintf(stderr, "*** fatal error: %s is smaller in size than a "
  86. "physical tape block\n", dmg);
  87. goto out;
  88. }
  89. sb = malloc(sizeof(struct super_block));
  90. if (!sb) {
  91. err = ENOMEM;
  92. goto out;
  93. }
  94. fs = calloc(1, sizeof(struct filsys));
  95. if (!fs) {
  96. free(sb);
  97. err = ENOMEM;
  98. goto out;
  99. }
  100. unixfs = sb;
  101. unixfs->s_flags = flags;
  102. unixfs->s_endian = (fse == UNIXFS_FS_INVALID) ? UNIXFS_FS_PDP : fse;
  103. unixfs->s_fs_info = (void*)fs;
  104. unixfs->s_bdev = fd;
  105. unixfs->s_statvfs.f_bsize = BSIZE;
  106. unixfs->s_statvfs.f_frsize = BSIZE;
  107. fs->s_fsize += stbuf.st_size / BSIZE;
  108. /* must initialize the inode layer before sanity checking */
  109. if ((err = unixfs_inodelayer_init(sizeof(struct tap_node_info))) != 0)
  110. goto out;
  111. struct spcl spcl;
  112. if (ancientfs_dump_readheader(fd, &spcl) != 0) {
  113. fprintf(stderr, "failed to read dump header\n");
  114. err = EINVAL;
  115. goto out;
  116. }
  117. if (spcl.c_type != TS_TAPE) {
  118. fprintf(stderr, "failed to recognize image as a tape dump\n");
  119. err = EINVAL;
  120. goto out;
  121. }
  122. fs->s_date = fs32_to_host(unixfs->s_endian, spcl.c_date);
  123. fs->s_ddate = fs32_to_host(unixfs->s_endian, spcl.c_ddate);
  124. int done = 0;
  125. while (!done) {
  126. err = ancientfs_dump_readheader(fd, &spcl);
  127. if (err) {
  128. if (err != 1) {
  129. fprintf(stderr, "*** warning: no tape header: retrying\n");
  130. continue;
  131. } else {
  132. fprintf(stderr, "failed to read next header (%d)\n", err);
  133. err = EINVAL;
  134. goto out;
  135. }
  136. }
  137. next:
  138. switch (spcl.c_type) {
  139. case TS_TAPE:
  140. break;
  141. case TS_END:
  142. done = 1;
  143. break;
  144. case TS_BITS:
  145. if (!fs->s_initialized) {
  146. fs->s_initialized = 1;
  147. int count = spcl.c_count;
  148. char* bmp = (char*)fs->s_dumpmap;
  149. while (count--) {
  150. if (read(fd, bmp, BSIZE) != BSIZE) {
  151. fprintf(stderr,
  152. "*** fatal error: failed to read bitmap\n");
  153. err = EIO;
  154. goto out;
  155. }
  156. /* fix up endian-ness */
  157. int idx;
  158. for (idx = 0; idx < BSIZE/sizeof(uint16_t); idx++)
  159. bmp[idx] = fs16_to_host(unixfs->s_endian, bmp[idx]);
  160. bmp += BSIZE / sizeof(uint16_t);
  161. }
  162. } else {
  163. fprintf(stderr, "*** warning: duplicate inode map\n");
  164. /* ignore the data */
  165. (void)lseek(fd, (off_t)(spcl.c_count * BSIZE), SEEK_CUR);
  166. }
  167. break;
  168. case TS_INODE: {
  169. struct dinode* dip = &spcl.c_dinode;
  170. a_ino_t candidate = spcl.c_inumber;
  171. if ((!BIT_ON(candidate, fs->s_dumpmap)) || (candidate == BADINO))
  172. continue;
  173. struct inode* ip = unixfs_inodelayer_iget((ino_t)candidate);
  174. if (!ip) {
  175. fprintf(stderr, "*** fatal error: no inode for %llu\n",
  176. (ino64_t)candidate);
  177. abort();
  178. }
  179. struct tap_node_info* ti = (struct tap_node_info*)ip->I_private;
  180. ti->ti_daddr = NULL;
  181. assert(!ip->I_initialized);
  182. ip->I_number = (ino_t)candidate;
  183. ip->I_mode = dip->di_mode;
  184. ip->I_nlink = dip->di_nlink;
  185. ip->I_uid = dip->di_uid;
  186. ip->I_gid = dip->di_gid;
  187. ip->I_size = dip->di_size;
  188. ip->I_atime_sec = dip->di_atime;
  189. ip->I_mtime_sec = dip->di_mtime;
  190. ip->I_ctime_sec = dip->di_ctime;
  191. if (S_ISDIR(ip->I_mode))
  192. fs->s_directories++;
  193. else
  194. fs->s_files++;
  195. /* populate i_daddr */
  196. off_t nblocks = (off_t)((ip->I_size + (BSIZE - 1)) / BSIZE);
  197. ti->ti_daddr = (uint32_t*)calloc(nblocks, sizeof(uint32_t));
  198. if (!ti->ti_daddr) {
  199. fprintf(stderr, "*** fatal error: cannot allocate memory\n");
  200. abort();
  201. }
  202. int block_index = 0;
  203. for (i = 0; i < nblocks; i++) {
  204. if (block_index >= spcl.c_count) {
  205. if (ancientfs_dump_readheader(fd, &spcl) == -1) {
  206. fprintf(stderr,
  207. "*** fatal error: cannot read header\n");
  208. abort();
  209. }
  210. if (spcl.c_type != TS_ADDR) {
  211. fprintf(stderr, "*** warning: expected TS_ADDR but "
  212. "got %hd\n", spcl.c_type);
  213. int k = i;
  214. for (; k < nblocks; k++)
  215. ti->ti_daddr[k] = 0;
  216. goto next;
  217. }
  218. block_index = 0;
  219. }
  220. if (spcl.c_addr[block_index]) {
  221. off_t nextb = lseek(fd, (off_t)BSIZE, SEEK_CUR);
  222. if (nextb == -1) {
  223. fprintf(stderr, "*** fatal error: cannot read tape\n");
  224. abort();
  225. }
  226. ti->ti_daddr[i] = spcl.c_tapea + block_index + 1;
  227. } else {
  228. ti->ti_daddr[i] = 0; /* zero fill */
  229. }
  230. block_index++;
  231. }
  232. if (S_ISCHR(ip->I_mode) || S_ISBLK(ip->I_mode)) {
  233. char* p1 = (char*)(ip->I_daddr);
  234. char* p2 = (char*)(dip->di_addr);
  235. for (i = 0; i < 4; i++) {
  236. *p1++ = *p2++;
  237. *p1++ = 0;
  238. *p1++ = *p2++;
  239. *p1++ = *p2++;
  240. }
  241. ip->I_daddr[0] = fs32_to_host(unixfs->s_endian, ip->I_daddr[0]);
  242. uint32_t rdev = ip->I_daddr[0];
  243. ip->I_rdev = makedev((rdev >> 8) & 255, rdev & 255);
  244. }
  245. if (ip->I_ino > fs->s_lastino)
  246. fs->s_lastino = ip->I_ino;
  247. unixfs_inodelayer_isucceeded(ip);
  248. }
  249. break;
  250. }
  251. }
  252. unixfs->s_statvfs.f_ffree = 0;
  253. unixfs->s_statvfs.f_files = fs->s_files + fs->s_directories;
  254. unixfs->s_statvfs.f_blocks = fs->s_fsize;
  255. unixfs->s_statvfs.f_bfree = 0;
  256. unixfs->s_statvfs.f_bavail = 0;
  257. unixfs->s_dentsize = DIRSIZ + 2;
  258. unixfs->s_statvfs.f_namemax = DIRSIZ;
  259. fs->s_rootip = unixfs_internal_iget(ROOTINO);
  260. if (!fs->s_rootip) {
  261. unixfs_internal_fini(fs);
  262. err = EINVAL;
  263. goto out;
  264. }
  265. fs->s_rootip->I_mtime_sec = fs->s_date;
  266. fs->s_rootip->I_ctime_sec = fs->s_ddate;
  267. unixfs_internal_iput(fs->s_rootip);
  268. snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "UNIX dump/restor");
  269. char* dmg_basename = basename((char*)dmg);
  270. snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (tape=%s)",
  271. unixfs_fstype, (dmg_basename) ? dmg_basename : "Tape Image");
  272. *fsname = unixfs->s_fsname;
  273. *volname = unixfs->s_volname;
  274. out:
  275. if (err) {
  276. if (fs)
  277. unixfs_internal_fini(fs);
  278. else if (sb)
  279. free(sb);
  280. if (fd >= 0)
  281. close(fd);
  282. return NULL;
  283. }
  284. return sb;
  285. }
  286. static void
  287. unixfs_internal_fini(void* filsys)
  288. {
  289. struct super_block* sb = (struct super_block*)filsys;
  290. struct filsys* fs = (struct filsys*)sb->s_fs_info;
  291. if (fs) {
  292. ino_t i = fs->s_lastino;
  293. for (; i >= (ino_t)ROOTINO; i--) {
  294. struct inode* tmp = unixfs_internal_iget(i);
  295. if (tmp) {
  296. struct tap_node_info* ti =
  297. (struct tap_node_info*)tmp->I_private;
  298. unixfs_internal_iput(tmp);
  299. unixfs_internal_iput(tmp);
  300. if (ti->ti_daddr)
  301. free(ti->ti_daddr);
  302. }
  303. }
  304. }
  305. unixfs_inodelayer_fini();
  306. if (sb) {
  307. if (sb->s_bdev >= 0)
  308. close(sb->s_bdev);
  309. sb->s_bdev = -1;
  310. if (fs)
  311. free(fs);
  312. }
  313. }
  314. static off_t
  315. unixfs_internal_alloc(void)
  316. {
  317. return (off_t)0;
  318. }
  319. static off_t
  320. unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error)
  321. {
  322. off_t nblocks = (off_t)((ip->I_size + (BSIZE - 1)) / BSIZE);
  323. if (lblkno >= nblocks) {
  324. *error = EFBIG;
  325. return (off_t)0;
  326. }
  327. *error = 0;
  328. return (off_t)(((struct tap_node_info*)ip->I_private)->ti_daddr[lblkno]);
  329. }
  330. static int
  331. unixfs_internal_bread(off_t blkno, char* blkbuf)
  332. {
  333. if (blkno >= ((struct filsys*)unixfs->s_fs_info)->s_fsize) {
  334. fprintf(stderr,
  335. "***fatal error: bread failed for block %llu\n", blkno);
  336. abort();
  337. /* NOTREACHED */
  338. }
  339. if (blkno == 0) { /* zero fill */
  340. memset(blkbuf, 0, UNIXFS_IOSIZE(unixfs));
  341. return 0;
  342. }
  343. if (pread(unixfs->s_bdev, blkbuf, UNIXFS_IOSIZE(unixfs),
  344. blkno * (off_t)BSIZE) != UNIXFS_IOSIZE(unixfs))
  345. return EIO;
  346. return 0;
  347. }
  348. static struct inode*
  349. unixfs_internal_iget(ino_t ino)
  350. {
  351. if (ino == MACFUSE_ROOTINO)
  352. ino = ROOTINO;
  353. struct inode* ip = unixfs_inodelayer_iget(ino);
  354. if (!ip) {
  355. fprintf(stderr, "*** fatal error: no inode for %llu\n", (ino64_t)ino);
  356. abort();
  357. }
  358. if (ip->I_initialized)
  359. return ip;
  360. unixfs_inodelayer_ifailed(ip);
  361. return NULL;
  362. }
  363. static void
  364. unixfs_internal_iput(struct inode* ip)
  365. {
  366. unixfs_inodelayer_iput(ip);
  367. }
  368. static int
  369. unixfs_internal_igetattr(ino_t ino, struct stat* stbuf)
  370. {
  371. if (ino == MACFUSE_ROOTINO)
  372. ino = ROOTINO;
  373. struct inode* ip = unixfs_internal_iget(ino);
  374. if (!ip)
  375. return ENOENT;
  376. unixfs_internal_istat(ip, stbuf);
  377. unixfs_internal_iput(ip);
  378. return 0;
  379. }
  380. static void
  381. unixfs_internal_istat(struct inode* ip, struct stat* stbuf)
  382. {
  383. memcpy(stbuf, &ip->I_stat, sizeof(struct stat));
  384. }
  385. static int
  386. unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
  387. {
  388. if (parentino == MACFUSE_ROOTINO)
  389. parentino = ROOTINO;
  390. stbuf->st_ino = 0;
  391. struct inode* dp = unixfs_internal_iget(parentino);
  392. if (!dp)
  393. return ENOENT;
  394. if (!S_ISDIR(dp->I_mode)) {
  395. unixfs_internal_iput(dp);
  396. return ENOTDIR;
  397. }
  398. int ret = ENOENT, eo = 0, count = dp->I_size / unixfs->s_dentsize;
  399. a_int offset = 0;
  400. char ubuf[UNIXFS_IOSIZE(unixfs)];
  401. struct dent udent;
  402. eloop:
  403. if (count == 0) {
  404. ret = ENOENT;
  405. goto out;
  406. }
  407. if ((offset & BMASK) == 0) {
  408. off_t blkno = unixfs_internal_bmap(dp, (off_t)(offset/BSIZE), &ret);
  409. if (UNIXFS_BADBLOCK(blkno, ret))
  410. goto out;
  411. if (unixfs_internal_bread(blkno, ubuf) != 0)
  412. goto out;
  413. }
  414. memset(&udent, 0, sizeof(udent));
  415. memcpy(&udent, ubuf + (offset & BMASK), unixfs->s_dentsize);
  416. udent.u_ino = fs16_to_host(unixfs->s_endian, udent.u_ino);
  417. offset += unixfs->s_dentsize;
  418. count--;
  419. if (udent.u_ino == 0) {
  420. if (eo == 0)
  421. eo = offset;
  422. goto eloop;
  423. }
  424. if (strncmp(name, udent.u_name, DIRSIZ) != 0)
  425. goto eloop;
  426. /* matched */
  427. ret = unixfs_internal_igetattr((ino_t)(udent.u_ino), stbuf);
  428. out:
  429. unixfs_internal_iput(dp);
  430. return ret;
  431. }
  432. static int
  433. unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
  434. off_t* offset, struct unixfs_direntry* dent)
  435. {
  436. if ((*offset + unixfs->s_dentsize) > dp->I_size)
  437. return -1;
  438. if (!dirbuf->flags.initialized || ((*offset & BMASK) == 0)) {
  439. int ret;
  440. off_t blkno = unixfs_internal_bmap(dp, (off_t)(*offset/BSIZE), &ret);
  441. if (UNIXFS_BADBLOCK(blkno, ret))
  442. return ret;
  443. ret = unixfs_internal_bread(blkno, dirbuf->data);
  444. if (ret != 0)
  445. return ret;
  446. dirbuf->flags.initialized = 1;
  447. }
  448. struct dent udent;
  449. size_t dirnamelen = min(DIRSIZ, UNIXFS_MAXNAMLEN);
  450. memset(&udent, 0, sizeof(udent));
  451. memcpy(&udent, dirbuf->data + (*offset & BMASK), unixfs->s_dentsize);
  452. udent.u_ino = fs16_to_host(unixfs->s_endian, udent.u_ino);
  453. dent->ino = udent.u_ino;
  454. memcpy(dent->name, udent.u_name, dirnamelen);
  455. dent->name[dirnamelen] = '\0';
  456. if (!udent.u_ino &&
  457. !BIT_ON(udent.u_ino,
  458. ((struct filsys*)(unixfs->s_fs_info))->s_dumpmap)) {
  459. dent->ino = 0;
  460. }
  461. *offset += unixfs->s_dentsize;
  462. return 0;
  463. }
  464. static ssize_t
  465. unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
  466. int* error)
  467. {
  468. ssize_t done = 0;
  469. size_t tomove = 0;
  470. ssize_t remaining = nbyte;
  471. ssize_t iosize = UNIXFS_IOSIZE(unixfs);
  472. char blkbuf[iosize];
  473. char* p = buf;
  474. while (remaining > 0) {
  475. off_t lbn = offset / BSIZE;
  476. off_t bn = unixfs_internal_bmap(ip, lbn, error);
  477. if (UNIXFS_BADBLOCK(bn, *error))
  478. break;
  479. *error = unixfs_internal_bread(bn, blkbuf);
  480. if (*error != 0)
  481. break;
  482. tomove = (remaining > iosize) ? iosize : remaining;
  483. memcpy(p, blkbuf, tomove);
  484. remaining -= tomove;
  485. done += tomove;
  486. offset += tomove;
  487. p += tomove;
  488. }
  489. if ((done == 0) && *error)
  490. return -1;
  491. return done;
  492. }
  493. static int
  494. unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
  495. {
  496. struct inode* ip = unixfs_internal_iget(ino);
  497. if (!ip)
  498. return ENOENT;
  499. int error;
  500. off_t bn = unixfs_internal_bmap(ip, (off_t)0, &error);
  501. if (UNIXFS_BADBLOCK(bn, error))
  502. goto out;
  503. /* we know MAXPATHLEN (256) < BSIZE (512/1024) == UNIXFS_MAXPATHLEN */
  504. error = unixfs_internal_bread(bn, path);
  505. if (error)
  506. goto out;
  507. size_t linklen = (ip->I_size > BSIZE - 1) ? BSIZE - 1: ip->I_size;
  508. path[linklen] = '\0';
  509. error = 0;
  510. out:
  511. unixfs_internal_iput(ip);
  512. return error;
  513. }
  514. static int
  515. unixfs_internal_sanitycheck(void* filsys, off_t disksize)
  516. {
  517. return 0;
  518. }
  519. static int
  520. unixfs_internal_statvfs(struct statvfs* svb)
  521. {
  522. memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
  523. return 0;
  524. }