summaryrefslogtreecommitdiff
path: root/src/dumm/cowfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dumm/cowfs.c')
-rw-r--r--src/dumm/cowfs.c980
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**)&current, &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;
-}
-