123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- /*
- * Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * This component and the accompanying materials are made available
- * under the terms of the License "Eclipse Public License v1.0"
- * which accompanies this distribution, and is available
- * at the URL "http://www.eclipse.org/legal/epl-v10.html".
- *
- * Initial Contributors:
- * Nokia Corporation - initial contribution.
- *
- * Contributors:
- *
- * Description:
- * caseless_fuse
- *
- * Fuse filesystem for case insensitivity and path separator correction
- * (i.e converts '\' in filenames to '/')
- *
- */
- #define FUSE_USE_VERSION 26
- #include <fuse.h>
- #include <stdio.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <malloc.h>
- #include <dlfcn.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/param.h>
- #include <dirent.h>
- #include <string.h>
- #include <alloca.h>
- #include <stdarg.h>
- #include <sys/times.h>
- #include <unistd.h>
- #include "pathcache.h"
- #include "filehandles.h"
- #include "debug.h"
- /* Context structure representing information needed for one "mountpoint" */
- typedef struct {
- char mountsource[MAXPATHLEN];
- int mountsourcelen;
- } caseless_fs;
- caseless_fs caseless_mount;
- /* convertpath
- source - input path, possibly with windows path separators (\)
- dest - output path in unix format
- Copy input path and convert backslashes to forward ones as we work.
- use strpbrk preferably so that we're getting the C library's best
- efforts to work for us.
- */
- void convertpath(const char source[], char *dest)
- {
- strcpy(dest,source);
- char *convert = dest;
- do
- {
- convert = strpbrk(convert, "\\");
- if (convert)
- {
- *convert = '/';
- continue;
- } else {
- break;
- }
- } while (convert != NULL);
-
- int e = strlen(dest);
- if (e > 0 && dest[e-1] == '/')
- dest[e-1] = '\0'; //slashes on the ends of paths confuses the cache
- }
- // Creates a path string on the stack that has the caseless mountsource
- // prepended onto it and has all slashes converted
- #define PATH_ALLOC(path, input_path) { \
- debug("PATH_ALLOC: input: '%s'\n", input_path); \
- path = alloca(strlen(input_path) + caseless_mount.mountsourcelen + 5); \
- debug("PATH_ALLOC: mountsource: '%s'\n", caseless_mount.mountsource); \
- strcpy(path, caseless_mount.mountsource); \
- convertpath(input_path, path+caseless_mount.mountsourcelen); \
- debug("PATH_ALLOC: output: '%s'\n", path); \
- }
- int caseless_find_parents(char *path)
- // Find the directory of a file/subdir that's going to be created
- {
- // find the component of the path that must exist already.
- char *lookback = path+strlen(path)-1;
- if (*lookback == '/') lookback--; //ignore eol-slashes
- debug("caseless: find_parents of '%s'", path);
- while (lookback > path)
- {
- if (*lookback == '/')
- {
- *lookback = '\0';
- int fcr = cached_findcaseless(path);
- *lookback = '/';
- return fcr;
- }
- lookback--;
- }
- return 0;
- }
- /*
- caseless_log_process
- Reports the commmandline of the process that initiated
- the current fuse operation e.g. you can see what command
- created a file.
- */
- static void caseless_log_process(const char *pathname)
- {
- struct fuse_context *fc = fuse_get_context();
- char *cmdfile = alloca(30);
- snprintf(cmdfile,30,"/proc/%d/cmdline", fc->pid);
- cmdfile[29] = '\0';
- int cmdline_fh = open(cmdfile, O_RDONLY );
- if (cmdline_fh != -1)
- {
-
- char *cmdline = alloca(512);
- int br = read(cmdline_fh, cmdline, 511);
- close(cmdline_fh);
- if (br > 0)
- {
- int i;
- for (i=0; i < br; i++)
- if (cmdline[i]=='\0') cmdline[i] = ' ';
- cmdline[br] = '\0';
- debug("\n\ncaseless: command that opened '%s' was: '%s'\n\n", pathname, cmdline);
- }
- }
- }
- static int caseless_open(const char *pathname, struct fuse_file_info *fi)
- {
- int rv=0;
- int er=0;
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- debug("\n\ncaseless: open '%s' rv=%d, errno=%d\n\n\n", pathname, rv, errno);
- // caseless_log_process(pathname);
- if (0 != cached_findcaseless(path) && !(fi->flags & O_CREAT))
- {
- debug("caseless: openattempt: %s\n", path);
- return -ENOENT;
- }
-
- errno = 0;
- rv = open(path, fi->flags);
- er = errno;
- debug("caseless: openattempt: rv is %d and errno is %d\n", rv,errno);
- if (rv != -1)
- {
- caseless_cache_path(path);
- fi->fh = fh_new(rv);
- if (fi->fh == -1)
- {
- /* some error if there are no spare filehandles */
- return -ENOENT;
- }
- }
- return -er;
- }
- /*
- Function for changing file time with nanosecond precision.
- */
- static int caseless_utimens(const char *pathname, const struct timespec tv[2])
- {
- int rv = 0;
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 == cached_findcaseless(path))
- {
- rv = utimensat(0, path,tv, 0); // absolute path should allow dirfd parameter to be ignored
- if (0 != rv)
- rv = -errno;
- } else {
- errno = ENOENT;
- }
- return rv;
- }
- static int caseless_getattr(const char *pathname, struct stat *buf)
- {
- int rv=0;
- int er=0;
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- int found = cached_findcaseless(path);
- if (found != 0)
- {
- debug("caseless: getattr not found: %s\n", path);
- return -ENOENT;
- }
- errno = 0;
- rv = lstat(path, buf);
- er = errno;
- debug("caseless: getattr found: %s rv=%d, errno=%d\n", path, rv, er);
- if ( rv <= 0 && errno == ENOENT)
- {
- debug("\n\ncaseless: tried to stat '%s' rv=%d, errno=%d\n\n\n", path, rv, errno);
- }
- if (rv == 0)
- {
- caseless_cache_path(path);
- }
-
- return -er;
- }
- /* helper function with debugging and NULL protection */
- char *case_corrected(char *path)
- {
- if (!path) return path;
-
- if (0 == cached_findcaseless(path))
- {
- debug("Found: %s \n", path);
- }
- else
- {
- debug( "Path Not found: %s\n", path);
- return NULL;
- }
- return path;
- }
- static int caseless_readdir(const char *pathname, void *buf,
- fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi)
- {
- DIR *dirh=NULL;
- int er=0;
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- dirh = opendir(path);
- debug("\n\ncaseless: opendir asked:'%s' trying:'%s' \n", pathname, path);
- if ( dirh == NULL )
- {
- er = errno;
- if (er == ENOENT)
- {
- debug("\n\ncaseless: readdir failed - asked: '%s' trying: '%s' \n\n\n", pathname, path);
- path = case_corrected(path);
- if (path)
- {
- dirh = opendir(path);
- if (!dirh)
- er = errno;
- else
- er = 0;
- } else {
- er = ENOENT;
- }
- }
- }
- if ( dirh != NULL )
- {
- struct dirent *entry;
- struct stat attrs;
-
- while ((entry = readdir(dirh)) != NULL)
- {
- memset(&attrs, 0, sizeof(attrs));
- attrs.st_ino = entry->d_ino;
- attrs.st_mode = entry->d_type << 12;
- if (filler(buf, entry->d_name, &attrs, 0))
- break;
- }
- closedir(dirh);
-
- }
- return -er;
- }
- static int caseless_read(const char *pathname, char *buf, size_t size, off_t offset,
- struct fuse_file_info *fi)
- {
- int rv = pread(fh_getreal(fi->fh), buf, size, offset);
- if (rv == -1)
- return -errno;
- return rv;
- }
- static int caseless_write(const char *pathname, const char *buf, size_t size,
- off_t offset, struct fuse_file_info *fi)
- {
- int rv = pwrite(fh_getreal(fi->fh), buf, size, offset);
- if (rv == -1)
- return -errno;
- return rv;
- }
- static int caseless_flush(const char *pathname, struct fuse_file_info *fi)
- {
- return 0;
- }
- static int caseless_release(const char *pathname, struct fuse_file_info *fi)
- {
- debug("\ncaseless: close %s\n", pathname);
- fh_close(fi->fh);
- return 0;
- }
- static int caseless_readlink (const char *pathname, char *buf, size_t bufsiz)
- {
- ssize_t s;
- s = readlink(pathname, buf, bufsiz);
- if (s == -1)
- return -errno;
- return 0;
- }
- // int (*getxattr) (const char *, const char *, char *, size_t);
- // int (*listxattr) (const char *, char *, size_t);
- // int (*removexattr) (const char *, const char *);
- static int caseless_getxattr(const char *path, const char *name,
- char *value, size_t size)
- {
- // Not implementing a pass-through yet
- debug("\ncaseless: getxattr '%s' into buffer size '%d' \n", name, size);
- if (size > 0 ) value[0] = '\0';
- return 0;
- }
- static int caseless_setxattr(const char *path, const char *name,
- const char *value, size_t size, int i)
- {
- // Not implementing a pass-through yet
- debug("\ncaseless: setxattr '%s' to '%s' \n", name, value);
- return 0;
- }
- static int caseless_create(const char *pathname, mode_t mode, struct fuse_file_info *fi)
- {
- int filehandle=0;
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != caseless_find_parents(path))
- {
- debug("caseless: caseless_create: path leading up to '%s' does not exist\n", path);
- return -ENOENT;
- }
- filehandle = creat(path, mode);
- if (filehandle != -1)
- {
- // caseless_log_process(pathname);
- caseless_cache_path(path);
- fi->fh = fh_new(filehandle);
- if (fi->fh == -1)
- {
- /* some error if there are no spare filehandles */
- return -ENOENT;
- }
- return 0; /* succeeded */
- }
- return -errno; /* failed */
- }
- static int caseless_mkdir(const char *pathname, mode_t mode)
- {
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != caseless_find_parents(path))
- {
- debug("caseless: mkdir path leading up to '%s' does not exist\n", path);
- return -ENOENT;
- }
- if (0 == cached_findcaseless(path))
- {
- debug("caseless: mkdir '%s' already exists\n", path);
- return -EEXIST;
- }
- if (0 != mkdir(path, mode))
- return -errno;
- caseless_cache_path(path);
- return 0;
- }
- static int caseless_unlink(const char *pathname)
- {
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != cached_findcaseless(path))
- {
- debug("caseless: unlink '%s' doesn't exist\n", path);
- return -EEXIST;
- }
- if (0 != unlink(path))
- {
- return -errno;
- }
- caseless_cache_missing(path);
- return 0;
- }
- static int caseless_access(const char *pathname, int mode)
- {
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != cached_findcaseless(path))
- {
- debug("caseless: access '%s' doesn't exist\n", path);
- return -EEXIST;
- }
- if (0 != access(path, mode))
- return -errno;
- return 0;
- }
- static int caseless_chmod(const char *pathname, mode_t mode)
- {
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != cached_findcaseless(path))
- {
- debug("caseless: access '%s' doesn't exist\n", path);
- return -EEXIST;
- }
- if (0 != chmod(path, mode))
- return -errno;
- return 0;
- }
- static int caseless_chown(const char *pathname, uid_t uid, gid_t gid)
- {
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != cached_findcaseless(path))
- {
- debug("caseless: chown '%s' doesn't exist\n", path);
- return -EEXIST;
- }
- if (0 != chown(path, uid, gid))
- return -errno;
- return 0;
- }
- static int caseless_truncate(const char *pathname, off_t offset)
- {
- char *path = NULL;
- PATH_ALLOC(path,pathname);
- if (0 != cached_findcaseless(path))
- {
- debug("caseless: truncate '%s' doesn't exist\n", path);
- return -EEXIST;
- }
- if (0 != truncate(path, offset))
- {
- int er = errno; /* save errno in case debug() messes it up */
- debug("caseless: truncate errno is '%d'\n", er);
- return -er;
- }
- return 0;
- }
- static int caseless_ftruncate(const char *pathname, off_t offset, struct fuse_file_info *fi)
- {
- if (0 != ftruncate(fh_getreal(fi->fh), offset))
- {
- int er = errno; /* save errno in case debug() messes it up */
- debug("caseless: ftruncate errno is '%d'\n", er);
- return -er;
- }
- return 0;
- }
- void *caseless_init(struct fuse_conn_info *conn)
- {
- debug("caseless_init: enter \n",1);
- fhtable_init();
- caseless_cache_init(caseless_mount.mountsource);
- debug("caseless_init: done \n",1);
- return NULL;
- }
- static struct fuse_operations caseless_operations = {
- .init = caseless_init,
- .getattr = caseless_getattr,
- .chmod = caseless_chmod,
- .chown = caseless_chown,
- .utimens = caseless_utimens,
- .setxattr = caseless_setxattr,
- .getxattr = caseless_getxattr,
- .readdir = caseless_readdir,
- .open = caseless_open,
- .create = caseless_create,
- .ftruncate = caseless_ftruncate,
- .truncate = caseless_truncate,
- .read = caseless_read,
- .write = caseless_write,
- .flush = caseless_flush,
- .release = caseless_release,
- .mkdir = caseless_mkdir,
- .unlink = caseless_unlink,
- .access = caseless_access,
- .readlink = caseless_readlink
- };
- int main(int argc, char *argv[])
- {
- char *mountsource = NULL;
- struct fuse_args fa = FUSE_ARGS_INIT(argc, argv);
- struct stat statbuf;
- int rv=0;
- // Parse the commandline ourselves so that we can find out
- // what the mountsource is
- mountsource = argv[argc-1];
- rv = lstat(mountsource, &statbuf);
- debug("caseless: mountsource %s\n", mountsource);
-
- if ( rv==0 && S_ISDIR(statbuf.st_mode) )
- {
- strncpy(caseless_mount.mountsource, mountsource, sizeof(caseless_mount.mountsource)-1);
- caseless_mount.mountsource[sizeof(caseless_mount.mountsource)-1] = '\0'; // ensure null termination.
- caseless_mount.mountsourcelen = strlen(caseless_mount.mountsource);
- mountsource = NULL;
- argv[argc] = '\0';
- argc--;
- } else {
- fprintf(stderr, "Final argument must be an existing directory which be presented in caseless form at the mountpoint\n");
- return(1);
- }
- return fuse_main(argc, argv, &caseless_operations, NULL);
- }
|