PageRenderTime 76ms CodeModel.GetById 15ms RepoModel.GetById 2ms app.codeStats 0ms

/filesystems/unixfs/common/unixfs/unixfs.c

http://macfuse.googlecode.com/
C | 358 lines | 276 code | 72 blank | 10 comment | 59 complexity | 87c473f2d88e0a87d502b25ed838386c MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, GPL-2.0
  1. /*
  2. * UnixFS
  3. *
  4. * A general-purpose file system layer for writing/reimplementing/porting
  5. * Unix file systems through MacFUSE.
  6. * Copyright (c) 2008 Amit Singh. All Rights Reserved.
  7. * http://osxbook.com
  8. */
  9. #include "unixfs.h"
  10. #include <errno.h>
  11. #include <stddef.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <unistd.h>
  16. #include <ctype.h>
  17. #include <dlfcn.h>
  18. #include <fuse/fuse_opt.h>
  19. #include <fuse/fuse_lowlevel.h>
  20. #define UNIXFS_META_TIMEOUT 60.0 /* timeout for nodes and their attributes */
  21. static struct unixfs* unixfs = (struct unixfs*)0;
  22. static void
  23. unixfs_ll_statfs(fuse_req_t req, fuse_ino_t ino)
  24. {
  25. struct statvfs sv;
  26. unixfs->ops->statvfs(&sv);
  27. fuse_reply_statfs(req, &sv);
  28. }
  29. /* no unixfs_ll_init() since we do initialization before mounting */
  30. static void
  31. unixfs_ll_destroy(void* data)
  32. {
  33. unixfs->ops->fini(unixfs->filsys);
  34. }
  35. static void
  36. unixfs_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char* name)
  37. {
  38. struct fuse_entry_param e;
  39. memset(&e, 0, sizeof(e));
  40. int error = unixfs->ops->namei(parent, name, &(e.attr));
  41. if (error) {
  42. fuse_reply_err(req, error);
  43. return;
  44. }
  45. e.ino = e.attr.st_ino;
  46. e.attr_timeout = e.entry_timeout = UNIXFS_META_TIMEOUT;
  47. fuse_reply_entry(req, &e);
  48. }
  49. static
  50. void unixfs_ll_getattr(fuse_req_t req, fuse_ino_t ino,
  51. struct fuse_file_info* fi)
  52. {
  53. struct stat stbuf;
  54. int error = unixfs->ops->igetattr(ino, &stbuf);
  55. if (!error)
  56. fuse_reply_attr(req, &stbuf, UNIXFS_META_TIMEOUT);
  57. else
  58. fuse_reply_err(req, error);
  59. }
  60. static void
  61. unixfs_ll_readlink(fuse_req_t req, fuse_ino_t ino)
  62. {
  63. int ret = ENOSYS;
  64. char path[UNIXFS_MAXPATHLEN];
  65. if ((ret = unixfs->ops->readlink(ino, path)) != 0)
  66. fuse_reply_err(req, ret);
  67. fuse_reply_readlink(req, path);
  68. }
  69. static void
  70. unixfs_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
  71. struct fuse_file_info* fi)
  72. {
  73. (void)fi;
  74. struct inode* dp = unixfs->ops->iget(ino);
  75. if (!dp) {
  76. fuse_reply_err(req, ENOENT);
  77. return;
  78. }
  79. struct stat stbuf;
  80. unixfs->ops->istat(dp, &stbuf);
  81. if (!S_ISDIR(stbuf.st_mode)) {
  82. unixfs->ops->iput(dp);
  83. fuse_reply_err(req, ENOTDIR);
  84. return;
  85. }
  86. off_t offset = 0;
  87. struct unixfs_direntry dent;
  88. struct replybuf {
  89. char* p;
  90. size_t size;
  91. } b;
  92. memset(&b, 0, sizeof(b));
  93. struct unixfs_dirbuf dirbuf;
  94. while (unixfs->ops->nextdirentry(dp, &dirbuf, &offset, &dent) == 0) {
  95. if (dent.ino == 0)
  96. continue;
  97. if (unixfs->ops->igetattr(dent.ino, &stbuf) != 0)
  98. continue;
  99. size_t oldsize = b.size;
  100. b.size += fuse_add_direntry(req, NULL, 0, dent.name, NULL, 0);
  101. char* newp = (char *)realloc(b.p, b.size);
  102. if (!newp) {
  103. fprintf(stderr, "*** fatal error: cannot allocate memory\n");
  104. abort();
  105. }
  106. b.p = newp;
  107. fuse_add_direntry(req, b.p + oldsize, b.size - oldsize, dent.name,
  108. &stbuf, b.size);
  109. }
  110. unixfs->ops->iput(dp);
  111. if (off < b.size)
  112. fuse_reply_buf(req, b.p + off, min(b.size - off, size));
  113. else
  114. fuse_reply_buf(req, NULL, 0);
  115. free(b.p);
  116. }
  117. static void
  118. unixfs_ll_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
  119. {
  120. struct inode* ip = unixfs->ops->iget(ino);
  121. if (!ip)
  122. fuse_reply_err(req, ENOENT);
  123. struct stat stbuf;
  124. unixfs->ops->istat(ip, &stbuf);
  125. if (!S_ISREG(stbuf.st_mode)) {
  126. if (S_ISDIR(stbuf.st_mode))
  127. fuse_reply_err(req, EISDIR);
  128. else if (S_ISBLK(stbuf.st_mode) || S_ISCHR(stbuf.st_mode))
  129. fuse_reply_err(req, ENXIO);
  130. else
  131. fuse_reply_err(req, EACCES);
  132. unixfs->ops->iput(ip);
  133. } else {
  134. fi->fh = (uint64_t)(long)ip;
  135. fuse_reply_open(req, fi);
  136. }
  137. }
  138. static void
  139. unixfs_ll_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
  140. {
  141. if (fi->fh)
  142. unixfs->ops->iput((struct inode *)(long)(fi->fh));
  143. fi->fh = 0;
  144. fuse_reply_err(req, 0);
  145. }
  146. static void
  147. unixfs_ll_read(fuse_req_t req, fuse_ino_t ino, size_t count, off_t offset,
  148. struct fuse_file_info* fi)
  149. {
  150. struct inode* ip = (struct inode*)(long)(fi->fh);
  151. if (!ip) {
  152. fuse_reply_err(req, EBADF);
  153. return;
  154. }
  155. struct stat stbuf;
  156. unixfs->ops->istat(ip, &stbuf);
  157. off_t size = stbuf.st_size;
  158. if ((count == 0) || (offset > size)) {
  159. fuse_reply_buf(req, NULL, 0);
  160. return;
  161. }
  162. if ((offset + count) > size)
  163. count = size - offset;
  164. char *buf = calloc(count, 1);
  165. if (!buf) {
  166. fuse_reply_err(req, ENOMEM);
  167. return;
  168. }
  169. int error = 0;
  170. char* bp = buf;
  171. size_t nbytes = 0;
  172. do {
  173. ssize_t ret = unixfs->ops->pbread(ip, bp, count, offset, &error);
  174. if (ret < 0)
  175. goto out;
  176. count -= ret;
  177. offset += ret;
  178. nbytes += ret;
  179. bp += ret;
  180. } while (!error && count);
  181. out:
  182. fuse_reply_buf(req, buf, nbytes);
  183. free(buf);
  184. }
  185. static struct fuse_lowlevel_ops unixfs_ll_oper = {
  186. .statfs = unixfs_ll_statfs,
  187. .destroy = unixfs_ll_destroy,
  188. .lookup = unixfs_ll_lookup,
  189. .getattr = unixfs_ll_getattr,
  190. .readlink = unixfs_ll_readlink,
  191. .readdir = unixfs_ll_readdir,
  192. .open = unixfs_ll_open,
  193. .release = unixfs_ll_release,
  194. .read = unixfs_ll_read,
  195. };
  196. struct options {
  197. char* dmg;
  198. int force;
  199. char* fsendian;
  200. char* type;
  201. } options;
  202. #define UNIXFS_OPT_KEY(t, p, v) { t, offsetof(struct options, p), v }
  203. static struct fuse_opt unixfs_opts[] = {
  204. UNIXFS_OPT_KEY("--dmg %s", dmg, 0),
  205. UNIXFS_OPT_KEY("--force", force, 1),
  206. UNIXFS_OPT_KEY("--fsendian %s", fsendian, 0),
  207. UNIXFS_OPT_KEY("--type %s", type, 0),
  208. FUSE_OPT_END
  209. };
  210. int
  211. main(int argc, char* argv[])
  212. {
  213. struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
  214. memset(&options, 0, sizeof(struct options));
  215. if ((fuse_opt_parse(&args, &options, unixfs_opts, NULL) == -1) ||
  216. !options.dmg) {
  217. unixfs_usage();
  218. return -1;
  219. }
  220. char* mountpoint;
  221. int multithreaded;
  222. int foregrounded;
  223. if (fuse_parse_cmdline(&args, &mountpoint,
  224. &multithreaded, &foregrounded) == -1) {
  225. unixfs_usage();
  226. return -1;
  227. }
  228. if (!(unixfs = unixfs_preflight(options.dmg, &(options.type), &unixfs))) {
  229. if (options.type)
  230. fprintf(stderr, "invalid file system type %s\n", options.type);
  231. else
  232. fprintf(stderr, "missing file system type\n");
  233. return -1;
  234. }
  235. if (options.force)
  236. unixfs->flags |= UNIXFS_FORCE;
  237. unixfs->fsname = options.type; /* XXX quick fix */
  238. unixfs->fsendian = UNIXFS_FS_INVALID;
  239. if (options.fsendian) {
  240. if (strcasecmp(options.fsendian, "pdp") == 0) {
  241. unixfs->fsendian = UNIXFS_FS_PDP;
  242. } else if (strcasecmp(options.fsendian, "big") == 0) {
  243. unixfs->fsendian = UNIXFS_FS_BIG;
  244. } else if (strcasecmp(options.fsendian, "little") == 0) {
  245. unixfs->fsendian = UNIXFS_FS_LITTLE;
  246. } else {
  247. fprintf(stderr, "invalid endian type %s\n", options.fsendian);
  248. return -1;
  249. }
  250. }
  251. if ((unixfs->filsys =
  252. unixfs->ops->init(options.dmg, unixfs->flags, unixfs->fsendian,
  253. &unixfs->fsname, &unixfs->volname)) == NULL) {
  254. fprintf(stderr, "failed to initialize file system\n");
  255. return -1;
  256. }
  257. char extra_args[UNIXFS_ARGLEN] = { 0 };
  258. unixfs_postflight(unixfs->fsname, unixfs->volname, extra_args);
  259. fuse_opt_add_arg(&args, extra_args);
  260. int err = -1;
  261. struct fuse_chan *ch;
  262. if ((ch = fuse_mount(mountpoint, &args)) != NULL) {
  263. struct fuse_session* se;
  264. se = fuse_lowlevel_new(&args, &unixfs_ll_oper, sizeof(unixfs_ll_oper),
  265. (void*)&unixfs);
  266. if (se != NULL) {
  267. if ((err = fuse_daemonize(foregrounded)) == -1)
  268. goto bailout;
  269. if (fuse_set_signal_handlers(se) != -1) {
  270. fuse_session_add_chan(se, ch);
  271. if (multithreaded)
  272. err = fuse_session_loop_mt(se);
  273. else
  274. err = fuse_session_loop(se);
  275. fuse_remove_signal_handlers(se);
  276. fuse_session_remove_chan(ch);
  277. }
  278. bailout:
  279. fuse_session_destroy(se);
  280. }
  281. fuse_unmount(mountpoint, ch);
  282. }
  283. fuse_opt_free_args(&args);
  284. return err ? 1 : 0;
  285. }