/filesystems/unixfs/ancientfs/ancientfs_dumpvn.c

http://macfuse.googlecode.com/ · C · 688 lines · 555 code · 122 blank · 11 comment · 125 complexity · a3d0d3b57621e3b2b778de01b993c567 MD5 · raw file

  1. /*
  2. * Ancient UNIX File Systems for MacFUSE
  3. * Amit Singh
  4. * http://osxbook.com
  5. */
  6. #include "ancientfs_dumpvn.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 variable-length names", dumpvn1024);
  19. #else
  20. DECL_UNIXFS("UNIX dump/restor variable-length names", dumpvn);
  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. #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
  386. static int
  387. __unixfs_internal_blkatoff(struct inode* ip, off_t offset, char* ubuf)
  388. {
  389. int error;
  390. off_t lbn = lblkno(offset);
  391. off_t bn = unixfs_internal_bmap(ip, lbn, &error);
  392. if (bn == 0)
  393. return error;
  394. return unixfs_internal_bread(bn, ubuf);
  395. }
  396. static int
  397. __unixfs_internal_dirbadentry(struct direct* ep, int entryoffsetinblock)
  398. {
  399. int i;
  400. if ((ep->d_reclen & 0x3) != 0 ||
  401. ep->d_reclen > ANCIENTFS_211BSD_DIRBLKSIZ - \
  402. (entryoffsetinblock & (ANCIENTFS_211BSD_DIRBLKSIZ - 1)) ||
  403. ep->d_reclen < ANCIENTFS_211BSD_DIRSIZ(ep) ||
  404. ep->d_namlen > UNIXFS_MAXNAMLEN)
  405. return 1;
  406. for (i = 0; i < ep->d_namlen; i++)
  407. if (ep->d_name[i] == '\0')
  408. return 1;
  409. return ep->d_name[i];
  410. }
  411. static int
  412. unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
  413. {
  414. if (parentino == MACFUSE_ROOTINO)
  415. parentino = ROOTINO;
  416. stbuf->st_ino = 0;
  417. struct inode* dp = unixfs_internal_iget(parentino);
  418. if (!dp)
  419. return ENOENT;
  420. if (!S_ISDIR(dp->I_mode)) {
  421. unixfs_internal_iput(dp);
  422. return ENOTDIR;
  423. }
  424. int ret = ENOENT, i, found = 0;
  425. off_t ni_offset = 0, entryoffsetinblock = 0;
  426. int endsearch = roundup(dp->I_size, ANCIENTFS_211BSD_DIRBLKSIZ);
  427. struct direct* ep;
  428. char ubuf[UNIXFS_IOSIZE(unixfs)];
  429. size_t namlen = strlen(name);
  430. while (ni_offset < endsearch) {
  431. if (blkoff(ni_offset) == 0) {
  432. ret = __unixfs_internal_blkatoff(dp, ni_offset, ubuf);
  433. if (ret) {
  434. ret = ENOENT;
  435. goto out;
  436. }
  437. entryoffsetinblock = 0;
  438. }
  439. ep = (struct direct*)((char*)ubuf + entryoffsetinblock);
  440. ep->d_ino = fs16_to_host(unixfs->s_endian, ep->d_ino);
  441. ep->d_reclen = fs16_to_host(unixfs->s_endian, ep->d_reclen);
  442. ep->d_namlen = fs16_to_host(unixfs->s_endian, ep->d_namlen);
  443. if (ep->d_reclen == 0 ||
  444. __unixfs_internal_dirbadentry(ep, entryoffsetinblock)) {
  445. i = ANCIENTFS_211BSD_DIRBLKSIZ -
  446. (entryoffsetinblock & (ANCIENTFS_211BSD_DIRBLKSIZ - 1));
  447. ni_offset += i;
  448. entryoffsetinblock += i;
  449. continue;
  450. }
  451. if (ep->d_ino) {
  452. if ((namlen == ep->d_namlen) &&
  453. bcmp(name, ep->d_name, ep->d_namlen) == 0) {
  454. found = 1;
  455. break;
  456. }
  457. }
  458. ni_offset += ep->d_reclen;
  459. entryoffsetinblock += ep->d_reclen;
  460. } /* while */
  461. out:
  462. unixfs_internal_iput(dp);
  463. if (found) /* matched */
  464. ret = unixfs_internal_igetattr((ino_t)(ep->d_ino), stbuf);
  465. return ret;
  466. }
  467. static int
  468. unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
  469. off_t* offset, struct unixfs_direntry* dent)
  470. {
  471. struct direct* ep;
  472. off_t ni_offset = *offset;
  473. off_t entryoffsetinblock = blkoff(ni_offset);
  474. int endsearch = roundup(dp->I_size, ANCIENTFS_211BSD_DIRBLKSIZ);
  475. if (ni_offset >= endsearch)
  476. return -1;
  477. if (!dirbuf->flags.initialized || (blkoff(ni_offset) == 0)) {
  478. int ret = __unixfs_internal_blkatoff(dp, ni_offset, dirbuf->data);
  479. if (ret)
  480. return ret;
  481. entryoffsetinblock = 0;
  482. dirbuf->flags.initialized = 1;
  483. }
  484. ep = (struct direct*)((char*)dirbuf->data + entryoffsetinblock);
  485. ep->d_ino = fs16_to_host(unixfs->s_endian, ep->d_ino);
  486. ep->d_reclen = fs16_to_host(unixfs->s_endian, ep->d_reclen);
  487. ep->d_namlen = fs16_to_host(unixfs->s_endian, ep->d_namlen);
  488. if (ep->d_reclen == 0 ||
  489. __unixfs_internal_dirbadentry(ep, entryoffsetinblock)) {
  490. int i =
  491. ANCIENTFS_211BSD_DIRBLKSIZ - (entryoffsetinblock &
  492. (ANCIENTFS_211BSD_DIRBLKSIZ - 1));
  493. ni_offset += i;
  494. entryoffsetinblock += i;
  495. dent->ino = 0;
  496. } else {
  497. dent->ino = (ino_t)ep->d_ino;
  498. memcpy(dent->name, ep->d_name, ep->d_namlen);
  499. dent->name[ep->d_namlen] = '\0';
  500. ni_offset += ep->d_reclen;
  501. entryoffsetinblock += ep->d_reclen;
  502. }
  503. *offset = ni_offset;
  504. return 0;
  505. }
  506. static ssize_t
  507. unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
  508. int* error)
  509. {
  510. ssize_t done = 0;
  511. size_t tomove = 0;
  512. ssize_t remaining = nbyte;
  513. ssize_t iosize = UNIXFS_IOSIZE(unixfs);
  514. char blkbuf[iosize];
  515. char* p = buf;
  516. while (remaining > 0) {
  517. off_t lbn = offset / BSIZE;
  518. off_t bn = unixfs_internal_bmap(ip, lbn, error);
  519. if (UNIXFS_BADBLOCK(bn, *error))
  520. break;
  521. *error = unixfs_internal_bread(bn, blkbuf);
  522. if (*error != 0)
  523. break;
  524. tomove = (remaining > iosize) ? iosize : remaining;
  525. memcpy(p, blkbuf, tomove);
  526. remaining -= tomove;
  527. done += tomove;
  528. offset += tomove;
  529. p += tomove;
  530. }
  531. if ((done == 0) && *error)
  532. return -1;
  533. return done;
  534. }
  535. static int
  536. unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
  537. {
  538. struct inode* ip = unixfs_internal_iget(ino);
  539. if (!ip)
  540. return ENOENT;
  541. int error;
  542. off_t bn = unixfs_internal_bmap(ip, (off_t)0, &error);
  543. if (UNIXFS_BADBLOCK(bn, error))
  544. goto out;
  545. /* we know MAXPATHLEN (256) < BSIZE (512/1024) == UNIXFS_MAXPATHLEN */
  546. error = unixfs_internal_bread(bn, path);
  547. if (error)
  548. goto out;
  549. size_t linklen = (ip->I_size > BSIZE - 1) ? BSIZE - 1: ip->I_size;
  550. path[linklen] = '\0';
  551. error = 0;
  552. out:
  553. unixfs_internal_iput(ip);
  554. return error;
  555. }
  556. static int
  557. unixfs_internal_sanitycheck(void* filsys, off_t disksize)
  558. {
  559. return 0;
  560. }
  561. static int
  562. unixfs_internal_statvfs(struct statvfs* svb)
  563. {
  564. memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
  565. return 0;
  566. }