caseless-fuse.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. * Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
  3. * All rights reserved.
  4. * This component and the accompanying materials are made available
  5. * under the terms of the License "Eclipse Public License v1.0"
  6. * which accompanies this distribution, and is available
  7. * at the URL "http://www.eclipse.org/legal/epl-v10.html".
  8. *
  9. * Initial Contributors:
  10. * Nokia Corporation - initial contribution.
  11. *
  12. * Contributors:
  13. *
  14. * Description:
  15. * caseless_fuse
  16. *
  17. * Fuse filesystem for case insensitivity and path separator correction
  18. * (i.e converts '\' in filenames to '/')
  19. *
  20. */
  21. #define FUSE_USE_VERSION 26
  22. #include <fuse.h>
  23. #include <stdio.h>
  24. #include <errno.h>
  25. #include <fcntl.h>
  26. #include <malloc.h>
  27. #include <dlfcn.h>
  28. #include <sys/types.h>
  29. #include <sys/stat.h>
  30. #include <sys/param.h>
  31. #include <dirent.h>
  32. #include <string.h>
  33. #include <alloca.h>
  34. #include <stdarg.h>
  35. #include <sys/times.h>
  36. #include <unistd.h>
  37. #include "pathcache.h"
  38. #include "filehandles.h"
  39. #include "debug.h"
  40. /* Context structure representing information needed for one "mountpoint" */
  41. typedef struct {
  42. char mountsource[MAXPATHLEN];
  43. int mountsourcelen;
  44. } caseless_fs;
  45. caseless_fs caseless_mount;
  46. /* convertpath
  47. source - input path, possibly with windows path separators (\)
  48. dest - output path in unix format
  49. Copy input path and convert backslashes to forward ones as we work.
  50. use strpbrk preferably so that we're getting the C library's best
  51. efforts to work for us.
  52. */
  53. void convertpath(const char source[], char *dest)
  54. {
  55. strcpy(dest,source);
  56. char *convert = dest;
  57. do
  58. {
  59. convert = strpbrk(convert, "\\");
  60. if (convert)
  61. {
  62. *convert = '/';
  63. continue;
  64. } else {
  65. break;
  66. }
  67. } while (convert != NULL);
  68. int e = strlen(dest);
  69. if (e > 0 && dest[e-1] == '/')
  70. dest[e-1] = '\0'; //slashes on the ends of paths confuses the cache
  71. }
  72. // Creates a path string on the stack that has the caseless mountsource
  73. // prepended onto it and has all slashes converted
  74. #define PATH_ALLOC(path, input_path) { \
  75. debug("PATH_ALLOC: input: '%s'\n", input_path); \
  76. path = alloca(strlen(input_path) + caseless_mount.mountsourcelen + 5); \
  77. debug("PATH_ALLOC: mountsource: '%s'\n", caseless_mount.mountsource); \
  78. strcpy(path, caseless_mount.mountsource); \
  79. convertpath(input_path, path+caseless_mount.mountsourcelen); \
  80. debug("PATH_ALLOC: output: '%s'\n", path); \
  81. }
  82. int caseless_find_parents(char *path)
  83. // Find the directory of a file/subdir that's going to be created
  84. {
  85. // find the component of the path that must exist already.
  86. char *lookback = path+strlen(path)-1;
  87. if (*lookback == '/') lookback--; //ignore eol-slashes
  88. debug("caseless: find_parents of '%s'", path);
  89. while (lookback > path)
  90. {
  91. if (*lookback == '/')
  92. {
  93. *lookback = '\0';
  94. int fcr = cached_findcaseless(path);
  95. *lookback = '/';
  96. return fcr;
  97. }
  98. lookback--;
  99. }
  100. return 0;
  101. }
  102. /*
  103. caseless_log_process
  104. Reports the commmandline of the process that initiated
  105. the current fuse operation e.g. you can see what command
  106. created a file.
  107. */
  108. static void caseless_log_process(const char *pathname)
  109. {
  110. struct fuse_context *fc = fuse_get_context();
  111. char *cmdfile = alloca(30);
  112. snprintf(cmdfile,30,"/proc/%d/cmdline", fc->pid);
  113. cmdfile[29] = '\0';
  114. int cmdline_fh = open(cmdfile, O_RDONLY );
  115. if (cmdline_fh != -1)
  116. {
  117. char *cmdline = alloca(512);
  118. int br = read(cmdline_fh, cmdline, 511);
  119. close(cmdline_fh);
  120. if (br > 0)
  121. {
  122. int i;
  123. for (i=0; i < br; i++)
  124. if (cmdline[i]=='\0') cmdline[i] = ' ';
  125. cmdline[br] = '\0';
  126. debug("\n\ncaseless: command that opened '%s' was: '%s'\n\n", pathname, cmdline);
  127. }
  128. }
  129. }
  130. static int caseless_open(const char *pathname, struct fuse_file_info *fi)
  131. {
  132. int rv=0;
  133. int er=0;
  134. char *path = NULL;
  135. PATH_ALLOC(path,pathname);
  136. debug("\n\ncaseless: open '%s' rv=%d, errno=%d\n\n\n", pathname, rv, errno);
  137. // caseless_log_process(pathname);
  138. if (0 != cached_findcaseless(path) && !(fi->flags & O_CREAT))
  139. {
  140. debug("caseless: openattempt: %s\n", path);
  141. return -ENOENT;
  142. }
  143. errno = 0;
  144. rv = open(path, fi->flags);
  145. er = errno;
  146. debug("caseless: openattempt: rv is %d and errno is %d\n", rv,errno);
  147. if (rv != -1)
  148. {
  149. caseless_cache_path(path);
  150. fi->fh = fh_new(rv);
  151. if (fi->fh == -1)
  152. {
  153. /* some error if there are no spare filehandles */
  154. return -ENOENT;
  155. }
  156. }
  157. return -er;
  158. }
  159. /*
  160. Function for changing file time with nanosecond precision.
  161. */
  162. static int caseless_utimens(const char *pathname, const struct timespec tv[2])
  163. {
  164. int rv = 0;
  165. char *path = NULL;
  166. PATH_ALLOC(path,pathname);
  167. if (0 == cached_findcaseless(path))
  168. {
  169. rv = utimensat(0, path,tv, 0); // absolute path should allow dirfd parameter to be ignored
  170. if (0 != rv)
  171. rv = -errno;
  172. } else {
  173. errno = ENOENT;
  174. }
  175. return rv;
  176. }
  177. static int caseless_getattr(const char *pathname, struct stat *buf)
  178. {
  179. int rv=0;
  180. int er=0;
  181. char *path = NULL;
  182. PATH_ALLOC(path,pathname);
  183. int found = cached_findcaseless(path);
  184. if (found != 0)
  185. {
  186. debug("caseless: getattr not found: %s\n", path);
  187. return -ENOENT;
  188. }
  189. errno = 0;
  190. rv = lstat(path, buf);
  191. er = errno;
  192. debug("caseless: getattr found: %s rv=%d, errno=%d\n", path, rv, er);
  193. if ( rv <= 0 && errno == ENOENT)
  194. {
  195. debug("\n\ncaseless: tried to stat '%s' rv=%d, errno=%d\n\n\n", path, rv, errno);
  196. }
  197. if (rv == 0)
  198. {
  199. caseless_cache_path(path);
  200. }
  201. return -er;
  202. }
  203. /* helper function with debugging and NULL protection */
  204. char *case_corrected(char *path)
  205. {
  206. if (!path) return path;
  207. if (0 == cached_findcaseless(path))
  208. {
  209. debug("Found: %s \n", path);
  210. }
  211. else
  212. {
  213. debug( "Path Not found: %s\n", path);
  214. return NULL;
  215. }
  216. return path;
  217. }
  218. static int caseless_readdir(const char *pathname, void *buf,
  219. fuse_fill_dir_t filler,
  220. off_t offset, struct fuse_file_info *fi)
  221. {
  222. DIR *dirh=NULL;
  223. int er=0;
  224. char *path = NULL;
  225. PATH_ALLOC(path,pathname);
  226. dirh = opendir(path);
  227. debug("\n\ncaseless: opendir asked:'%s' trying:'%s' \n", pathname, path);
  228. if ( dirh == NULL )
  229. {
  230. er = errno;
  231. if (er == ENOENT)
  232. {
  233. debug("\n\ncaseless: readdir failed - asked: '%s' trying: '%s' \n\n\n", pathname, path);
  234. path = case_corrected(path);
  235. if (path)
  236. {
  237. dirh = opendir(path);
  238. if (!dirh)
  239. er = errno;
  240. else
  241. er = 0;
  242. } else {
  243. er = ENOENT;
  244. }
  245. }
  246. }
  247. if ( dirh != NULL )
  248. {
  249. struct dirent *entry;
  250. struct stat attrs;
  251. while ((entry = readdir(dirh)) != NULL)
  252. {
  253. memset(&attrs, 0, sizeof(attrs));
  254. attrs.st_ino = entry->d_ino;
  255. attrs.st_mode = entry->d_type << 12;
  256. if (filler(buf, entry->d_name, &attrs, 0))
  257. break;
  258. }
  259. closedir(dirh);
  260. }
  261. return -er;
  262. }
  263. static int caseless_read(const char *pathname, char *buf, size_t size, off_t offset,
  264. struct fuse_file_info *fi)
  265. {
  266. int rv = pread(fh_getreal(fi->fh), buf, size, offset);
  267. if (rv == -1)
  268. return -errno;
  269. return rv;
  270. }
  271. static int caseless_write(const char *pathname, const char *buf, size_t size,
  272. off_t offset, struct fuse_file_info *fi)
  273. {
  274. int rv = pwrite(fh_getreal(fi->fh), buf, size, offset);
  275. if (rv == -1)
  276. return -errno;
  277. return rv;
  278. }
  279. static int caseless_flush(const char *pathname, struct fuse_file_info *fi)
  280. {
  281. return 0;
  282. }
  283. static int caseless_release(const char *pathname, struct fuse_file_info *fi)
  284. {
  285. debug("\ncaseless: close %s\n", pathname);
  286. fh_close(fi->fh);
  287. return 0;
  288. }
  289. static int caseless_readlink (const char *pathname, char *buf, size_t bufsiz)
  290. {
  291. ssize_t s;
  292. s = readlink(pathname, buf, bufsiz);
  293. if (s == -1)
  294. return -errno;
  295. return 0;
  296. }
  297. // int (*getxattr) (const char *, const char *, char *, size_t);
  298. // int (*listxattr) (const char *, char *, size_t);
  299. // int (*removexattr) (const char *, const char *);
  300. static int caseless_getxattr(const char *path, const char *name,
  301. char *value, size_t size)
  302. {
  303. // Not implementing a pass-through yet
  304. debug("\ncaseless: getxattr '%s' into buffer size '%d' \n", name, size);
  305. if (size > 0 ) value[0] = '\0';
  306. return 0;
  307. }
  308. static int caseless_setxattr(const char *path, const char *name,
  309. const char *value, size_t size, int i)
  310. {
  311. // Not implementing a pass-through yet
  312. debug("\ncaseless: setxattr '%s' to '%s' \n", name, value);
  313. return 0;
  314. }
  315. static int caseless_create(const char *pathname, mode_t mode, struct fuse_file_info *fi)
  316. {
  317. int filehandle=0;
  318. char *path = NULL;
  319. PATH_ALLOC(path,pathname);
  320. if (0 != caseless_find_parents(path))
  321. {
  322. debug("caseless: caseless_create: path leading up to '%s' does not exist\n", path);
  323. return -ENOENT;
  324. }
  325. filehandle = creat(path, mode);
  326. if (filehandle != -1)
  327. {
  328. // caseless_log_process(pathname);
  329. caseless_cache_path(path);
  330. fi->fh = fh_new(filehandle);
  331. if (fi->fh == -1)
  332. {
  333. /* some error if there are no spare filehandles */
  334. return -ENOENT;
  335. }
  336. return 0; /* succeeded */
  337. }
  338. return -errno; /* failed */
  339. }
  340. static int caseless_mkdir(const char *pathname, mode_t mode)
  341. {
  342. char *path = NULL;
  343. PATH_ALLOC(path,pathname);
  344. if (0 != caseless_find_parents(path))
  345. {
  346. debug("caseless: mkdir path leading up to '%s' does not exist\n", path);
  347. return -ENOENT;
  348. }
  349. if (0 == cached_findcaseless(path))
  350. {
  351. debug("caseless: mkdir '%s' already exists\n", path);
  352. return -EEXIST;
  353. }
  354. if (0 != mkdir(path, mode))
  355. return -errno;
  356. caseless_cache_path(path);
  357. return 0;
  358. }
  359. static int caseless_unlink(const char *pathname)
  360. {
  361. char *path = NULL;
  362. PATH_ALLOC(path,pathname);
  363. if (0 != cached_findcaseless(path))
  364. {
  365. debug("caseless: unlink '%s' doesn't exist\n", path);
  366. return -EEXIST;
  367. }
  368. if (0 != unlink(path))
  369. {
  370. return -errno;
  371. }
  372. caseless_cache_missing(path);
  373. return 0;
  374. }
  375. static int caseless_access(const char *pathname, int mode)
  376. {
  377. char *path = NULL;
  378. PATH_ALLOC(path,pathname);
  379. if (0 != cached_findcaseless(path))
  380. {
  381. debug("caseless: access '%s' doesn't exist\n", path);
  382. return -EEXIST;
  383. }
  384. if (0 != access(path, mode))
  385. return -errno;
  386. return 0;
  387. }
  388. static int caseless_chmod(const char *pathname, mode_t mode)
  389. {
  390. char *path = NULL;
  391. PATH_ALLOC(path,pathname);
  392. if (0 != cached_findcaseless(path))
  393. {
  394. debug("caseless: access '%s' doesn't exist\n", path);
  395. return -EEXIST;
  396. }
  397. if (0 != chmod(path, mode))
  398. return -errno;
  399. return 0;
  400. }
  401. static int caseless_chown(const char *pathname, uid_t uid, gid_t gid)
  402. {
  403. char *path = NULL;
  404. PATH_ALLOC(path,pathname);
  405. if (0 != cached_findcaseless(path))
  406. {
  407. debug("caseless: chown '%s' doesn't exist\n", path);
  408. return -EEXIST;
  409. }
  410. if (0 != chown(path, uid, gid))
  411. return -errno;
  412. return 0;
  413. }
  414. static int caseless_truncate(const char *pathname, off_t offset)
  415. {
  416. char *path = NULL;
  417. PATH_ALLOC(path,pathname);
  418. if (0 != cached_findcaseless(path))
  419. {
  420. debug("caseless: truncate '%s' doesn't exist\n", path);
  421. return -EEXIST;
  422. }
  423. if (0 != truncate(path, offset))
  424. {
  425. int er = errno; /* save errno in case debug() messes it up */
  426. debug("caseless: truncate errno is '%d'\n", er);
  427. return -er;
  428. }
  429. return 0;
  430. }
  431. static int caseless_ftruncate(const char *pathname, off_t offset, struct fuse_file_info *fi)
  432. {
  433. if (0 != ftruncate(fh_getreal(fi->fh), offset))
  434. {
  435. int er = errno; /* save errno in case debug() messes it up */
  436. debug("caseless: ftruncate errno is '%d'\n", er);
  437. return -er;
  438. }
  439. return 0;
  440. }
  441. void *caseless_init(struct fuse_conn_info *conn)
  442. {
  443. debug("caseless_init: enter \n",1);
  444. fhtable_init();
  445. caseless_cache_init(caseless_mount.mountsource);
  446. debug("caseless_init: done \n",1);
  447. return NULL;
  448. }
  449. static struct fuse_operations caseless_operations = {
  450. .init = caseless_init,
  451. .getattr = caseless_getattr,
  452. .chmod = caseless_chmod,
  453. .chown = caseless_chown,
  454. .utimens = caseless_utimens,
  455. .setxattr = caseless_setxattr,
  456. .getxattr = caseless_getxattr,
  457. .readdir = caseless_readdir,
  458. .open = caseless_open,
  459. .create = caseless_create,
  460. .ftruncate = caseless_ftruncate,
  461. .truncate = caseless_truncate,
  462. .read = caseless_read,
  463. .write = caseless_write,
  464. .flush = caseless_flush,
  465. .release = caseless_release,
  466. .mkdir = caseless_mkdir,
  467. .unlink = caseless_unlink,
  468. .access = caseless_access,
  469. .readlink = caseless_readlink
  470. };
  471. int main(int argc, char *argv[])
  472. {
  473. char *mountsource = NULL;
  474. struct fuse_args fa = FUSE_ARGS_INIT(argc, argv);
  475. struct stat statbuf;
  476. int rv=0;
  477. // Parse the commandline ourselves so that we can find out
  478. // what the mountsource is
  479. mountsource = argv[argc-1];
  480. rv = lstat(mountsource, &statbuf);
  481. debug("caseless: mountsource %s\n", mountsource);
  482. if ( rv==0 && S_ISDIR(statbuf.st_mode) )
  483. {
  484. strncpy(caseless_mount.mountsource, mountsource, sizeof(caseless_mount.mountsource)-1);
  485. caseless_mount.mountsource[sizeof(caseless_mount.mountsource)-1] = '\0'; // ensure null termination.
  486. caseless_mount.mountsourcelen = strlen(caseless_mount.mountsource);
  487. mountsource = NULL;
  488. argv[argc] = '\0';
  489. argc--;
  490. } else {
  491. fprintf(stderr, "Final argument must be an existing directory which be presented in caseless form at the mountpoint\n");
  492. return(1);
  493. }
  494. return fuse_main(argc, argv, &caseless_operations, NULL);
  495. }