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.

381 lines
9.5 KiB

4 years ago
  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 "config.hpp"
  15. #include "dirinfo.hpp"
  16. #include "endian.hpp"
  17. #include "errno.hpp"
  18. #include "fileinfo.hpp"
  19. #include "fs_close.hpp"
  20. #include "fs_findallfiles.hpp"
  21. #include "fs_ioctl.hpp"
  22. #include "fs_open.hpp"
  23. #include "fs_path.hpp"
  24. #include "gidcache.hpp"
  25. #include "str.hpp"
  26. #include "ugid.hpp"
  27. #include <string>
  28. #include <vector>
  29. #include <fcntl.h>
  30. #include <string.h>
  31. using std::string;
  32. using std::vector;
  33. #ifndef _IOC_TYPE
  34. #define _IOC_TYPE(X) (((X) >> 8) & 0xFF)
  35. #endif
  36. typedef char IOCTL_BUF[4096];
  37. #define IOCTL_APP_TYPE 0xDF
  38. #define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF)
  39. #define IOCTL_GC _IO(IOCTL_APP_TYPE,1)
  40. #define IOCTL_GC1 _IO(IOCTL_APP_TYPE,2)
  41. #define IOCTL_INVALIDATE_ALL_NODES _IO(IOCTL_APP_TYPE,3)
  42. #define IOCTL_INVALIDATE_GID_CACHE _IO(IOCTL_APP_TYPE,4)
  43. // From linux/btrfs.h
  44. #define BTRFS_IOCTL_MAGIC 0x94
  45. #ifndef FS_IOC_GETFLAGS
  46. # define FS_IOC_GETFLAGS _IOR('f',1,long)
  47. #endif
  48. #ifndef FS_IOC_SETFLAGS
  49. # define FS_IOC_SETFLAGS _IOW('f',2,long)
  50. #endif
  51. #ifndef FS_IOC_GETVERSION
  52. # define FS_IOC_GETVERSION _IOR('v',1,long)
  53. #endif
  54. #ifndef FS_IOC_SETVERSION
  55. # define FS_IOC_SETVERSION _IOW('v',2,long)
  56. #endif
  57. /*
  58. There is a bug with FUSE and these ioctl commands. The regular
  59. libfuse high level API assumes the output buffer size based on the
  60. command and gives no control over this. FS_IOC_GETFLAGS and
  61. FS_IOC_SETFLAGS however are defined as `long` when in fact it is an
  62. `int`. On 64bit systems where long is 8 bytes this can lead to
  63. libfuse telling the kernel to write 8 bytes and if the user only
  64. allocated an integer then it will overwrite the 4 bytes after the
  65. variable which could result in data corruption and/or crashes.
  66. I've modified the API to allow changing of the output buffer
  67. size. This fixes the issue on little endian systems because the
  68. lower 4 bytes are the same regardless of what the user
  69. allocated. However, on big endian systems that's not the case and it
  70. is not possible to safely handle the situation.
  71. https://lwn.net/Articles/575846/
  72. */
  73. namespace l
  74. {
  75. static
  76. int
  77. ioctl(const int fd_,
  78. const uint32_t cmd_,
  79. void *data_,
  80. uint32_t *out_bufsz_)
  81. {
  82. int rv;
  83. switch(cmd_)
  84. {
  85. case FS_IOC_GETFLAGS:
  86. case FS_IOC_SETFLAGS:
  87. case FS_IOC_GETVERSION:
  88. case FS_IOC_SETVERSION:
  89. if(endian::is_big() && (sizeof(long) != sizeof(int)))
  90. return -ENOTTY;
  91. if((data_ != NULL) && (*out_bufsz_ > 4))
  92. *out_bufsz_ = 4;
  93. break;
  94. }
  95. rv = fs::ioctl(fd_,cmd_,data_);
  96. return ((rv == -1) ? -errno : rv);
  97. }
  98. static
  99. int
  100. ioctl_file(const fuse_file_info_t *ffi_,
  101. const uint32_t cmd_,
  102. void *data_,
  103. uint32_t *out_bufsz_)
  104. {
  105. FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
  106. const fuse_context *fc = fuse_get_context();
  107. const ugid::Set ugid(fc->uid,fc->gid);
  108. return l::ioctl(fi->fd,cmd_,data_,out_bufsz_);
  109. }
  110. #ifndef O_NOATIME
  111. #define O_NOATIME 0
  112. #endif
  113. static
  114. int
  115. ioctl_dir_base(const Policy::Search &searchFunc_,
  116. const Branches &branches_,
  117. const char *fusepath_,
  118. const uint32_t cmd_,
  119. void *data_,
  120. uint32_t *out_bufsz_)
  121. {
  122. int fd;
  123. int rv;
  124. string fullpath;
  125. StrVec basepaths;
  126. rv = searchFunc_(branches_,fusepath_,&basepaths);
  127. if(rv == -1)
  128. return -errno;
  129. fullpath = fs::path::make(basepaths[0],fusepath_);
  130. fd = fs::open(fullpath,O_RDONLY|O_NOATIME|O_NONBLOCK);
  131. if(fd == -1)
  132. return -errno;
  133. rv = l::ioctl(fd,cmd_,data_,out_bufsz_);
  134. fs::close(fd);
  135. return rv;
  136. }
  137. static
  138. int
  139. ioctl_dir(const fuse_file_info_t *ffi_,
  140. const uint32_t cmd_,
  141. void *data_,
  142. uint32_t *out_bufsz_)
  143. {
  144. Config::Read cfg;
  145. DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
  146. const fuse_context *fc = fuse_get_context();
  147. const ugid::Set ugid(fc->uid,fc->gid);
  148. return l::ioctl_dir_base(cfg->func.open.policy,
  149. cfg->branches,
  150. di->fusepath.c_str(),
  151. cmd_,
  152. data_,
  153. out_bufsz_);
  154. }
  155. static
  156. int
  157. strcpy(const std::string &s_,
  158. void *data_)
  159. {
  160. char *data = (char*)data_;
  161. if(s_.size() >= (sizeof(IOCTL_BUF) - 1))
  162. return -ERANGE;
  163. memcpy(data,s_.c_str(),s_.size());
  164. data[s_.size()] = '\0';
  165. return s_.size();
  166. }
  167. static
  168. int
  169. file_basepath(const Policy::Search &searchFunc_,
  170. const Branches &branches_,
  171. const char *fusepath_,
  172. void *data_)
  173. {
  174. int rv;
  175. StrVec basepaths;
  176. rv = searchFunc_(branches_,fusepath_,&basepaths);
  177. if(rv == -1)
  178. return -errno;
  179. return l::strcpy(basepaths[0],data_);
  180. }
  181. static
  182. int
  183. file_basepath(const fuse_file_info_t *ffi_,
  184. void *data_)
  185. {
  186. Config::Read cfg;
  187. std::string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
  188. return l::file_basepath(cfg->func.open.policy,
  189. cfg->branches,
  190. fusepath.c_str(),
  191. data_);
  192. }
  193. static
  194. int
  195. file_relpath(const fuse_file_info_t *ffi_,
  196. void *data_)
  197. {
  198. std::string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
  199. return l::strcpy(fusepath,data_);
  200. }
  201. static
  202. int
  203. file_fullpath(const Policy::Search &searchFunc_,
  204. const Branches &branches_,
  205. const string &fusepath_,
  206. void *data_)
  207. {
  208. int rv;
  209. string fullpath;
  210. StrVec basepaths;
  211. rv = searchFunc_(branches_,fusepath_,&basepaths);
  212. if(rv == -1)
  213. return -errno;
  214. fullpath = fs::path::make(basepaths[0],fusepath_);
  215. return l::strcpy(fullpath,data_);
  216. }
  217. static
  218. int
  219. file_fullpath(const fuse_file_info_t *ffi_,
  220. void *data_)
  221. {
  222. Config::Read cfg;
  223. std::string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
  224. return l::file_fullpath(cfg->func.open.policy,
  225. cfg->branches,
  226. fusepath,
  227. data_);
  228. }
  229. static
  230. int
  231. file_allpaths(const fuse_file_info_t *ffi_,
  232. void *data_)
  233. {
  234. Config::Read cfg;
  235. string concated;
  236. StrVec paths;
  237. StrVec branches;
  238. string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
  239. cfg->branches->to_paths(branches);
  240. fs::findallfiles(branches,fusepath.c_str(),&paths);
  241. concated = str::join(paths,'\0');
  242. return l::strcpy(concated,data_);
  243. }
  244. static
  245. int
  246. file_info(const fuse_file_info_t *ffi_,
  247. void *data_)
  248. {
  249. char *key = (char*)data_;
  250. if(!strcmp("basepath",key))
  251. return l::file_basepath(ffi_,data_);
  252. if(!strcmp("relpath",key))
  253. return l::file_relpath(ffi_,data_);
  254. if(!strcmp("fullpath",key))
  255. return l::file_fullpath(ffi_,data_);
  256. if(!strcmp("allpaths",key))
  257. return l::file_allpaths(ffi_,data_);
  258. return -ENOATTR;
  259. }
  260. static
  261. bool
  262. is_mergerfs_ioctl_cmd(const unsigned long cmd_)
  263. {
  264. return (_IOC_TYPE(cmd_) == IOCTL_APP_TYPE);
  265. }
  266. static
  267. bool
  268. is_btrfs_ioctl_cmd(const unsigned long cmd_)
  269. {
  270. return (_IOC_TYPE(cmd_) == BTRFS_IOCTL_MAGIC);
  271. }
  272. static
  273. int
  274. ioctl_custom(const fuse_file_info_t *ffi_,
  275. unsigned long cmd_,
  276. void *data_)
  277. {
  278. switch(cmd_)
  279. {
  280. case IOCTL_FILE_INFO:
  281. return l::file_info(ffi_,data_);
  282. case IOCTL_GC:
  283. fuse_gc();
  284. return 0;
  285. case IOCTL_GC1:
  286. fuse_gc1();
  287. return 0;
  288. case IOCTL_INVALIDATE_ALL_NODES:
  289. fuse_invalidate_all_nodes();
  290. return 0;
  291. case IOCTL_INVALIDATE_GID_CACHE:
  292. GIDCache::invalidate_all_caches();
  293. break;
  294. }
  295. return -ENOTTY;
  296. }
  297. }
  298. namespace FUSE
  299. {
  300. int
  301. ioctl(const fuse_file_info_t *ffi_,
  302. unsigned long cmd_,
  303. void *arg_,
  304. unsigned int flags_,
  305. void *data_,
  306. uint32_t *out_bufsz_)
  307. {
  308. if(l::is_btrfs_ioctl_cmd(cmd_))
  309. return -ENOTTY;
  310. if(l::is_mergerfs_ioctl_cmd(cmd_))
  311. return l::ioctl_custom(ffi_,cmd_,data_);
  312. if(flags_ & FUSE_IOCTL_DIR)
  313. return l::ioctl_dir(ffi_,cmd_,data_,out_bufsz_);
  314. return l::ioctl_file(ffi_,cmd_,data_,out_bufsz_);
  315. }
  316. }