/filesystems/loopback/loopback.c

http://macfuse.googlecode.com/ · C · 756 lines · 576 code · 158 blank · 22 comment · 110 complexity · 017ce370291472d6f69b51ececc259da MD5 · raw file

  1. /*
  2. FUSE: Filesystem in Userspace
  3. Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
  4. This program can be distributed under the terms of the GNU GPL.
  5. See the file COPYING.
  6. */
  7. /*
  8. * Loopback MacFUSE file system in C. Uses the high-level FUSE API.
  9. * Based on the fusexmp_fh.c example from the Linux FUSE distribution.
  10. * Amit Singh <http://osxbook.com>
  11. */
  12. #include <AvailabilityMacros.h>
  13. #if !defined(AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER)
  14. #error "This file system requires Leopard and above."
  15. #endif
  16. #define FUSE_USE_VERSION 26
  17. #define _GNU_SOURCE
  18. #include <fuse.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24. #include <dirent.h>
  25. #include <errno.h>
  26. #include <sys/time.h>
  27. #include <sys/xattr.h>
  28. #include <sys/attr.h>
  29. #include <sys/param.h>
  30. #if defined(_POSIX_C_SOURCE)
  31. typedef unsigned char u_char;
  32. typedef unsigned short u_short;
  33. typedef unsigned int u_int;
  34. typedef unsigned long u_long;
  35. #endif
  36. #define G_PREFIX "org"
  37. #define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security"
  38. #define A_PREFIX "com"
  39. #define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security"
  40. #define XATTR_APPLE_PREFIX "com.apple."
  41. static int
  42. loopback_getattr(const char *path, struct stat *stbuf)
  43. {
  44. int res;
  45. res = lstat(path, stbuf);
  46. if (res == -1) {
  47. return -errno;
  48. }
  49. return 0;
  50. }
  51. static int
  52. loopback_fgetattr(const char *path, struct stat *stbuf,
  53. struct fuse_file_info *fi)
  54. {
  55. int res;
  56. (void)path;
  57. res = fstat(fi->fh, stbuf);
  58. if (res == -1) {
  59. return -errno;
  60. }
  61. return 0;
  62. }
  63. static int
  64. loopback_readlink(const char *path, char *buf, size_t size)
  65. {
  66. int res;
  67. res = readlink(path, buf, size - 1);
  68. if (res == -1) {
  69. return -errno;
  70. }
  71. buf[res] = '\0';
  72. return 0;
  73. }
  74. struct loopback_dirp {
  75. DIR *dp;
  76. struct dirent *entry;
  77. off_t offset;
  78. };
  79. static int
  80. loopback_opendir(const char *path, struct fuse_file_info *fi)
  81. {
  82. int res;
  83. struct loopback_dirp *d = malloc(sizeof(struct loopback_dirp));
  84. if (d == NULL) {
  85. return -ENOMEM;
  86. }
  87. d->dp = opendir(path);
  88. if (d->dp == NULL) {
  89. res = -errno;
  90. free(d);
  91. return res;
  92. }
  93. d->offset = 0;
  94. d->entry = NULL;
  95. fi->fh = (unsigned long)d;
  96. return 0;
  97. }
  98. static inline struct loopback_dirp *
  99. get_dirp(struct fuse_file_info *fi)
  100. {
  101. return (struct loopback_dirp *)(uintptr_t)fi->fh;
  102. }
  103. static int
  104. loopback_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
  105. off_t offset, struct fuse_file_info *fi)
  106. {
  107. struct loopback_dirp *d = get_dirp(fi);
  108. (void)path;
  109. if (offset != d->offset) {
  110. seekdir(d->dp, offset);
  111. d->entry = NULL;
  112. d->offset = offset;
  113. }
  114. while (1) {
  115. struct stat st;
  116. off_t nextoff;
  117. if (!d->entry) {
  118. d->entry = readdir(d->dp);
  119. if (!d->entry) {
  120. break;
  121. }
  122. }
  123. memset(&st, 0, sizeof(st));
  124. st.st_ino = d->entry->d_ino;
  125. st.st_mode = d->entry->d_type << 12;
  126. nextoff = telldir(d->dp);
  127. if (filler(buf, d->entry->d_name, &st, nextoff)) {
  128. break;
  129. }
  130. d->entry = NULL;
  131. d->offset = nextoff;
  132. }
  133. return 0;
  134. }
  135. static int
  136. loopback_releasedir(const char *path, struct fuse_file_info *fi)
  137. {
  138. struct loopback_dirp *d = get_dirp(fi);
  139. (void)path;
  140. closedir(d->dp);
  141. free(d);
  142. return 0;
  143. }
  144. static int
  145. loopback_mknod(const char *path, mode_t mode, dev_t rdev)
  146. {
  147. int res;
  148. if (S_ISFIFO(mode)) {
  149. res = mkfifo(path, mode);
  150. } else {
  151. res = mknod(path, mode, rdev);
  152. }
  153. if (res == -1) {
  154. return -errno;
  155. }
  156. return 0;
  157. }
  158. static int
  159. loopback_mkdir(const char *path, mode_t mode)
  160. {
  161. int res;
  162. res = mkdir(path, mode);
  163. if (res == -1) {
  164. return -errno;
  165. }
  166. return 0;
  167. }
  168. static int
  169. loopback_unlink(const char *path)
  170. {
  171. int res;
  172. res = unlink(path);
  173. if (res == -1) {
  174. return -errno;
  175. }
  176. return 0;
  177. }
  178. static int
  179. loopback_rmdir(const char *path)
  180. {
  181. int res;
  182. res = rmdir(path);
  183. if (res == -1) {
  184. return -errno;
  185. }
  186. return 0;
  187. }
  188. static int
  189. loopback_symlink(const char *from, const char *to)
  190. {
  191. int res;
  192. res = symlink(from, to);
  193. if (res == -1) {
  194. return -errno;
  195. }
  196. return 0;
  197. }
  198. static int
  199. loopback_rename(const char *from, const char *to)
  200. {
  201. int res;
  202. res = rename(from, to);
  203. if (res == -1) {
  204. return -errno;
  205. }
  206. return 0;
  207. }
  208. static int
  209. loopback_exchange(const char *path1, const char *path2, unsigned long options)
  210. {
  211. int res;
  212. res = exchangedata(path1, path2, options);
  213. if (res == -1) {
  214. return -errno;
  215. }
  216. return 0;
  217. }
  218. static int
  219. loopback_link(const char *from, const char *to)
  220. {
  221. int res;
  222. res = link(from, to);
  223. if (res == -1) {
  224. return -errno;
  225. }
  226. return 0;
  227. }
  228. static int
  229. loopback_fsetattr_x(const char *path, struct setattr_x *attr,
  230. struct fuse_file_info *fi)
  231. {
  232. int res;
  233. uid_t uid = -1;
  234. gid_t gid = -1;
  235. if (SETATTR_WANTS_MODE(attr)) {
  236. res = lchmod(path, attr->mode);
  237. if (res == -1) {
  238. return -errno;
  239. }
  240. }
  241. if (SETATTR_WANTS_UID(attr)) {
  242. uid = attr->uid;
  243. }
  244. if (SETATTR_WANTS_GID(attr)) {
  245. gid = attr->gid;
  246. }
  247. if ((uid != -1) || (gid != -1)) {
  248. res = lchown(path, uid, gid);
  249. if (res == -1) {
  250. return -errno;
  251. }
  252. }
  253. if (SETATTR_WANTS_SIZE(attr)) {
  254. if (fi) {
  255. res = ftruncate(fi->fh, attr->size);
  256. } else {
  257. res = truncate(path, attr->size);
  258. }
  259. if (res == -1) {
  260. return -errno;
  261. }
  262. }
  263. if (SETATTR_WANTS_MODTIME(attr)) {
  264. struct timeval tv[2];
  265. if (!SETATTR_WANTS_ACCTIME(attr)) {
  266. gettimeofday(&tv[0], NULL);
  267. } else {
  268. tv[0].tv_sec = attr->acctime.tv_sec;
  269. tv[0].tv_usec = attr->acctime.tv_nsec / 1000;
  270. }
  271. tv[1].tv_sec = attr->modtime.tv_sec;
  272. tv[1].tv_usec = attr->modtime.tv_nsec / 1000;
  273. res = utimes(path, tv);
  274. if (res == -1) {
  275. return -errno;
  276. }
  277. }
  278. if (SETATTR_WANTS_CRTIME(attr)) {
  279. struct attrlist attributes;
  280. attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
  281. attributes.reserved = 0;
  282. attributes.commonattr = ATTR_CMN_CRTIME;
  283. attributes.dirattr = 0;
  284. attributes.fileattr = 0;
  285. attributes.forkattr = 0;
  286. attributes.volattr = 0;
  287. res = setattrlist(path, &attributes, &attr->crtime,
  288. sizeof(struct timespec), FSOPT_NOFOLLOW);
  289. if (res == -1) {
  290. return -errno;
  291. }
  292. }
  293. if (SETATTR_WANTS_CHGTIME(attr)) {
  294. struct attrlist attributes;
  295. attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
  296. attributes.reserved = 0;
  297. attributes.commonattr = ATTR_CMN_CHGTIME;
  298. attributes.dirattr = 0;
  299. attributes.fileattr = 0;
  300. attributes.forkattr = 0;
  301. attributes.volattr = 0;
  302. res = setattrlist(path, &attributes, &attr->chgtime,
  303. sizeof(struct timespec), FSOPT_NOFOLLOW);
  304. if (res == -1) {
  305. return -errno;
  306. }
  307. }
  308. if (SETATTR_WANTS_BKUPTIME(attr)) {
  309. struct attrlist attributes;
  310. attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
  311. attributes.reserved = 0;
  312. attributes.commonattr = ATTR_CMN_BKUPTIME;
  313. attributes.dirattr = 0;
  314. attributes.fileattr = 0;
  315. attributes.forkattr = 0;
  316. attributes.volattr = 0;
  317. res = setattrlist(path, &attributes, &attr->bkuptime,
  318. sizeof(struct timespec), FSOPT_NOFOLLOW);
  319. if (res == -1) {
  320. return -errno;
  321. }
  322. }
  323. if (SETATTR_WANTS_FLAGS(attr)) {
  324. res = lchflags(path, attr->flags);
  325. if (res == -1) {
  326. return -errno;
  327. }
  328. }
  329. return 0;
  330. }
  331. static int
  332. loopback_setattr_x(const char *path, struct setattr_x *attr)
  333. {
  334. return loopback_fsetattr_x(path, attr, (struct fuse_file_info *)0);
  335. }
  336. static int
  337. loopback_getxtimes(const char *path, struct timespec *bkuptime,
  338. struct timespec *crtime)
  339. {
  340. int res = 0;
  341. struct attrlist attributes;
  342. attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
  343. attributes.reserved = 0;
  344. attributes.commonattr = 0;
  345. attributes.dirattr = 0;
  346. attributes.fileattr = 0;
  347. attributes.forkattr = 0;
  348. attributes.volattr = 0;
  349. struct xtimeattrbuf {
  350. uint32_t size;
  351. struct timespec xtime;
  352. } __attribute__ ((packed));
  353. struct xtimeattrbuf buf;
  354. attributes.commonattr = ATTR_CMN_BKUPTIME;
  355. res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW);
  356. if (res == 0) {
  357. (void)memcpy(bkuptime, &(buf.xtime), sizeof(struct timespec));
  358. } else {
  359. (void)memset(bkuptime, 0, sizeof(struct timespec));
  360. }
  361. attributes.commonattr = ATTR_CMN_CRTIME;
  362. res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW);
  363. if (res == 0) {
  364. (void)memcpy(crtime, &(buf.xtime), sizeof(struct timespec));
  365. } else {
  366. (void)memset(crtime, 0, sizeof(struct timespec));
  367. }
  368. return 0;
  369. }
  370. static int
  371. loopback_create(const char *path, mode_t mode, struct fuse_file_info *fi)
  372. {
  373. int fd;
  374. fd = open(path, fi->flags, mode);
  375. if (fd == -1) {
  376. return -errno;
  377. }
  378. fi->fh = fd;
  379. return 0;
  380. }
  381. static int
  382. loopback_open(const char *path, struct fuse_file_info *fi)
  383. {
  384. int fd;
  385. fd = open(path, fi->flags);
  386. if (fd == -1) {
  387. return -errno;
  388. }
  389. fi->fh = fd;
  390. return 0;
  391. }
  392. static int
  393. loopback_read(const char *path, char *buf, size_t size, off_t offset,
  394. struct fuse_file_info *fi)
  395. {
  396. int res;
  397. (void)path;
  398. res = pread(fi->fh, buf, size, offset);
  399. if (res == -1) {
  400. res = -errno;
  401. }
  402. return res;
  403. }
  404. static int
  405. loopback_write(const char *path, const char *buf, size_t size,
  406. off_t offset, struct fuse_file_info *fi)
  407. {
  408. int res;
  409. (void)path;
  410. res = pwrite(fi->fh, buf, size, offset);
  411. if (res == -1) {
  412. res = -errno;
  413. }
  414. return res;
  415. }
  416. static int
  417. loopback_statfs(const char *path, struct statvfs *stbuf)
  418. {
  419. int res;
  420. res = statvfs(path, stbuf);
  421. if (res == -1) {
  422. return -errno;
  423. }
  424. return 0;
  425. }
  426. static int
  427. loopback_flush(const char *path, struct fuse_file_info *fi)
  428. {
  429. int res;
  430. (void)path;
  431. res = close(dup(fi->fh));
  432. if (res == -1) {
  433. return -errno;
  434. }
  435. return 0;
  436. }
  437. static int
  438. loopback_release(const char *path, struct fuse_file_info *fi)
  439. {
  440. (void)path;
  441. close(fi->fh);
  442. return 0;
  443. }
  444. static int
  445. loopback_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
  446. {
  447. int res;
  448. (void)path;
  449. (void)isdatasync;
  450. res = fsync(fi->fh);
  451. if (res == -1) {
  452. return -errno;
  453. }
  454. return 0;
  455. }
  456. static int
  457. loopback_setxattr(const char *path, const char *name, const char *value,
  458. size_t size, int flags, uint32_t position)
  459. {
  460. int res;
  461. if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) {
  462. flags &= ~(XATTR_NOSECURITY);
  463. }
  464. if (!strcmp(name, A_KAUTH_FILESEC_XATTR)) {
  465. char new_name[MAXPATHLEN];
  466. memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
  467. memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
  468. res = setxattr(path, new_name, value, size, position, flags);
  469. } else {
  470. res = setxattr(path, name, value, size, position, flags);
  471. }
  472. if (res == -1) {
  473. return -errno;
  474. }
  475. return 0;
  476. }
  477. static int
  478. loopback_getxattr(const char *path, const char *name, char *value, size_t size,
  479. uint32_t position)
  480. {
  481. int res;
  482. if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) {
  483. char new_name[MAXPATHLEN];
  484. memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
  485. memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
  486. res = getxattr(path, new_name, value, size, position, XATTR_NOFOLLOW);
  487. } else {
  488. res = getxattr(path, name, value, size, position, XATTR_NOFOLLOW);
  489. }
  490. if (res == -1) {
  491. return -errno;
  492. }
  493. return res;
  494. }
  495. static int
  496. loopback_listxattr(const char *path, char *list, size_t size)
  497. {
  498. ssize_t res = listxattr(path, list, size, XATTR_NOFOLLOW);
  499. if (res > 0) {
  500. if (list) {
  501. size_t len = 0;
  502. char *curr = list;
  503. do {
  504. size_t thislen = strlen(curr) + 1;
  505. if (strcmp(curr, G_KAUTH_FILESEC_XATTR) == 0) {
  506. memmove(curr, curr + thislen, res - len - thislen);
  507. res -= thislen;
  508. break;
  509. }
  510. curr += thislen;
  511. len += thislen;
  512. } while (len < res);
  513. } else {
  514. /*
  515. ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0,
  516. XATTR_NOFOLLOW);
  517. if (res2 >= 0) {
  518. res -= sizeof(G_KAUTH_FILESEC_XATTR);
  519. }
  520. */
  521. }
  522. }
  523. if (res == -1) {
  524. return -errno;
  525. }
  526. return res;
  527. }
  528. static int
  529. loopback_removexattr(const char *path, const char *name)
  530. {
  531. int res;
  532. if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) {
  533. char new_name[MAXPATHLEN];
  534. memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR));
  535. memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1);
  536. res = removexattr(path, new_name, XATTR_NOFOLLOW);
  537. } else {
  538. res = removexattr(path, name, XATTR_NOFOLLOW);
  539. }
  540. if (res == -1) {
  541. return -errno;
  542. }
  543. return 0;
  544. }
  545. void *
  546. loopback_init(struct fuse_conn_info *conn)
  547. {
  548. FUSE_ENABLE_SETVOLNAME(conn);
  549. FUSE_ENABLE_XTIMES(conn);
  550. return NULL;
  551. }
  552. void
  553. loopback_destroy(void *userdata)
  554. {
  555. /* nothing */
  556. }
  557. static struct fuse_operations loopback_oper = {
  558. .init = loopback_init,
  559. .destroy = loopback_destroy,
  560. .getattr = loopback_getattr,
  561. .fgetattr = loopback_fgetattr,
  562. /* .access = loopback_access, */
  563. .readlink = loopback_readlink,
  564. .opendir = loopback_opendir,
  565. .readdir = loopback_readdir,
  566. .releasedir = loopback_releasedir,
  567. .mknod = loopback_mknod,
  568. .mkdir = loopback_mkdir,
  569. .symlink = loopback_symlink,
  570. .unlink = loopback_unlink,
  571. .rmdir = loopback_rmdir,
  572. .rename = loopback_rename,
  573. .link = loopback_link,
  574. .create = loopback_create,
  575. .open = loopback_open,
  576. .read = loopback_read,
  577. .write = loopback_write,
  578. .statfs = loopback_statfs,
  579. .flush = loopback_flush,
  580. .release = loopback_release,
  581. .fsync = loopback_fsync,
  582. .setxattr = loopback_setxattr,
  583. .getxattr = loopback_getxattr,
  584. .listxattr = loopback_listxattr,
  585. .removexattr = loopback_removexattr,
  586. .exchange = loopback_exchange,
  587. .getxtimes = loopback_getxtimes,
  588. .setattr_x = loopback_setattr_x,
  589. .fsetattr_x = loopback_fsetattr_x,
  590. };
  591. int
  592. main(int argc, char *argv[])
  593. {
  594. umask(0);
  595. return fuse_main(argc, argv, &loopback_oper, NULL);
  596. }