/* Copyright (C) 2012 Philip Langdale * Copyright (C) 2022 Yann Autissier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef linux /* For pread()/pwrite()/utimensat() */ #define _XOPEN_SOURCE 700 #endif #define PIFS_VERSION "0.0.1" #define FUSE_USE_VERSION 31 #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SETXATTR #include #endif // HAVE_SETXATTR #include struct options { char *dir; char *mdd; char *log; int help; int version; } options; /* macro to define options */ #define PIFS_OPTIONS(t, p, v) { t, offsetof(struct options, p), v } static struct fuse_opt pifs_opts[] = { PIFS_OPTIONS("-h", help, 1), PIFS_OPTIONS("--help", help, 1), PIFS_OPTIONS("log=%s", log, 0), PIFS_OPTIONS("mdd=%s", mdd, 0), PIFS_OPTIONS("-V", version, 1), PIFS_OPTIONS("--version", version, 1), FUSE_OPT_END }; /* define debug infos in log file */ #define FUSE_LOG(ret,...) \ fuse_log(FUSE_LOG_INFO,"pid=%-6d clock=%-8ld %-16s %-6d %-32s",getpid(),clock(),__FUNCTION__,ret,ret < 0 ? strerror(errno) : ""); \ fuse_log(FUSE_LOG_INFO,__VA_ARGS__); \ /* define metadata file path */ #define MDD_PATH(path) \ char mdd_path[PATH_MAX]; \ snprintf(mdd_path, PATH_MAX, "%s%s", options.mdd, path); \ /** Get file attributes * * Similar to stat(). The 'st_dev' and 'st_blksize' fields are * ignored. The 'st_ino' field is ignored except if the 'use_ino' * mount option is given. In that case it is passed to userspace, * but libfuse and the kernel will still assign a different * inode for internal use (called the "nodeid"). * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); * * struct stat { * dev_t st_dev; ID of device containing file * ino_t st_ino; inode number * mode_t st_mode; protection * nlink_t st_nlink; number of hard links * uid_t st_uid; user ID of owner * gid_t st_gid; group ID of owner * dev_t st_rdev; device ID (if special file) * off_t st_size; total size, in bytes * blksize_t st_blksize; blocksize for file system I/O * blkcnt_t st_blocks; number of 512B blocks allocated * time_t st_atime; time of last access * time_t st_mtime; time of last modification * time_t st_ctime; time of last status change * }; **/ static int pifs_getattr(const char *path, struct stat *buf, struct fuse_file_info *info) { MDD_PATH(path); int ret = lstat(mdd_path, buf); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } /** Read the target of a symbolic link * * The buffer should be filled with a null terminated string. The * buffer size argument includes the space for the terminating * null character. If the linkname is too long to fit in the * buffer, it should be truncated. The return value should be 0 * for success. **/ static int pifs_readlink(const char *path, char *buf, size_t bufsiz) { MDD_PATH(path); int ret = readlink(mdd_path, buf, bufsiz - 1); if (ret == -1) { FUSE_LOG(ret,"mdd=%s\n",mdd_path); return -errno; } buf[ret] = '\0'; FUSE_LOG(ret,"mdd=%s, buf=%s, bufsiz=%zu\n",mdd_path,buf,bufsiz); return 0; } /** Create a file node * * This is called for creation of all non-directory, non-symlink * nodes. If the filesystem defines a create() method, then for * regular files that will be called instead. **/ static int pifs_mknod(const char *path, mode_t mode, dev_t dev) { MDD_PATH(path); int ret = mknod(mdd_path, mode, dev); FUSE_LOG(ret,"mdd=%s, mode=%o\n",mdd_path,mode); return ret == -1 ? -errno : ret; } /** Create a directory * * Note that the mode argument may not have the type specification * bits set, i.e. S_ISDIR(mode) can be false. To obtain the * correct directory type bits use mode|S_IFDIR **/ static int pifs_mkdir(const char *path, mode_t mode) { MDD_PATH(path); int ret = mkdir(mdd_path, mode | S_IFDIR); FUSE_LOG(ret,"mdd=%s, mode=%o\n",mdd_path,mode); return ret == -1 ? -errno : ret; } /** Remove a file **/ static int pifs_unlink(const char *path) { MDD_PATH(path); int ret = unlink(mdd_path); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } /** Remove a directory **/ static int pifs_rmdir(const char *path) { MDD_PATH(path); int ret = rmdir(mdd_path); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } /** Create a symbolic link **/ static int pifs_symlink(const char *oldpath, const char *newpath) { MDD_PATH(newpath); int ret = symlink(oldpath, mdd_path); FUSE_LOG(ret,"mdd=%s, old=%s\n",mdd_path,oldpath); return ret == -1 ? -errno : ret; } /** Rename a file * * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If * RENAME_NOREPLACE is specified, the filesystem must not * overwrite *newname* if it exists and return an error * instead. If `RENAME_EXCHANGE` is specified, the filesystem * must atomically exchange the two files, i.e. both must * exist and neither may be deleted. **/ static int pifs_rename(const char *oldpath, const char *newpath, unsigned int flags) { MDD_PATH(newpath); char old_path[PATH_MAX]; snprintf(old_path, PATH_MAX, "%s%s", options.mdd, oldpath); if ((flags == 0) && (access(mdd_path, R_OK) == 0)) { FUSE_LOG(-1,"mdd=%s, old=%s, flags=%o\n",mdd_path,old_path,flags); return -1; } int ret = rename(old_path, mdd_path); FUSE_LOG(ret,"mdd=%s, old=%s, flags=%o\n",mdd_path,old_path,flags); return ret == -1 ? -errno : ret; } /** Create a hard link to a file **/ static int pifs_link(const char *oldpath, const char *newpath) { MDD_PATH(newpath); int ret = link(oldpath, mdd_path); FUSE_LOG(ret,"mdd=%s, old=%s\n",mdd_path,oldpath); return ret == -1 ? -errno : ret; } /** Change the permission bits of a file * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * int chmod(const char *, mode_t, struct fuse_file_info *fi); **/ static int pifs_chmod(const char *path, mode_t mode, struct fuse_file_info *info) { MDD_PATH(path); int ret = chmod(mdd_path, mode); FUSE_LOG(ret,"mdd=%s, mode=%o\n",mdd_path,mode); return ret == -1 ? -errno : ret; } /** Change the owner and group of a file * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * int chown(const char *, uid_t, gid_t, struct fuse_file_info *fi); * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. **/ static int pifs_chown(const char *path, uid_t owner, gid_t group, struct fuse_file_info *info) { MDD_PATH(path); int ret = chown(mdd_path, owner, group); FUSE_LOG(ret,"mdd=%s, owner=%d, group=%d\n",mdd_path,owner,group); return ret == -1 ? -errno : ret; } /** Change the size of a file * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. int truncate(const char *, off_t, struct fuse_file_info *fi); * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. **/ static int pifs_truncate(const char *path, off_t length, struct fuse_file_info *info) { MDD_PATH(path); int ret = truncate(mdd_path, length); FUSE_LOG(ret,"mdd=%s, length=%jd\n",mdd_path,(intmax_t)length); return ret == -1 ? -errno : ret; } /** Open a file * * Open flags are available in fi->flags. The following rules * apply. * * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be * filtered out / handled by the kernel. * * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) * should be used by the filesystem to check if the operation is * permitted. If the ``-o default_permissions`` mount option is * given, this check is already done by the kernel before calling * open() and may thus be omitted by the filesystem. * * - When writeback caching is enabled, the kernel may send * read requests even for files opened with O_WRONLY. The * filesystem should be prepared to handle this. * * - When writeback caching is disabled, the filesystem is * expected to properly handle the O_APPEND flag and ensure * that each write is appending to the end of the file. * * - When writeback caching is enabled, the kernel will * handle O_APPEND. However, unless all changes to the file * come through the kernel this will not work reliably. The * filesystem should thus either ignore the O_APPEND flag * (and let the kernel handle it), or return an error * (indicating that reliably O_APPEND is not available). * * Filesystem may store an arbitrary file handle (pointer, * index, etc) in fi->fh, and use this in other all other file * operations (read, write, flush, release, fsync). * * Filesystem may also implement stateless file I/O and not store * anything in fi->fh. * * There are also some flags (direct_io, keep_cache) which the * filesystem may set in fi, to change the way the file is opened. * See fuse_file_info structure in for more details. * * If this request is answered with an error code of ENOSYS * and FUSE_CAP_NO_OPEN_SUPPORT is set in * `fuse_conn_info.capable`, this is treated as success and * future calls to open will also succeed without being send * to the filesystem process. **/ static int pifs_open(const char *path, struct fuse_file_info *info) { MDD_PATH(path); int ret = open(mdd_path, info->flags); FUSE_LOG(ret,"mdd=%s, flags=%o\n",mdd_path,info->flags); if (ret != -1) info->fh = ret; return ret == -1 ? -errno : 0; } /** Read data from an open file * * Read should return exactly the number of bytes requested except * on EOF or error, otherwise the rest of the data will be * substituted with zeroes. An exception to this is when the * 'direct_io' mount option is specified, in which case the return * value of the read system call will reflect the return value of * this operation. **/ static int pifs_read(const char *path, char *buf, size_t count, off_t offset, struct fuse_file_info *info) { char buffer[4096]; FILE *fp; int size = 0; int ret = lseek(info->fh, offset, SEEK_SET); if (ret == -1) { return -errno; } dup2(info->fh, STDIN_FILENO); // fp = popen("ipfs cat", "r"); fp = popen("cat", "r"); if (fp == 0) { perror("popen(3) failed"); return -1; } do { ret = fread(buffer, sizeof(char), sizeof(buffer)-1, fp); if (ret == -1 && errno != EAGAIN) { return -errno; } sprintf(&buf[offset+size], "%s", buffer); size += ret; count -= ret; } while( ret == sizeof(buffer)-1 && count > 0 ); buf[offset + size] = '\0'; pclose(fp); return size; } /** Write data to an open file * * Write should return exactly the number of bytes requested * except on error. An exception to this is when the 'direct_io' * mount option is specified (see read operation). * * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is * expected to reset the setuid and setgid bits. **/ static int pifs_write(const char *path, const char *buf, size_t count, off_t offset, struct fuse_file_info *info) { int fd[2]; int ret = lseek(info->fh, offset, SEEK_SET); if (ret == -1) { return -errno; } if(pipe(fd)){ perror("pipe(2) failed"); return -1; } switch(fork()){ case -1: perror("fork(2) failed"); return -1; case 0: // child dup2(fd[0], STDIN_FILENO); dup2(info->fh, STDOUT_FILENO); close(fd[0]); close(fd[1]); // ret = execlp("ipfs", "ipfs", "add", "-q", NULL); ret = execlp("cat", "cat", NULL); if (ret == -1) { return -errno; } break; default: // parent close(fd[0]); ret = write(fd[1], buf, count); if (ret == -1) { return -errno; } close(fd[1]); } return count; } /** Get file system statistics * * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored **/ static int pifs_statfs(const char *path, struct statvfs *buf) { MDD_PATH(path); int ret = statvfs(mdd_path, buf); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } /** Possibly flush cached data * * BIG NOTE: This is not equivalent to fsync(). It's not a * request to sync dirty data. * * Flush is called on each close() of a file descriptor, as opposed to * release which is called on the close of the last file descriptor for * a file. Under Linux, errors returned by flush() will be passed to * userspace as errors from close(), so flush() is a good place to write * back any cached dirty data. However, many applications ignore errors * on close(), and on non-Linux systems, close() may succeed even if flush() * returns an error. For these reasons, filesystems should not assume * that errors returned by flush will ever be noticed or even * delivered. * * NOTE: The flush() method may be called more than once for each * open(). This happens if more than one file descriptor refers to an * open file handle, e.g. due to dup(), dup2() or fork() calls. It is * not possible to determine if a flush is final, so each flush should * be treated equally. Multiple write-flush sequences are relatively * rare, so this shouldn't be a problem. * * Filesystems shouldn't assume that flush will be called at any * particular point. It may be called more times than expected, or not * at all. * * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html **/ int pifs_flush(const char *, struct fuse_file_info *info); /** Release an open file * * Release is called when there are no more references to an open * file: all file descriptors are closed and all memory mappings * are unmapped. * * For every open() call there will be exactly one release() call * with the same flags and file handle. It is possible to * have a file opened more than once, in which case only the last * release will mean, that no more reads/writes will happen on the * file. The return value of release is ignored. **/ static int pifs_release(const char *path, struct fuse_file_info *info) { MDD_PATH(path); int ret = close(info->fh); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } /** Synchronize file contents * * If the datasync parameter is non-zero, then only the user data * should be flushed, not the meta data. **/ static int pifs_fsync(const char *path, int datasync, struct fuse_file_info *info) { MDD_PATH(path); int ret = datasync ? fdatasync(info->fh) : fsync(info->fh); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } #ifdef HAVE_SETXATTR /** Set extended attributes **/ static int pifs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { MDD_PATH(path); int ret = setxattr(mdd_path, name, value, size, flags); FUSE_LOG(ret,"mdd=%s, name=%s, value=%s, size=%zu, flags=%o\n",mdd_path,name,value,size,flags); return ret == -1 ? -errno : ret; } /** Get extended attributes **/ static int pifs_getxattr(const char *path, const char *name, char *value, size_t size) { MDD_PATH(path); int ret = getxattr(mdd_path, name, value, size); FUSE_LOG(ret,"mdd=%s, name=%s, value=%s, size=%zu\n",mdd_path,name,value,size); return ret == -1 ? -errno : ret; } /** List extended attributes **/ static int pifs_listxattr(const char *path, char *list, size_t size) { MDD_PATH(path); int ret = listxattr(mdd_path, list, size); FUSE_LOG(ret,"mdd=%s, list=%s, size=%zu\n",mdd_path,list,size); return ret == -1 ? -errno : ret; } /** Remove extended attributes **/ static int pifs_removexattr(const char *path, const char *name) { MDD_PATH(path); int ret = removexattr(mdd_path, name); FUSE_LOG(ret,"mdd=%s, name=%s\n",mdd_path,name); return ret == -1 ? -errno : ret; } #endif // HAVE_SETXATTR /** Open directory * * Unless the 'default_permissions' mount option is given, * this method should check if opendir is permitted for this * directory. Optionally opendir may also return an arbitrary * filehandle in the fuse_file_info structure, which will be * passed to readdir, releasedir and fsyncdir. **/ static int pifs_opendir(const char *path, struct fuse_file_info *info) { MDD_PATH(path); DIR *dir = opendir(mdd_path); if (!dir){ FUSE_LOG(-1,"mdd=%s\n",mdd_path); return -errno; } FUSE_LOG(0,"mdd=%s\n",mdd_path); info->fh = (uint64_t) dir; return !dir ? -errno : 0; } /** Read directory * * The filesystem may choose between two modes of operation: * * 1) The readdir implementation ignores the offset parameter, and * passes zero to the filler function's offset. The filler * function will not return '1' (unless an error happens), so the * whole directory is read in a single readdir operation. * * 2) The readdir implementation keeps track of the offsets of the * directory entries. It uses the offset parameter and always * passes non-zero offset to the filler function. When the buffer * is full (or an error happens) the filler function will return * '1'. * * When FUSE_READDIR_PLUS is not set, only some parameters of the * fill function (the fuse_fill_dir_t parameter) are actually used: * The file type (which is part of stat::st_mode) is used. And if * fuse_config::use_ino is set, the inode (stat::st_ino) is also * used. The other fields are ignored when FUSE_READDIR_PLUS is not * set. **/ static int pifs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *info, enum fuse_readdir_flags flags) { MDD_PATH(path); DIR *dir = (DIR *) info->fh; if (offset) { seekdir(dir, offset); } int ret; do { errno = 0; struct dirent *de = readdir(dir); if (!de) { if (errno) { FUSE_LOG(-1,"mdd=%s\n",mdd_path); return -errno; } else { break; } } ret = filler(buf, de->d_name, NULL, de->d_off, 0); FUSE_LOG(ret,"mdd=%s, name=%s\n",mdd_path,de->d_name); } while (ret == 0); return 0; } /** Release directory * * If the directory has been removed after the call to opendir, the * path parameter will be NULL. **/ static int pifs_releasedir(const char *path, struct fuse_file_info *info) { MDD_PATH(path); int ret = closedir((DIR *)info->fh); FUSE_LOG(ret,"mdd=%s\n",mdd_path); return ret == -1 ? -errno : ret; } /** Synchronize directory contents * * If the directory has been removed after the call to opendir, the * path parameter will be NULL. * * If the datasync parameter is non-zero, then only the user data * should be flushed, not the meta data **/ static int pifs_fsyncdir(const char *path, int datasync, struct fuse_file_info *info) { MDD_PATH(path); int fd = dirfd((DIR *)info->fh); if (fd == -1) { FUSE_LOG(-1,"mdd=%s\n",mdd_path); return -errno; } int ret = datasync ? fdatasync(fd) : fsync(fd); FUSE_LOG(ret,"mdd=%s, datasync=%d\n",mdd_path,datasync); return ret == -1 ? -errno : ret; } /** * Initialize filesystem * * The return value will passed in the `private_data` field of * `struct fuse_context` to all file operations, and as a * parameter to the destroy() method. It overrides the initial * value provided to fuse_main() / fuse_new(). **/ void *pifs_init(struct fuse_conn_info *conn, struct fuse_config *cfg); /** * Clean up filesystem * * Called on filesystem exit. **/ void pifs_destroy(void *private_data); /** * Check file access permissions * * This will be called for the access() system call. If the * 'default_permissions' mount option is given, this method is not * called. * * This method is not called under Linux kernel versions 2.4.x **/ static int pifs_access(const char *path, int mode) { MDD_PATH(path); int ret = access(mdd_path, mode); FUSE_LOG(ret,"mdd=%s, mode=%d\n",mdd_path,mode); return ret == -1 ? -errno : ret; } /** * Create and open a file * * If the file does not exist, first create it with the specified * mode, and then open it. * * If this method is not implemented or under Linux kernel * versions earlier than 2.6.15, the mknod() and open() methods * will be called instead. **/ static int pifs_create(const char *path, mode_t mode, struct fuse_file_info *info) { MDD_PATH(path); int ret = creat(mdd_path, mode); FUSE_LOG(ret,"mdd=%s, mode=%o\n",mdd_path,mode); info->fh = ret; return ret == -1 ? -errno : 0; } /** * Perform POSIX file locking operation * * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. * * For the meaning of fields in 'struct flock' see the man page * for fcntl(2). The l_whence field will always be set to * SEEK_SET. * * For checking lock ownership, the 'fuse_file_info->owner' * argument must be used. * * For F_GETLK operation, the library will first check currently * held locks, and if a conflicting lock is found it will return * information without calling this method. This ensures, that * for local locks the l_pid field is correctly filled in. The * results may not be accurate in case of race conditions and in * the presence of hard links, but it's unlikely that an * application would rely on accurate GETLK results in these * cases. If a conflicting lock is not found, this method will be * called, and the filesystem may fill out l_pid by a meaningful * value, or it may leave this field zero. * * For F_SETLK and F_SETLKW the l_pid field will be set to the pid * of the process performing the locking operation. * * Note: if this method is not implemented, the kernel will still * allow file locking to work locally. Hence it is only * interesting for network filesystems and similar. * * struct flock { * ... * short l_type; * Type of lock: F_RDLCK, * F_WRLCK, F_UNLCK * short l_whence; * How to interpret l_start: * SEEK_SET, SEEK_CUR, SEEK_END * off_t l_start; * Starting offset for lock * off_t l_len; * Number of bytes to lock * pid_t l_pid; * PID of process blocking our lock * (F_GETLK only) * ... * }; * * F_SETLK (struct flock *) * Acquire a lock (when l_type is F_RDLCK or F_WRLCK) or release * a lock (when l_type is F_UNLCK) on the bytes specified by the * l_whence, l_start, and l_len fields of lock. If a conflicting * lock is held by another process, this call returns -1 and sets * errno to EACCES or EAGAIN. * F_SETLKW (struct flock *) * As for F_SETLK, but if a conflicting lock is held on the file, * then wait for that lock to be released. If a signal is caught * while waiting, then the call is interrupted and (after the * signal handler has returned) returns immediately (with return * value -1 and errno set to EINTR; see signal(7)). * F_GETLK (struct flock *) * On input to this call, lock describes a lock we would like to * place on the file. If the lock could be placed, fcntl() does * not actually place it, but returns F_UNLCK in the l_type field * of lock and leaves the other fields of the structure unchanged. * If one or more incompatible locks would prevent this lock being * placed, then fcntl() returns details about one of these locks * in the l_type, l_whence, l_start, and l_len fields of lock and * sets l_pid to be the PID of the process holding that lock. **/ static int pifs_lock(const char *path, struct fuse_file_info *info, int cmd, struct flock *lock) { MDD_PATH(path); int ret = fcntl(info->fh, cmd, lock); FUSE_LOG(ret,"mdd=%s, cmd=%d(%s), lock=%d(%s)\n",mdd_path, cmd,(cmd==F_GETLK?"F_GETLK":(cmd==F_SETLK?"F_SETLK":"F_SETLKW")), lock->l_type,(lock->l_type == F_RDLCK? "F_RDLCK":(lock->l_type == F_WRLCK?"F_WRLCK":"F_UNLCK"))); return ret == -1 ? -errno : ret; } /** * Change the access and modification times of a file with * nanosecond resolution * * This supersedes the old utime() interface. New applications * should use this. * * `fi` will always be NULL if the file is not currently open, but * may also be NULL if the file is open. * * See the utimensat(2) man page for details. **/ static int pifs_utimens(const char *path, const struct timespec times[2], struct fuse_file_info *info) { MDD_PATH(path); DIR *dir = opendir(options.mdd); if (!dir) { FUSE_LOG(-1,"mdd=%s\n",options.mdd); return -errno; } int ret = utimensat(dirfd(dir), basename((char *) path), times, 0); FUSE_LOG(ret,"mdd=%s\n",mdd_path); closedir(dir); return ret == -1 ? -errno : ret; } static struct fuse_operations pifs_ops = { .getattr = pifs_getattr, .readlink = pifs_readlink, .mknod = pifs_mknod, .mkdir = pifs_mkdir, .rmdir = pifs_rmdir, .unlink = pifs_unlink, .symlink = pifs_symlink, .rename = pifs_rename, .link = pifs_link, .chmod = pifs_chmod, .chown = pifs_chown, .truncate = pifs_truncate, .utimens = pifs_utimens, .open = pifs_open, .read = pifs_read, .write = pifs_write, .statfs = pifs_statfs, .release = pifs_release, .fsync = pifs_fsync, #ifdef HAVE_SETXATTR .setxattr = pifs_setxattr, .getxattr = pifs_getxattr, .listxattr = pifs_listxattr, .removexattr = pifs_removexattr, #endif // HAVE_SETXATTR .opendir = pifs_opendir, .readdir = pifs_readdir, .releasedir = pifs_releasedir, .fsyncdir = pifs_fsyncdir, .access = pifs_access, .create = pifs_create, .lock = pifs_lock, }; static void pifs_log(enum fuse_log_level level, const char *fmt, va_list ap) { FILE *fp = fopen(options.log, "a"); vfprintf(fp, fmt, ap); fclose(fp); } static void usage(const char *progname) { printf("Usage: %s [options] \n" "pifs options:\n" " -o log= log file to trace fuse calls\n" " -o mdd= metadata directory to store ipfs hashes\n" "\n", progname); } int main (int argc, char *argv[]) { int ret; FILE *fp; struct fuse_args args = FUSE_ARGS_INIT(argc, argv); memset(&options, 0, sizeof(struct options)); if (fuse_opt_parse(&args, &options, pifs_opts, NULL) == -1) { return -1; } if (options.help) { usage(argv[0]); assert(fuse_opt_add_arg(&args, "--help") == 0); args.argv[0][0] = '\0'; } else if (options.version) { fprintf(stdout, "pifs version %s\n", PIFS_VERSION); return 0; } else { options.dir = args.argv[1]; if (!options.mdd) { fprintf(stderr, "%s: Metadata directory must be specified with -o mdd=\n", argv[0]); return 1; } if (access(options.mdd, R_OK | W_OK | X_OK) == -1) { fprintf(stderr, "%s: Cannot access metadata directory '%s': %s\n", argv[0], options.mdd, strerror(errno)); return 1; } if (options.log != NULL) { if ((fp = fopen(options.log, "w")) == NULL) { fprintf(stderr, "%s: Cannot write to log file '%s': %s\n", argv[0], options.log, strerror(errno)); return 1; } fclose(fp); fuse_set_log_func((fuse_log_func_t) pifs_log); FUSE_LOG(0,"bin=%s, dir=%s, mdd=%s, log=%s\n", args.argv[0], options.dir, options.mdd ,options.log); } if (options.dir == NULL) { fprintf(stderr, "%s: No mountpoint specified\n", argv[0]); return 2; } else if (strcmp(options.dir,"-o") == 0) { fprintf(stderr, "%s: Invalid option argument '%s'\n", argv[0], args.argv[2]); return 3; } else if (access(options.dir, W_OK | X_OK) == -1) { fprintf(stderr, "%s: Cannot mount directory '%s': %s\n", argv[0], options.dir, strerror(errno)); return 4; } } ret = fuse_main(args.argc, args.argv, &pifs_ops, NULL); /* The following error codes may be returned from fuse_main(): * 1: Invalid option arguments * 2: No mount point specified * 3: FUSE setup failed * 4: Mounting failed * 5: Failed to daemonize (detach from session) * 6: Failed to set up signal handlers * 7: An error occurred during the life of the file system * @return 0 on success, nonzero on failure */ fuse_opt_free_args(&args); return ret; }