/filesystems/unixfs/ancientfs/ancientfs_tar.c

http://macfuse.googlecode.com/ · C · 682 lines · 535 code · 134 blank · 13 comment · 107 complexity · 3b5b9c693f7672e200c8ac652da5512c MD5 · raw file

  1. /*
  2. * Ancient UNIX File Systems for MacFUSE
  3. * Amit Singh
  4. * http://osxbook.com
  5. */
  6. #include "ancientfs_tar.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. DECL_UNIXFS("UNIX V7 tar/ustar", tar);
  17. #define DECIMAL 10
  18. #define OCTAL 8
  19. #define TAR_ATOI(from, to, len, base) { \
  20. memmove(buf, from, len); \
  21. buf[len] = '\0'; \
  22. to = strtol(buf, (char **)NULL, base); \
  23. }
  24. struct tar_entry {
  25. char name[UNIXFS_MAXPATHLEN + 1];
  26. char linktargetname[UNIXFS_MAXPATHLEN + 1];
  27. struct stat stat;
  28. };
  29. static int ancientfs_tar_readheader(int fd, struct tar_entry* te);
  30. static int ancientfs_tar_chksum(union hblock* hb);
  31. int
  32. ancientfs_tar_chksum(union hblock* hb)
  33. {
  34. char* cp;
  35. for (cp = hb->dbuf.chksum; cp < &hb->dbuf.chksum[sizeof(hb->dbuf.chksum)];
  36. cp++)
  37. *cp = ' ';
  38. int i = 0;
  39. for (cp = hb->dummy; cp < &hb->dummy[TBLOCK]; cp++)
  40. i += *cp;
  41. return i;
  42. }
  43. static int
  44. ancientfs_tar_readheader(int fd, struct tar_entry* te)
  45. {
  46. static int cksum_failed = 0;
  47. int nr, ustar;
  48. char buf[20];
  49. char hb[sizeof(union hblock) + 1];
  50. struct header* hdr;
  51. retry:
  52. ustar = unixfs->s_flags & ANCIENTFS_USTAR;
  53. nr = read(fd, hb, sizeof(union hblock));
  54. if (nr != sizeof(union hblock)) {
  55. if (!nr)
  56. return 1;
  57. if (nr < 0)
  58. return -1;
  59. }
  60. hdr = &((union hblock*)hb)->dbuf;
  61. long chksum = 0;
  62. TAR_ATOI(hdr->chksum, chksum, sizeof(hdr->chksum), OCTAL);
  63. if (chksum != ancientfs_tar_chksum((union hblock*)hb)) {
  64. cksum_failed++;
  65. if (!(cksum_failed % 10))
  66. fprintf(stderr,
  67. "*** warning: checksum failed (%d failures so far)\n",
  68. cksum_failed);
  69. goto retry;
  70. }
  71. memset(te, 0, sizeof(*te));
  72. TAR_ATOI(hdr->mode, te->stat.st_mode, sizeof(hdr->mode), OCTAL);
  73. TAR_ATOI(hdr->uid, te->stat.st_uid, sizeof(hdr->uid), OCTAL);
  74. te->stat.st_mode = ancientfs_tar_mode(te->stat.st_mode, unixfs->s_flags);
  75. char tbuf[16];
  76. memset(tbuf, 0, 16);
  77. memcpy(tbuf, hdr->size, 12);
  78. TAR_ATOI(tbuf, te->stat.st_size, 16, OCTAL);
  79. memset(tbuf, 0, 16);
  80. memcpy(tbuf, hdr->mtime, 12);
  81. TAR_ATOI(tbuf, te->stat.st_mtime, 16, OCTAL);
  82. te->stat.st_atime = te->stat.st_ctime = te->stat.st_mtime;
  83. if ((hdr->typeflag == TARTYPE_SYM) || (hdr->typeflag == TARTYPE_LNK)) {
  84. /* translate hard link to symbolic link */
  85. te->stat.st_mode |= S_IFLNK;
  86. memcpy(te->linktargetname, hdr->linkname, 100);
  87. te->stat.st_size = strlen(te->linktargetname);
  88. } else {
  89. switch (hdr->typeflag) {
  90. case 0:
  91. case TARTYPE_REG:
  92. te->stat.st_mode |= S_IFREG;
  93. break;
  94. case TARTYPE_SYM:
  95. case TARTYPE_LNK:
  96. te->stat.st_mode |= S_IFLNK;
  97. break;
  98. case TARTYPE_CHR:
  99. te->stat.st_mode |= S_IFCHR;
  100. break;
  101. case TARTYPE_BLK:
  102. te->stat.st_mode |= S_IFBLK;
  103. break;
  104. case TARTYPE_DIR:
  105. te->stat.st_mode |= S_IFDIR;
  106. break;
  107. case TARTYPE_FIFO:
  108. te->stat.st_mode |= S_IFIFO;
  109. break;
  110. }
  111. }
  112. if (!ustar) {
  113. if (hdr->name[99] == '/') /* full field used */
  114. te->stat.st_mode = S_IFDIR | (uint16_t)(te->stat.st_mode & 07777);
  115. else {
  116. size_t len = strlen(hdr->name);
  117. if (hdr->name[len - 1] == '/')
  118. te->stat.st_mode =
  119. S_IFDIR | (uint16_t)(te->stat.st_mode & 07777);
  120. }
  121. memcpy(te->name, hdr->name, 100);
  122. te->name[100] = '\0';
  123. } else { /* ustar */
  124. if (hdr->prefix[0]) {
  125. memcpy(te->name, hdr->name, 100);
  126. te->name[100] = '\0';
  127. } else {
  128. if (hdr->prefix[154]) {
  129. memcpy(te->name, hdr->prefix, 155);
  130. memcpy(te->name + 155, hdr->name, 100);
  131. te->name[255] = '\0';
  132. } else {
  133. int n = snprintf(te->name, UNIXFS_MAXPATHLEN, "%s",
  134. hdr->prefix);
  135. memcpy(te->name + n, hdr->name, 100);
  136. te->name[n + 99] = '\0';
  137. }
  138. }
  139. }
  140. uint16_t dmajor;
  141. uint16_t dminor;
  142. TAR_ATOI(hdr->devmajor, dmajor, sizeof(hdr->devmajor), OCTAL);
  143. TAR_ATOI(hdr->devminor, dminor, sizeof(hdr->devminor), OCTAL);
  144. te->stat.st_rdev = makedev(dmajor, dminor);
  145. te->stat.st_nlink = 1;
  146. return 0;
  147. }
  148. static void*
  149. unixfs_internal_init(const char* dmg, uint32_t flags, fs_endian_t fse,
  150. char** fsname, char** volname)
  151. {
  152. int fd = -1;
  153. if ((fd = open(dmg, O_RDONLY)) < 0) {
  154. perror("open");
  155. return NULL;
  156. }
  157. int err;
  158. struct stat stbuf;
  159. struct super_block* sb = (struct super_block*)0;
  160. struct filsys* fs = (struct filsys*)0;
  161. if ((err = fstat(fd, &stbuf)) != 0) {
  162. perror("fstat");
  163. goto out;
  164. }
  165. if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
  166. err = EINVAL;
  167. fprintf(stderr, "%s is not a tape image file\n", dmg);
  168. goto out;
  169. }
  170. char hb[sizeof(union hblock) + 1];
  171. if (read(fd, hb, sizeof(union hblock)) != sizeof(union hblock)) {
  172. fprintf(stderr, "failed to read data from file\n");
  173. err = EIO;
  174. goto out;
  175. }
  176. char* magic = (((union hblock*)hb)->dbuf).magic;
  177. if (memcmp(magic, TMAGIC, TMAGLEN - 1) == 0) {
  178. flags |= ANCIENTFS_USTAR;
  179. if (magic[6] == ' ')
  180. fprintf(stderr, "*** warning: pre-POSIX ustar archive\n");
  181. } else {
  182. flags |= ANCIENTFS_V7TAR;
  183. fprintf(stderr, "*** warning: not ustar; assuming ancient tar\n");
  184. }
  185. sb = malloc(sizeof(struct super_block));
  186. if (!sb) {
  187. err = ENOMEM;
  188. goto out;
  189. }
  190. assert(sizeof(struct filsys) <= TBLOCK);
  191. fs = calloc(1, TBLOCK);
  192. if (!fs) {
  193. free(sb);
  194. err = ENOMEM;
  195. goto out;
  196. }
  197. unixfs = sb;
  198. unixfs->s_flags = flags;
  199. /* not used */
  200. unixfs->s_endian = (fse == UNIXFS_FS_INVALID) ? UNIXFS_FS_LITTLE : fse;
  201. unixfs->s_fs_info = (void*)fs;
  202. unixfs->s_bdev = fd;
  203. /* must initialize the inode layer before sanity checking */
  204. if ((err = unixfs_inodelayer_init(sizeof(struct tar_node_info))) != 0)
  205. goto out;
  206. struct inode* rootip = unixfs_inodelayer_iget((ino_t)ROOTINO);
  207. if (!rootip) {
  208. fprintf(stderr, "*** fatal error: no root inode\n");
  209. abort();
  210. }
  211. rootip->I_mode = S_IFDIR | 0755;
  212. rootip->I_uid = getuid();
  213. rootip->I_gid = getgid();
  214. rootip->I_size = 2;
  215. rootip->I_atime_sec = rootip->I_mtime_sec = rootip->I_ctime_sec = time(0);
  216. struct tar_node_info* rootti = (struct tar_node_info*)rootip->I_private;
  217. rootti->ti_self = rootip;
  218. rootti->ti_parent = NULL;
  219. rootti->ti_children = NULL;
  220. rootti->ti_next_sibling = NULL;
  221. unixfs_inodelayer_isucceeded(rootip);
  222. fs->s_fsize = stbuf.st_size / TBLOCK;
  223. fs->s_files = 0;
  224. fs->s_directories = 1 + 1 + 1;
  225. fs->s_rootip = rootip;
  226. fs->s_lastino = ROOTINO;
  227. lseek(fd, (off_t)0, SEEK_SET); /* rewind tape */
  228. struct tar_entry _te, *te = &_te;
  229. for (;;) {
  230. off_t toseek = 0;
  231. if ((err = ancientfs_tar_readheader(fd, te)) != 0) {
  232. if (err == 1)
  233. break;
  234. else {
  235. fprintf(stderr,
  236. "*** fatal error: cannot read block (error %d)\n", err);
  237. err = EIO;
  238. goto out;
  239. }
  240. }
  241. char* path = te->name;
  242. ino_t parent_ino = ROOTINO;
  243. size_t pathlen = strlen(te->name);
  244. if ((*path == '.') && ((pathlen == 1) ||
  245. ((pathlen == 2) && (*(path + 1) == '/')))) {
  246. /* root */
  247. rootip->I_mode = te->stat.st_mode;
  248. rootip->I_atime_sec = \
  249. rootip->I_mtime_sec = \
  250. rootip->I_ctime_sec = te->stat.st_mtime;
  251. continue;
  252. }
  253. /* we don't deal with many fancy paths here: just '/' and './' */
  254. if (*path == '/')
  255. path++;
  256. else if (*path == '.' && *(path + 1) == '/')
  257. path += 2;
  258. char *cnp, *term;
  259. for (cnp = strtok_r(path, "/", &term); cnp;
  260. cnp = strtok_r(NULL, "/", &term)) {
  261. /* we have { parent_ino, cnp } */
  262. struct stat stbuf;
  263. int missing = unixfs_internal_namei(parent_ino, cnp, &stbuf);
  264. if (!missing) {
  265. parent_ino = stbuf.st_ino;
  266. continue;
  267. }
  268. struct inode* ip =
  269. unixfs_inodelayer_iget((ino_t)(fs->s_lastino + 1));
  270. if (!ip) {
  271. fprintf(stderr, "*** fatal error: no inode for %llu\n",
  272. (ino64_t)(fs->s_lastino + 1));
  273. abort();
  274. }
  275. ip->I_mode = te->stat.st_mode;
  276. ip->I_uid = te->stat.st_uid;
  277. ip->I_gid = te->stat.st_gid;
  278. ip->I_size = te->stat.st_size;
  279. ip->I_nlink = te->stat.st_nlink;
  280. ip->I_rdev = te->stat.st_rdev;
  281. ip->I_atime_sec = ip->I_mtime_sec = ip->I_ctime_sec =
  282. te->stat.st_mtime;
  283. struct tar_node_info* ti = (struct tar_node_info*)ip->I_private;
  284. size_t namelen = strlen(cnp);
  285. ti->ti_name = malloc(namelen + 1);
  286. if (!ti->ti_name) {
  287. fprintf(stderr, "*** fatal error: cannot allocate memory\n");
  288. abort();
  289. }
  290. memcpy(ti->ti_name, cnp, namelen);
  291. ti->ti_name[namelen] = '\0';
  292. ip->I_daddr[0] = 0;
  293. if (S_ISLNK(ip->I_mode)) {
  294. namelen = strlen(te->linktargetname);
  295. ti->ti_linktargetname = malloc(namelen + 1);
  296. if (!ti->ti_name) {
  297. fprintf(stderr,
  298. "*** fatal error: cannot allocate memory\n");
  299. abort();
  300. }
  301. memcpy(ti->ti_linktargetname, te->linktargetname, namelen);
  302. ti->ti_linktargetname[namelen] = '\0';
  303. } else if (S_ISREG(ip->I_mode)) {
  304. ip->I_daddr[0] = (uint32_t)lseek(fd, (off_t)0, SEEK_CUR);
  305. toseek = ip->I_size;
  306. }
  307. ti->ti_self = ip;
  308. ti->ti_children = NULL;
  309. struct inode* parent_ip = unixfs_internal_iget(parent_ino);
  310. parent_ip->I_size += 1;
  311. ti->ti_parent = (struct tar_node_info*)(parent_ip->I_private);
  312. ti->ti_next_sibling = ti->ti_parent->ti_children;
  313. ti->ti_parent->ti_children = ti;
  314. if (S_ISDIR(ip->I_mode)) {
  315. fs->s_directories++;
  316. parent_ino = fs->s_lastino + 1;
  317. ip->I_size = 2;
  318. } else
  319. fs->s_files++;
  320. fs->s_lastino++;
  321. unixfs_internal_iput(parent_ip);
  322. unixfs_inodelayer_isucceeded(ip);
  323. /* no put */
  324. } /* for each component */
  325. if (toseek) {
  326. toseek = (toseek + TBLOCK - 1)/TBLOCK;
  327. toseek *= TBLOCK;
  328. (void)lseek(fd, (off_t)toseek, SEEK_CUR);
  329. }
  330. } /* for each block */
  331. err = 0;
  332. unixfs->s_statvfs.f_bsize = TBLOCK;
  333. unixfs->s_statvfs.f_frsize = TBLOCK;
  334. unixfs->s_statvfs.f_ffree = 0;
  335. unixfs->s_statvfs.f_files = fs->s_files + fs->s_directories;
  336. unixfs->s_statvfs.f_blocks = fs->s_fsize;
  337. unixfs->s_statvfs.f_bfree = 0;
  338. unixfs->s_statvfs.f_bavail = 0;
  339. unixfs->s_dentsize = 1;
  340. unixfs->s_statvfs.f_namemax = UNIXFS_MAXNAMLEN;
  341. snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "UNIX %star",
  342. (unixfs->s_flags & ANCIENTFS_V7TAR) ? "V7 " : "us");
  343. char* dmg_basename = basename((char*)dmg);
  344. snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (tape=%s)",
  345. unixfs_fstype, (dmg_basename) ? dmg_basename : "Tar Image");
  346. *fsname = unixfs->s_fsname;
  347. *volname = unixfs->s_volname;
  348. out:
  349. if (err) {
  350. if (fd >= 0)
  351. close(fd);
  352. if (fs)
  353. free(fs);
  354. if (sb)
  355. free(sb);
  356. return NULL;
  357. }
  358. return sb;
  359. }
  360. static void
  361. unixfs_internal_fini(void* filsys)
  362. {
  363. struct super_block* sb = (struct super_block*)filsys;
  364. struct filsys* fs = (struct filsys*)sb->s_fs_info;
  365. ino_t i = fs->s_lastino;
  366. for (; i >= ROOTINO; i--) {
  367. struct inode* tmp = unixfs_internal_iget(i);
  368. if (tmp) {
  369. struct tar_node_info* ti = (struct tar_node_info*)tmp->I_private;
  370. if (ti) {
  371. free(ti->ti_name);
  372. if (ti->ti_linktargetname)
  373. free(ti->ti_linktargetname);
  374. }
  375. unixfs_internal_iput(tmp);
  376. unixfs_internal_iput(tmp);
  377. }
  378. }
  379. unixfs_inodelayer_fini();
  380. if (sb) {
  381. if (sb->s_bdev >= 0)
  382. close(sb->s_bdev);
  383. sb->s_bdev = -1;
  384. if (sb->s_fs_info)
  385. free(sb->s_fs_info);
  386. }
  387. }
  388. static off_t
  389. unixfs_internal_alloc(void)
  390. {
  391. return (off_t)0;
  392. }
  393. static off_t
  394. unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error)
  395. {
  396. return (off_t)0;
  397. }
  398. static int
  399. unixfs_internal_bread(off_t blkno, char* blkbuf)
  400. {
  401. return EIO;
  402. }
  403. static struct inode*
  404. unixfs_internal_iget(ino_t ino)
  405. {
  406. struct inode* ip = unixfs_inodelayer_iget(ino);
  407. if (!ip) {
  408. fprintf(stderr, "*** fatal error: no inode for %llu\n", (ino64_t)ino);
  409. abort();
  410. }
  411. if (ip->I_initialized)
  412. return ip;
  413. unixfs_inodelayer_ifailed(ip);
  414. return NULL;
  415. }
  416. static void
  417. unixfs_internal_iput(struct inode* ip)
  418. {
  419. unixfs_inodelayer_iput(ip);
  420. }
  421. static int
  422. unixfs_internal_igetattr(ino_t ino, struct stat* stbuf)
  423. {
  424. struct inode* ip = unixfs_internal_iget(ino);
  425. if (!ip)
  426. return ENOENT;
  427. unixfs_internal_istat(ip, stbuf);
  428. unixfs_internal_iput(ip);
  429. return 0;
  430. }
  431. static void
  432. unixfs_internal_istat(struct inode* ip, struct stat* stbuf)
  433. {
  434. memcpy(stbuf, &ip->I_stat, sizeof(struct stat));
  435. }
  436. static int
  437. unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
  438. {
  439. int ret = ENOENT;
  440. stbuf->st_ino = 0;
  441. size_t namelen = strlen(name);
  442. if (namelen > UNIXFS_MAXNAMLEN)
  443. return ENAMETOOLONG;
  444. struct inode* dp = unixfs_internal_iget(parentino);
  445. if (!dp)
  446. return ENOENT;
  447. if (!S_ISDIR(dp->I_mode)) {
  448. ret = ENOTDIR;
  449. goto out;
  450. }
  451. struct tar_node_info* child =
  452. ((struct tar_node_info*)dp->I_private)->ti_children;;
  453. if (!child) {
  454. ret = ENOENT;
  455. goto out;
  456. }
  457. int found = 0;
  458. do {
  459. size_t target_namelen = strlen((const char*)child->ti_name);
  460. if ((namelen == target_namelen) &&
  461. (memcmp(name, child->ti_name, target_namelen) == 0)) {
  462. found = 1;
  463. break;
  464. }
  465. child = child->ti_next_sibling;
  466. } while (child);
  467. if (found)
  468. ret = unixfs_internal_igetattr((ino_t)child->ti_self->I_ino, stbuf);
  469. out:
  470. unixfs_internal_iput(dp);
  471. return ret;
  472. }
  473. static int
  474. unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
  475. off_t* offset, struct unixfs_direntry* dent)
  476. {
  477. if (*offset >= dp->I_size)
  478. return -1;
  479. if (*offset < 2) {
  480. int idx = 0;
  481. dent->name[idx++] = '.';
  482. dent->ino = ROOTINO;
  483. if (*offset == 1) {
  484. if (dp->I_ino != ROOTINO) {
  485. struct inode* pdp = unixfs_internal_iget(dp->I_ino);
  486. if (pdp) {
  487. dent->ino = pdp->I_ino;
  488. unixfs_internal_iput(pdp);
  489. }
  490. }
  491. dent->name[idx++] = '.';
  492. }
  493. dent->name[idx++] = '\0';
  494. goto out;
  495. }
  496. struct tar_node_info* child =
  497. ((struct tar_node_info*)dp->I_private)->ti_children;;
  498. off_t i;
  499. for (i = 0; i < (*offset - 2); i++)
  500. child = child->ti_next_sibling;
  501. dent->ino = (ino_t)child->ti_self->I_ino;
  502. size_t dirnamelen = strlen(child->ti_name);
  503. dirnamelen = min(dirnamelen, UNIXFS_MAXNAMLEN);
  504. memcpy(dent->name, child->ti_name, dirnamelen);
  505. dent->name[dirnamelen] = '\0';
  506. out:
  507. *offset += 1;
  508. return 0;
  509. }
  510. static ssize_t
  511. unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
  512. int* error)
  513. {
  514. off_t start = (off_t)ip->I_daddr[0];
  515. /* caller already checked for bounds */
  516. return pread(unixfs->s_bdev, buf, nbyte, start + offset);
  517. }
  518. static int
  519. unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
  520. {
  521. struct inode* ip = unixfs_internal_iget(ino);
  522. if (!ip)
  523. return ENOENT;
  524. int error;
  525. struct tar_node_info* ti = (struct tar_node_info*)ip->I_private;
  526. if (!ti->ti_linktargetname) {
  527. error = ENOENT;
  528. goto out;
  529. }
  530. size_t linklen = min(ip->I_size, TBLOCK);
  531. memcpy(path, ti->ti_linktargetname, linklen);
  532. path[linklen] = '\0';
  533. error = 0;
  534. out:
  535. unixfs_internal_iput(ip);
  536. return error;
  537. }
  538. static int
  539. unixfs_internal_sanitycheck(void* filsys, off_t disksize)
  540. {
  541. return 0;
  542. }
  543. static int
  544. unixfs_internal_statvfs(struct statvfs* svb)
  545. {
  546. memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
  547. return 0;
  548. }