You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

342 lines
7.4 KiB

  1. /*
  2. Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
  3. Permission to use, copy, modify, and/or distribute this software for any
  4. purpose with or without fee is hereby granted, provided that the above
  5. copyright notice and this permission notice appear in all copies.
  6. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  7. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  8. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  9. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  10. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  11. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  12. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  13. */
  14. #include <fuse.h>
  15. #include <stddef.h>
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <string>
  21. #include <vector>
  22. #include <sstream>
  23. #include <iostream>
  24. #include <iomanip>
  25. #include "config.hpp"
  26. #include "num.hpp"
  27. #include "policy.hpp"
  28. #include "str.hpp"
  29. #include "version.hpp"
  30. using std::string;
  31. using std::vector;
  32. using namespace mergerfs;
  33. enum
  34. {
  35. MERGERFS_OPT_HELP,
  36. MERGERFS_OPT_VERSION
  37. };
  38. static
  39. void
  40. set_option(fuse_args &args,
  41. const std::string &option_)
  42. {
  43. string option;
  44. option = "-o" + option_;
  45. fuse_opt_insert_arg(&args,1,option.c_str());
  46. }
  47. static
  48. void
  49. set_kv_option(fuse_args &args,
  50. const std::string &key,
  51. const std::string &value)
  52. {
  53. std::string option;
  54. option = key + '=' + value;
  55. set_option(args,option);
  56. }
  57. static
  58. void
  59. set_fsname(fuse_args &args,
  60. const vector<string> &srcmounts)
  61. {
  62. if(srcmounts.size() > 0)
  63. {
  64. std::string fsname;
  65. fsname = str::remove_common_prefix_and_join(srcmounts,':');
  66. set_kv_option(args,"fsname",fsname);
  67. }
  68. }
  69. static
  70. void
  71. set_subtype(fuse_args &args)
  72. {
  73. set_kv_option(args,"subtype","mergerfs");
  74. }
  75. static
  76. void
  77. set_default_options(fuse_args &args)
  78. {
  79. set_option(args,"atomic_o_trunc");
  80. set_option(args,"auto_cache");
  81. set_option(args,"big_writes");
  82. set_option(args,"default_permissions");
  83. set_option(args,"splice_move");
  84. set_option(args,"splice_read");
  85. set_option(args,"splice_write");
  86. }
  87. static
  88. int
  89. parse_and_process_minfreespace(const std::string &value,
  90. uint64_t &minfreespace)
  91. {
  92. int rv;
  93. rv = num::to_uint64_t(value,minfreespace);
  94. if(rv == -1)
  95. return 1;
  96. return 0;
  97. }
  98. static
  99. int
  100. parse_and_process_moveonenospc(const std::string &value,
  101. bool &moveonenospc)
  102. {
  103. if(value == "false")
  104. moveonenospc = false;
  105. else if(value == "true")
  106. moveonenospc = true;
  107. else
  108. return 1;
  109. return 0;
  110. }
  111. static
  112. int
  113. parse_and_process_arg(Config &config,
  114. const std::string &arg,
  115. fuse_args *outargs)
  116. {
  117. if(arg == "defaults")
  118. {
  119. set_default_options(*outargs);
  120. return 0;
  121. }
  122. return 1;
  123. }
  124. static
  125. int
  126. parse_and_process_kv_arg(Config &config,
  127. const std::string &key,
  128. const std::string &value)
  129. {
  130. int rv = -1;
  131. std::vector<std::string> keypart;
  132. str::split(keypart,key,'.');
  133. if(keypart.size() == 2)
  134. {
  135. if(keypart[0] == "func")
  136. rv = config.set_func_policy(keypart[1],value);
  137. else if(keypart[0] == "category")
  138. rv = config.set_category_policy(keypart[1],value);
  139. }
  140. else
  141. {
  142. if(key == "minfreespace")
  143. rv = parse_and_process_minfreespace(value,config.minfreespace);
  144. else if(key == "moveonenospc")
  145. rv = parse_and_process_moveonenospc(value,config.moveonenospc);
  146. }
  147. if(rv == -1)
  148. rv = 1;
  149. return rv;
  150. }
  151. static
  152. int
  153. process_opt(Config &config,
  154. const std::string &arg,
  155. fuse_args *outargs)
  156. {
  157. int rv;
  158. std::vector<std::string> argvalue;
  159. str::split(argvalue,arg,'=');
  160. switch(argvalue.size())
  161. {
  162. case 1:
  163. rv = parse_and_process_arg(config,argvalue[0],outargs);
  164. break;
  165. case 2:
  166. rv = parse_and_process_kv_arg(config,argvalue[0],argvalue[1]);
  167. break;
  168. default:
  169. rv = 1;
  170. break;
  171. };
  172. return rv;
  173. }
  174. static
  175. int
  176. process_srcmounts(const char *arg,
  177. Config &config)
  178. {
  179. vector<string> paths;
  180. str::split(paths,arg,':');
  181. fs::glob(paths,config.srcmounts);
  182. fs::realpathize(config.srcmounts);
  183. return 0;
  184. }
  185. static
  186. int
  187. process_destmounts(const char *arg,
  188. Config &config)
  189. {
  190. config.destmount = arg;
  191. return 1;
  192. }
  193. static
  194. void
  195. usage(void)
  196. {
  197. std::cout <<
  198. "Usage: mergerfs [options] <srcpaths> <destpath>\n"
  199. "\n"
  200. " -o [opt,...] mount options\n"
  201. " -h --help print help\n"
  202. " -v --version print version\n"
  203. "\n"
  204. "mergerfs options:\n"
  205. " <srcpaths> ':' delimited list of directories. Supports\n"
  206. " shell globbing (must be escaped in shell)\n"
  207. " -o defaults default FUSE options which seem to provide the\n"
  208. " best performance: atomic_o_trunc, auto_cache,\n"
  209. " big_writes, default_permissions, splice_read,\n"
  210. " splice_write, splice_move\n"
  211. " -o direct_io bypass additional caching, increases write\n"
  212. " speeds at the cost of reads\n"
  213. " -o minfreespace=<int> minimum free space needed for certain policies:\n"
  214. " default 4G\n"
  215. " -o moveonenospc=<bool> try to move file to another drive when ENOSPC\n"
  216. " on write: default false\n"
  217. " -o func.<f>=<p> set function <f> to policy <p>\n"
  218. " -o category.<c>=<p> set functions in category <c> to <p>\n"
  219. << std::endl;
  220. }
  221. static
  222. void
  223. version(void)
  224. {
  225. std::cout << "mergerfs version: " << MERGERFS_VERSION
  226. << std::endl;
  227. }
  228. static
  229. int
  230. option_processor(void *data,
  231. const char *arg,
  232. int key,
  233. fuse_args *outargs)
  234. {
  235. int rv = 0;
  236. Config &config = *(Config*)data;
  237. switch(key)
  238. {
  239. case FUSE_OPT_KEY_OPT:
  240. rv = process_opt(config,arg,outargs);
  241. break;
  242. case FUSE_OPT_KEY_NONOPT:
  243. rv = config.srcmounts.empty() ?
  244. process_srcmounts(arg,config) :
  245. process_destmounts(arg,config);
  246. break;
  247. case MERGERFS_OPT_HELP:
  248. usage();
  249. close(2);
  250. dup(1);
  251. fuse_opt_add_arg(outargs,"-ho");
  252. break;
  253. case MERGERFS_OPT_VERSION:
  254. version();
  255. fuse_opt_add_arg(outargs,"--version");
  256. break;
  257. default:
  258. break;
  259. }
  260. return rv;
  261. }
  262. namespace mergerfs
  263. {
  264. namespace options
  265. {
  266. void
  267. parse(fuse_args &args,
  268. Config &config)
  269. {
  270. const struct fuse_opt opts[] =
  271. {
  272. FUSE_OPT_KEY("-h",MERGERFS_OPT_HELP),
  273. FUSE_OPT_KEY("--help",MERGERFS_OPT_HELP),
  274. FUSE_OPT_KEY("-v",MERGERFS_OPT_VERSION),
  275. FUSE_OPT_KEY("-V",MERGERFS_OPT_VERSION),
  276. FUSE_OPT_KEY("--version",MERGERFS_OPT_VERSION),
  277. {NULL,-1U,0}
  278. };
  279. fuse_opt_parse(&args,
  280. &config,
  281. opts,
  282. ::option_processor);
  283. set_fsname(args,config.srcmounts);
  284. set_subtype(args);
  285. }
  286. }
  287. }