diff options
Diffstat (limited to 'src/dumm/cowfs.c')
-rw-r--r-- | src/dumm/cowfs.c | 980 |
1 files changed, 0 insertions, 980 deletions
diff --git a/src/dumm/cowfs.c b/src/dumm/cowfs.c deleted file mode 100644 index ac581fed1..000000000 --- a/src/dumm/cowfs.c +++ /dev/null @@ -1,980 +0,0 @@ -/* - * Copyright (C) 2009 Tobias Brunner - * Copyright (C) 2007 Martin Willi - * HSR Hochschule fuer Technik Rapperswil - * Copyright (C) 2001-2007 Miklos Szeredi - * - * Based on example shipped with FUSE. - * - * 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 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * 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 General Public License - * for more details. - */ - - -#define FUSE_USE_VERSION 26 -#define _GNU_SOURCE - -#include <fuse.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <dirent.h> -#include <errno.h> -#include <sys/time.h> - -#include "cowfs.h" - -#include <library.h> -#include <utils/debug.h> -#include <threading/thread.h> -#include <threading/rwlock.h> -#include <collections/linked_list.h> - -/** define _XOPEN_SOURCE 500 fails when using libstrongswan, define popen */ -extern ssize_t pread(int fd, void *buf, size_t count, off_t offset); -extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); - -typedef struct private_cowfs_t private_cowfs_t; - -struct private_cowfs_t { - /** public cowfs interface */ - cowfs_t public; - /** fuse channel to mountpoint */ - struct fuse_chan *chan; - /** fuse handle */ - struct fuse *fuse; - /** mountpoint of cowfs FUSE */ - char *mount; - /** master filesystem path */ - char *master; - /** host filesystem path */ - char *host; - /** overlay filesystems */ - linked_list_t *overlays; - /** lock for overlays */ - rwlock_t *lock; - /** fd of read only master filesystem */ - int master_fd; - /** copy on write overlay to master */ - int host_fd; - /** thread processing FUSE */ - thread_t *thread; -}; - -typedef struct overlay_t overlay_t; - -/** - * data for overlay filesystems - */ -struct overlay_t { - /** path to overlay */ - char *path; - /** overlay fd */ - int fd; -}; - -/** - * destroy an overlay - */ -static void overlay_destroy(overlay_t *this) -{ - close(this->fd); - free(this->path); - free(this); -} - -CALLBACK(overlay_equals, bool, - overlay_t *this, va_list args) -{ - overlay_t *other; - - VA_ARGS_VGET(args, other); - return streq(this->path, other->path); -} - -/** - * remove and destroy the overlay with the given absolute path. - * returns FALSE, if not found. - */ -static bool overlay_remove(private_cowfs_t *this, char *path) -{ - overlay_t over, *current; - over.path = path; - if (!this->overlays->find_first(this->overlays, overlay_equals, - (void**)¤t, &over)) - { - return FALSE; - } - this->overlays->remove(this->overlays, current, NULL); - overlay_destroy(current); - return TRUE; -} - -/** - * get this pointer stored in fuse context - */ -static private_cowfs_t *get_this() -{ - return (fuse_get_context())->private_data; -} - -/** - * make a path relative - */ -static void rel(const char **path) -{ - if (**path == '/') - { - (*path)++; - } - if (**path == '\0') - { - *path = "."; - } -} - -/** - * get the highest overlay in which path exists - */ -static int get_rd(const char *path) -{ - overlay_t *over; - enumerator_t *enumerator; - private_cowfs_t *this = get_this(); - - this->lock->read_lock(this->lock); - enumerator = this->overlays->create_enumerator(this->overlays); - while (enumerator->enumerate(enumerator, (void**)&over)) - { - if (faccessat(over->fd, path, F_OK, 0) == 0) - { - int fd = over->fd; - enumerator->destroy(enumerator); - this->lock->unlock(this->lock); - return fd; - } - } - enumerator->destroy(enumerator); - this->lock->unlock(this->lock); - - if (faccessat(this->host_fd, path, F_OK, 0) == 0) - { - return this->host_fd; - } - return this->master_fd; -} - -/** - * get the highest overlay available, to write something - */ -static int get_wr(const char *path) -{ - overlay_t *over; - private_cowfs_t *this = get_this(); - int fd = this->host_fd; - this->lock->read_lock(this->lock); - if (this->overlays->get_first(this->overlays, (void**)&over) == SUCCESS) - { - fd = over->fd; - } - this->lock->unlock(this->lock); - return fd; -} - -/** - * create full "path" at "wr" the same way they exist at "rd" - */ -static bool clone_path(int rd, int wr, const char *path) -{ - char *pos, *full; - struct stat st; - full = strdupa(path); - pos = full; - - while ((pos = strchr(pos, '/'))) - { - *pos = '\0'; - if (fstatat(wr, full, &st, 0) < 0) - { - /* TODO: handle symlinks!? */ - if (fstatat(rd, full, &st, 0) < 0) - { - return FALSE; - } - if (mkdirat(wr, full, st.st_mode) < 0) - { - return FALSE; - } - } - *pos = '/'; - pos++; - } - return TRUE; -} - -/** - * copy a (special) file from a readonly to a read-write overlay - */ -static int copy(const char *path) -{ - char *buf[4096]; - int len; - int rd, wr; - int from, to; - struct stat st; - - rd = get_rd(path); - wr = get_wr(path); - - if (rd == wr) - { - /* already writeable */ - return wr; - } - if (fstatat(rd, path, &st, 0) < 0) - { - return -1; - } - if (!clone_path(rd, wr, path)) - { - return -1; - } - if (mknodat(wr, path, st.st_mode, st.st_rdev) < 0) - { - return -1; - } - /* copy if no special file */ - if (st.st_size) - { - from = openat(rd, path, O_RDONLY, st.st_mode); - if (from < 0) - { - return -1; - } - to = openat(wr, path, O_WRONLY , st.st_mode); - if (to < 0) - { - close(from); - return -1; - } - while ((len = read(from, buf, sizeof(buf))) > 0) - { - if (write(to, buf, len) < len) - { - /* TODO: only on len < 0 ? */ - close(from); - close(to); - return -1; - } - } - close(from); - close(to); - if (len < 0) - { - return -1; - } - } - return wr; -} - -/** - * FUSE getattr method - */ -static int cowfs_getattr(const char *path, struct stat *stbuf) -{ - rel(&path); - - if (fstatat(get_rd(path), path, stbuf, AT_SYMLINK_NOFOLLOW) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE access method - */ -static int cowfs_access(const char *path, int mask) -{ - rel(&path); - - if (faccessat(get_rd(path), path, mask, 0) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE readlink method - */ -static int cowfs_readlink(const char *path, char *buf, size_t size) -{ - int res; - - rel(&path); - - res = readlinkat(get_rd(path), path, buf, size - 1); - if (res < 0) - { - return -errno; - } - buf[res] = '\0'; - return 0; -} - -/** - * get a directory stream of two concatenated paths - */ -static DIR* get_dir(char *dir, const char *subdir) -{ - char *full; - - if (dir == NULL) - { - return NULL; - } - - full = alloca(strlen(dir) + strlen(subdir) + 1); - strcpy(full, dir); - strcat(full, subdir); - - return opendir(full); -} - -/** - * check if a directory stream contains a directory - */ -static bool contains_dir(DIR *d, char *dirname) -{ - struct dirent *ent; - - rewinddir(d); - while ((ent = readdir(d))) - { - if (streq(ent->d_name, dirname)) - { - return TRUE; - } - } - return FALSE; -} - -/** - * check if one of the higher overlays contains a directory - */ -static bool overlays_contain_dir(DIR **d, char *dirname) -{ - for (; *d; ++d) - { - if (contains_dir(*d, dirname)) - { - return TRUE; - } - } - return FALSE; -} - -/** - * FUSE readdir method - */ -static int cowfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ -#define ADD_DIR(overlay, base, path) ({\ - DIR *dir = get_dir(base, path);\ - if (dir) { *(--overlay) = dir; }\ -}) - private_cowfs_t *this = get_this(); - int count; - DIR **d, **overlays; - struct stat st; - struct dirent *ent; - overlay_t *over; - enumerator_t *enumerator; - - memset(&st, 0, sizeof(st)); - - this->lock->read_lock(this->lock); - /* create a null-terminated array of DIR objects for all overlays (including - * the master and host layer). the order is from bottom to top */ - count = this->overlays->get_count(this->overlays) + 2; - overlays = calloc(count + 1, sizeof(DIR*)); - d = &overlays[count]; - - enumerator = this->overlays->create_enumerator(this->overlays); - while (enumerator->enumerate(enumerator, (void**)&over)) - { - ADD_DIR(d, over->path, path); - } - enumerator->destroy(enumerator); - this->lock->unlock(this->lock); - - ADD_DIR(d, this->host, path); - ADD_DIR(d, this->master, path); - - for (; *d; ++d) - { - rewinddir(*d); - while((ent = readdir(*d))) - { - if (!overlays_contain_dir(d + 1, ent->d_name)) - { - st.st_ino = ent->d_ino; - st.st_mode = ent->d_type << 12; - filler(buf, ent->d_name, &st, 0); - } - } - closedir(*d); - } - - free(overlays); - return 0; -} - -/** - * FUSE mknod method - */ -static int cowfs_mknod(const char *path, mode_t mode, dev_t rdev) -{ - int fd; - rel(&path); - - fd = get_wr(path); - if (!clone_path(get_rd(path), fd, path)) - { - return -errno; - } - - if (mknodat(fd, path, mode, rdev) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE mkdir method - */ -static int cowfs_mkdir(const char *path, mode_t mode) -{ - int fd; - rel(&path); - - fd = get_wr(path); - if (!clone_path(get_rd(path), fd, path)) - { - return -errno; - } - if (mkdirat(fd, path, mode) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE unlink method - */ -static int cowfs_unlink(const char *path) -{ - rel(&path); - - /* TODO: whiteout master */ - if (unlinkat(get_wr(path), path, 0) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE rmdir method - */ -static int cowfs_rmdir(const char *path) -{ - rel(&path); - - /* TODO: whiteout master */ - if (unlinkat(get_wr(path), path, AT_REMOVEDIR) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE symlink method - */ -static int cowfs_symlink(const char *from, const char *to) -{ - int fd; - const char *fromrel = from; - - rel(&to); - rel(&fromrel); - - fd = get_wr(to); - if (!clone_path(get_rd(fromrel), fd, fromrel)) - { - return -errno; - } - if (symlinkat(from, fd, to) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE rename method - */ -static int cowfs_rename(const char *from, const char *to) -{ - int fd; - - rel(&from); - rel(&to); - - fd = copy(from); - if (fd < 0) - { - return -errno; - } - if (renameat(fd, from, get_wr(to), to) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE link method - */ -static int cowfs_link(const char *from, const char *to) -{ - int rd, wr; - - rel(&from); - rel(&to); - - rd = get_rd(from); - wr = get_wr(to); - - if (!clone_path(rd, wr, to)) - { - DBG1(DBG_LIB, "cloning path '%s' failed", to); - return -errno; - } - if (linkat(rd, from, wr, to, 0) < 0) - { - DBG1(DBG_LIB, "linking '%s' to '%s' failed", from, to); - return -errno; - } - return 0; -} - -/** - * FUSE chmod method - */ -static int cowfs_chmod(const char *path, mode_t mode) -{ - int fd; - struct stat st; - - rel(&path); - fd = get_rd(path); - if (fstatat(fd, path, &st, 0) < 0) - { - return -errno; - } - if (st.st_mode == mode) - { - return 0; - } - fd = copy(path); - if (fd < 0) - { - return -errno; - } - if (fchmodat(fd, path, mode, 0) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE chown method - */ -static int cowfs_chown(const char *path, uid_t uid, gid_t gid) -{ - int fd; - struct stat st; - - rel(&path); - fd = get_rd(path); - if (fstatat(fd, path, &st, 0) < 0) - { - return -errno; - } - if (st.st_uid == uid && st.st_gid == gid) - { - return 0; - } - fd = copy(path); - if (fd < 0) - { - return -errno; - } - if (fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE truncate method - */ -static int cowfs_truncate(const char *path, off_t size) -{ - int fd; - struct stat st; - - rel(&path); - fd = get_rd(path); - if (fstatat(fd, path, &st, 0) < 0) - { - return -errno; - } - if (st.st_size == size) - { - return 0; - } - fd = copy(path); - if (fd < 0) - { - return -errno; - } - fd = openat(fd, path, O_WRONLY); - if (fd < 0) - { - return -errno; - } - if (ftruncate(fd, size) < 0) - { - close(fd); - return -errno; - } - close(fd); - return 0; -} - -/** - * FUSE utimens method - */ -static int cowfs_utimens(const char *path, const struct timespec ts[2]) -{ - struct timeval tv[2]; - int fd; - - rel(&path); - fd = copy(path); - if (fd < 0) - { - return -errno; - } - - tv[0].tv_sec = ts[0].tv_sec; - tv[0].tv_usec = ts[0].tv_nsec / 1000; - tv[1].tv_sec = ts[1].tv_sec; - tv[1].tv_usec = ts[1].tv_nsec / 1000; - - if (futimesat(fd, path, tv) < 0) - { - return -errno; - } - return 0; -} - -/** - * FUSE open method - */ -static int cowfs_open(const char *path, struct fuse_file_info *fi) -{ - int fd; - - rel(&path); - fd = get_rd(path); - - fd = openat(fd, path, fi->flags); - if (fd < 0) - { - return -errno; - } - close(fd); - return 0; -} - -/** - * FUSE read method - */ -static int cowfs_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - int file, fd, res; - - rel(&path); - - fd = get_rd(path); - - file = openat(fd, path, O_RDONLY); - if (file < 0) - { - return -errno; - } - - res = pread(file, buf, size, offset); - if (res < 0) - { - res = -errno; - } - close(file); - return res; -} - -/** - * FUSE write method - */ -static int cowfs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - int file, fd, res; - - rel(&path); - - fd = copy(path); - if (fd < 0) - { - return -errno; - } - file = openat(fd, path, O_WRONLY); - if (file < 0) - { - return -errno; - } - res = pwrite(file, buf, size, offset); - if (res < 0) - { - res = -errno; - } - close(file); - return res; -} - -/** - * FUSE statfs method - */ -static int cowfs_statfs(const char *path, struct statvfs *stbuf) -{ - int fd; - - fd = get_rd(path); - if (fstatvfs(fd, stbuf) < 0) - { - return -errno; - } - - return 0; -} - -/** - * FUSE init method - */ -static void *cowfs_init(struct fuse_conn_info *conn) -{ - struct fuse_context *ctx; - - ctx = fuse_get_context(); - - return ctx->private_data; -} - -/** - * FUSE method vectors - */ -static struct fuse_operations cowfs_operations = { - .getattr = cowfs_getattr, - .access = cowfs_access, - .readlink = cowfs_readlink, - .readdir = cowfs_readdir, - .mknod = cowfs_mknod, - .mkdir = cowfs_mkdir, - .symlink = cowfs_symlink, - .unlink = cowfs_unlink, - .rmdir = cowfs_rmdir, - .rename = cowfs_rename, - .link = cowfs_link, - .chmod = cowfs_chmod, - .chown = cowfs_chown, - .truncate = cowfs_truncate, - .utimens = cowfs_utimens, - .open = cowfs_open, - .read = cowfs_read, - .write = cowfs_write, - .statfs = cowfs_statfs, - .init = cowfs_init, -}; - -METHOD(cowfs_t, add_overlay, bool, - private_cowfs_t *this, char *path) -{ - overlay_t *over = malloc_thing(overlay_t); - over->fd = open(path, O_RDONLY | O_DIRECTORY); - if (over->fd < 0) - { - DBG1(DBG_LIB, "failed to open overlay directory '%s': %m", path); - free(over); - return FALSE; - } - over->path = realpath(path, NULL); - this->lock->write_lock(this->lock); - overlay_remove(this, over->path); - this->overlays->insert_first(this->overlays, over); - this->lock->unlock(this->lock); - return TRUE; -} - -METHOD(cowfs_t, del_overlay, bool, - private_cowfs_t *this, char *path) -{ - bool removed; - char real[PATH_MAX]; - this->lock->write_lock(this->lock); - removed = overlay_remove(this, realpath(path, real)); - this->lock->unlock(this->lock); - return removed; -} - -METHOD(cowfs_t, pop_overlay, bool, - private_cowfs_t *this) -{ - overlay_t *over; - this->lock->write_lock(this->lock); - if (this->overlays->remove_first(this->overlays, (void**)&over) != SUCCESS) - { - this->lock->unlock(this->lock); - return FALSE; - } - this->lock->unlock(this->lock); - overlay_destroy(over); - return TRUE; -} - -METHOD(cowfs_t, destroy, void, - private_cowfs_t *this) -{ - fuse_exit(this->fuse); - fuse_unmount(this->mount, this->chan); - this->thread->join(this->thread); - fuse_destroy(this->fuse); - this->lock->destroy(this->lock); - this->overlays->destroy_function(this->overlays, (void*)overlay_destroy); - free(this->mount); - free(this->master); - free(this->host); - close(this->master_fd); - close(this->host_fd); - free(this); -} - -/** - * creates a new cowfs fuse instance - */ -cowfs_t *cowfs_create(char *master, char *host, char *mount) -{ - struct fuse_args args = {0, NULL, 0}; - private_cowfs_t *this; - - INIT(this, - .public = { - .add_overlay = _add_overlay, - .del_overlay = _del_overlay, - .pop_overlay = _pop_overlay, - .destroy = _destroy, - } - ); - - this->master_fd = open(master, O_RDONLY | O_DIRECTORY); - if (this->master_fd < 0) - { - DBG1(DBG_LIB, "failed to open master filesystem '%s'", master); - free(this); - return NULL; - } - this->host_fd = open(host, O_RDONLY | O_DIRECTORY); - if (this->host_fd < 0) - { - DBG1(DBG_LIB, "failed to open host filesystem '%s'", host); - close(this->master_fd); - free(this); - return NULL; - } - - this->chan = fuse_mount(mount, &args); - if (this->chan == NULL) - { - DBG1(DBG_LIB, "mounting cowfs FUSE on '%s' failed", mount); - close(this->master_fd); - close(this->host_fd); - free(this); - return NULL; - } - - this->fuse = fuse_new(this->chan, &args, &cowfs_operations, - sizeof(cowfs_operations), this); - if (this->fuse == NULL) - { - DBG1(DBG_LIB, "creating cowfs FUSE handle failed"); - close(this->master_fd); - close(this->host_fd); - fuse_unmount(mount, this->chan); - free(this); - return NULL; - } - - this->mount = strdup(mount); - this->master = strdup(master); - this->host = strdup(host); - this->overlays = linked_list_create(); - this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT); - - this->thread = thread_create((thread_main_t)fuse_loop, this->fuse); - if (!this->thread) - { - DBG1(DBG_LIB, "creating thread to handle FUSE failed"); - fuse_unmount(mount, this->chan); - this->lock->destroy(this->lock); - this->overlays->destroy(this->overlays); - free(this->mount); - free(this->master); - free(this->host); - close(this->master_fd); - close(this->host_fd); - free(this); - return NULL; - } - - return &this->public; -} - |