Logo Search packages:      
Sourcecode: afuse version File versions  Download package

afuse.c

/*
      afuse -      An automounter using FUSE
      Copyright (C) 2008 Jacob Bower <jacob.bower@ic.ac.uk>

      Portions of this program derive from examples provided with
      FUSE-2.5.2.

      This program can be distributed under the terms of the GNU GPL.
      See the file COPYING.

      Contributions:

      Feb '08, Jeremy Maitin-Shepard <jbms@cmu.edu>
      * Added timeout-based unmounting.
      * Forced immediate unmounting by removing root directory of
        auto mounts.
      * Minor performance tweaks.
      * Better handling of filesystems which are unexpectedly
        unmounted.

*/

#include <config.h>

#ifdef linux
// For pread()/pwrite()
#define _XOPEN_SOURCE 500
#endif

#include <fuse.h>
#include <fuse_opt.h>
// for mkdtemp
#define __USE_BSD
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h>
#include <alloca.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdint.h>
#include <signal.h>
#ifdef HAVE_SETXATTR
#include <sys/xattr.h>
#endif

#include "fd_list.h"
#include "dir_list.h"
#include "utils.h"

#include "variable_pairing_heap.h"

#define TMP_DIR_TEMPLATE "/tmp/afuse-XXXXXX"
char *mount_point_directory;
dev_t mount_point_dev;

// Data structure filled in when parsing command line args
struct user_options_t {
      char *mount_command_template;
      char *unmount_command_template;
      bool flush_writes;
      uint64_t auto_unmount_delay;
} user_options = {NULL, NULL, false, UINT64_MAX};

typedef struct _mount_list_t {
      struct _mount_list_t *next;
      struct _mount_list_t *prev;

      char *root_name;
      char *mount_point;
      fd_list_t *fd_list;
      dir_list_t *dir_list;

      PH_NEW_LINK(struct _mount_list_t) auto_unmount_ph_node;
      /* This is the sort key for the auto_unmount_ph heap.  It will
         equal UINT64_MAX if this node is not in the heap. */
      int64_t auto_unmount_time;
} mount_list_t;

PH_DECLARE_TYPE(auto_unmount_ph, mount_list_t)
PH_DEFINE_TYPE(auto_unmount_ph, mount_list_t, auto_unmount_ph_node, auto_unmount_time)

#define BLOCK_SIGALRM \
      sigset_t block_sigalrm_oldset, block_sigalrm_set;     \
      sigemptyset(&block_sigalrm_set); \
      sigaddset(&block_sigalrm_set, SIGALRM); \
      sigprocmask(SIG_BLOCK, &block_sigalrm_set, &block_sigalrm_oldset)

#define UNBLOCK_SIGALRM \
      sigprocmask(SIG_SETMASK, &block_sigalrm_oldset, NULL)

auto_unmount_ph_t auto_unmount_ph;
int64_t auto_unmount_next_timeout = INT64_MAX;

static int64_t from_timeval(const struct timeval *tv)
{
      return (int64_t)tv->tv_sec * 1000000 + ((int64_t)tv->tv_usec);
}

static void to_timeval(struct timeval *tv, int64_t usec)
{
      tv->tv_sec = usec / 1000000;
      tv->tv_usec = usec % 1000000;
}

static int get_retval(int res)
{
      if (res == -1)
            return -errno;
      return 0;
}

static int check_mount(mount_list_t *mount)
{
      struct stat buf;

      if (lstat(mount->mount_point, &buf) == -1)
            return 0;
      if (buf.st_dev == mount_point_dev)
            return 0;
      return 1;
}

static void update_auto_unmount(mount_list_t *mount)
{
      if (user_options.auto_unmount_delay == UINT64_MAX)
            return;

      /* Get the current time */
      struct timeval tv;
      mount_list_t *min_mount;
      int64_t cur_time, next_time;
      gettimeofday(&tv, NULL);
      cur_time = from_timeval(&tv);

      if (mount)
      {
            /* Always remove first */
            if (mount->auto_unmount_time != INT64_MAX)
                  auto_unmount_ph_remove(&auto_unmount_ph, mount);

            if (!mount->fd_list && !mount->dir_list)
            {
                  mount->auto_unmount_time = cur_time + user_options.auto_unmount_delay;
                  auto_unmount_ph_insert(&auto_unmount_ph, mount);
            } else
            {
                  mount->auto_unmount_time = INT64_MAX;
            }
      }
      min_mount = auto_unmount_ph_min(&auto_unmount_ph);
      next_time = min_mount ? min_mount->auto_unmount_time : INT64_MAX;

      if (next_time != auto_unmount_next_timeout)
      {
            struct itimerval itv;
            auto_unmount_next_timeout = next_time;

            if (next_time != INT64_MAX)
            {
                  if (next_time > cur_time)
                        to_timeval(&itv.it_value, next_time - cur_time);
                  else /* Timer is set to expire immediately --- set it to 1 instead*/
                        to_timeval(&itv.it_value, 1);
            } else
            {
                  /* Disable the timer */
                  to_timeval(&itv.it_value, 0);
            }
            to_timeval(&itv.it_interval, 0);
            if (setitimer(ITIMER_REAL, &itv, NULL) != 0) {
                  perror("Error setting timer");
            }
      }
}

static void handle_auto_unmount_timer(int x)
{
      /* Get the current time */
      struct timeval tv;
      int64_t cur_time;
      mount_list_t *mount;
      gettimeofday(&tv, NULL);
      cur_time = from_timeval(&tv);

      while ((mount = auto_unmount_ph_min(&auto_unmount_ph)) != NULL &&
               mount->auto_unmount_time <= cur_time)
      {
            do_umount(mount);
      }

      update_auto_unmount(NULL);
}

mount_list_t *mount_list = NULL;

mount_list_t *find_mount(const char *root_name)
{
      mount_list_t *current_mount = mount_list;

      while(current_mount) {
            if( strcmp(root_name, current_mount->root_name) == 0)
                  return current_mount;

            current_mount = current_mount->next;
      }

      return NULL;
}

int is_mount(const char *root_name)
{
      return find_mount(root_name) ? 1 : 0;
}

mount_list_t *add_mount(const char *root_name, char *mount_point)
{
      mount_list_t *new_mount;

      new_mount = (mount_list_t *)my_malloc( sizeof(mount_list_t) );
      new_mount->root_name = my_strdup(root_name);
      new_mount->mount_point = mount_point;

      new_mount->next = mount_list;
      new_mount->prev = NULL;
      new_mount->fd_list = NULL;
      new_mount->dir_list = NULL;
      new_mount->auto_unmount_time = INT64_MAX;
      if(mount_list)
            mount_list->prev = new_mount;

      mount_list = new_mount;

      update_auto_unmount(new_mount);

      return new_mount;
}

void remove_mount(mount_list_t *current_mount)
{
      if (current_mount->auto_unmount_time != INT64_MAX)
            auto_unmount_ph_remove(&auto_unmount_ph, current_mount);

      free(current_mount->root_name);
      free(current_mount->mount_point);
      if(current_mount->prev)
            current_mount->prev->next = current_mount->next;
      else
            mount_list = current_mount->next;
      if(current_mount->next)
            current_mount->next->prev = current_mount->prev;
      free(current_mount);
      update_auto_unmount(NULL);
}

char *make_mount_point(const char *root_name)
{
      char *dir_tmp;
      int i;

      // Create the mount point
      dir_tmp = my_malloc(strlen(mount_point_directory) + 2 + strlen(root_name));
      strcpy(dir_tmp, mount_point_directory);
      strcat(dir_tmp, "/");
      strcat(dir_tmp, root_name);

      if(mkdir(dir_tmp, 0700) == -1 && errno != EEXIST) {
            fprintf(stderr, "Cannot create directory: %s (%s)\n",
                  dir_tmp, strerror(errno));
            free(dir_tmp);
            return NULL;
      }
      return dir_tmp;
}


// !!FIXME!! allow escaping of %'s
// Note: this method strips out quotes and applies them itself as should be appropriate
char *expand_template(const char *template, const char *mount_point, const char *root_name)
{
      int len = 0;
      int i;
      char *expanded_name;
      char *expanded_name_start;

      // calculate length
      for(i = 0; template[i]; i++)
            if(template[i] == '%') {
                  switch(template[i + 1])
                  {
                        case 'm':
                              len += strlen(mount_point) + 2;
                              i++;
                              break;
                        case 'r':
                              len += strlen(root_name) + 2;
                              i++;
                              break;
                  }
            } else if(template[i] != '"')
                  len++;

      expanded_name_start = expanded_name = my_malloc(len + 1);

      for(i = 0; template[i]; i++)
            if(template[i] == '%') {
                  int j = 0;
                  switch(template[i + 1])
                  {
                        case 'm':
                              *expanded_name++ = '"';
                              while(mount_point[j])
                                    *expanded_name++ = mount_point[j++];
                              *expanded_name++ = '"';
                              i++;
                              break;
                        case 'r':
                              *expanded_name++ = '"';
                              while(root_name[j])
                                    *expanded_name++ = root_name[j++];
                              *expanded_name++ = '"';
                              i++;
                              break;
                  }
            } else if(template[i] != '"')
                  *expanded_name++ = template[i];

      *expanded_name = '\0';

      return expanded_name_start;
}

mount_list_t *do_mount(const char *root_name)
{
      char *mount_point;
      char *mount_command;
      mount_list_t *mount;
      int sysret;

      fprintf(stderr, "Mounting: %s\n", root_name);

      if( !(mount_point = make_mount_point(root_name)) ) {
            fprintf(stderr, "Failed to create mount point directory: %s/%s\n",
                  mount_point_directory, root_name);
            return NULL;
      }

      mount_command = expand_template(user_options.mount_command_template,
                                      mount_point, root_name);
      sysret = system(mount_command);

      fprintf(stderr, "sysret: %.8x\n", sysret);

      if(sysret) {
            fprintf(stderr, "Failed to invoke mount command: '%s' (%s)\n",
                  mount_command, sysret != -1 ?
                        "Error executing mount" :
                        strerror(errno));

            // remove the now unused directory
            if( rmdir(mount_point) == -1 )
                  fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
                        mount_point, strerror(errno));

            free(mount_command);
            free(mount_point);
            return NULL;
      }

      mount = add_mount(root_name, mount_point);

      free(mount_command);
      return mount;
}

int do_umount(mount_list_t *mount)
{
      char *unmount_command;
      int sysret;

      fprintf(stderr, "Unmounting: %s\n", mount->root_name);

      unmount_command = expand_template(user_options.unmount_command_template,
                                        mount->mount_point, mount->root_name);
      sysret = system(unmount_command);
      if(sysret) {
            fprintf(stderr, "Failed to invoke unmount command: '%s' (%s)\n",
                    unmount_command, sysret != -1 ?
                                 "Error executing mount" :
                               strerror(errno));
            /* Still unmount anyway */
      }

      if( rmdir(mount->mount_point) == -1 )
            fprintf(stderr, "Failed to remove mount point dir: %s (%s)",
                        mount->mount_point, strerror(errno));
      remove_mount(mount);
      free(unmount_command);
      return 1;
}

void unmount_all(void)
{
      fprintf(stderr, "Attempting to unmount all filesystems:\n");

      while(mount_list) {
            fprintf(stderr, "\tUnmounting: %s\n", mount_list->root_name);

            do_umount(mount_list);
      }

      fprintf(stderr, "done.\n");
}

void shutdown(void)
{
      BLOCK_SIGALRM;

      unmount_all();

      UNBLOCK_SIGALRM;

      if(rmdir(mount_point_directory) == -1)
            fprintf(stderr, "Failed to remove temporary mount point directory: %s (%s)\n",
                    mount_point_directory, strerror(errno));
}

int max_path_out_len(const char *path_in)
{
      return strlen(mount_point_directory) + strlen(path_in) + 2;
}

// returns true if path is the a child directory of a root node
// e.g. /a/b is a child, /a is not.
int extract_root_name(const char *path, char *root_name)
{
      int i;
      int is_child;

      for(i = 1; path[i] && path[i] != '/'; i++)
            root_name[i - 1] = path[i];
      root_name[i - 1] = '\0';

      return strlen(&path[i]);
}

typedef enum {PROC_PATH_FAILED, PROC_PATH_ROOT_DIR, PROC_PATH_PROXY_DIR} proc_result_t;

proc_result_t process_path(const char *path_in, char *path_out, char *root_name,
                           int attempt_mount, mount_list_t **out_mount)
{
      int i;
      char *path_out_base;
      int is_child;
      int len;
      mount_list_t *mount = NULL;

      *out_mount = NULL;

      fprintf(stderr, "Path in: %s\n", path_in);
      is_child = extract_root_name(path_in, root_name);
      fprintf(stderr, "root_name is: %s\n", root_name);

      // Mount filesystem if necessary
      // the combination of is_child and attempt_mount prevent inappropriate
      // mounting of a filesystem for example if the user tries to mknod
      // in the afuse root this should cause an error not a mount.
      // !!FIXME!! this is broken on FUSE < 2.5 (?) because a getattr
      // on the root node seems to occur with every single access.
      if( /*(is_child || attempt_mount ) &&  */
         strlen(root_name) > 0            &&
         !(mount = find_mount(root_name)) &&
         !(mount = do_mount(root_name)))
            return PROC_PATH_FAILED;

      if (mount && !check_mount(mount))
      {
            do_umount(mount);
            mount = do_mount(root_name);
            if (!mount)
                  return PROC_PATH_FAILED;
      }

      // construct path_out (mount_point_directory + '/' + path_in + '\0')
      path_out_base = path_out;
      len = strlen(mount_point_directory);
      memcpy(path_out, mount_point_directory, len);
      path_out += len;
      *path_out++ = '/';
      len = strlen(path_in) - 1;
      memcpy(path_out, path_in + 1, len);
      path_out += len;
      *path_out = '\0';
      fprintf(stderr, "Path out: %s\n", path_out_base);

      *out_mount = mount;

      return strlen(root_name) ? PROC_PATH_PROXY_DIR : PROC_PATH_ROOT_DIR;
}

static int afuse_getattr(const char *path, struct stat *stbuf)
{
      int res;
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      int retval;
      mount_list_t *mount;
      BLOCK_SIGALRM;

      fprintf(stderr, "> GetAttr\n");

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;

            case PROC_PATH_ROOT_DIR:
                  fprintf(stderr, "Getattr on: (%s) - %s\n", path, root_name);
                  if(mount || strlen(root_name) == 0) {
                        stbuf->st_mode    = S_IFDIR | 0700;
                        stbuf->st_nlink   = 1;
                        stbuf->st_uid     = getuid();
                        stbuf->st_gid     = getgid();
                        stbuf->st_size    = 0;
                        stbuf->st_blksize = 0;
                        stbuf->st_blocks  = 0;
                        stbuf->st_atime   = 0;
                        stbuf->st_mtime   = 0;
                        stbuf->st_ctime   = 0;
                        retval = 0;
                  } else
                        retval = -ENOENT;
                  break;

            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(lstat(real_path, stbuf));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_readlink(const char *path, char *buf, size_t size)
{
      int res;
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      int retval;
      mount_list_t *mount;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOENT;
                  break;
            case PROC_PATH_PROXY_DIR:
                  res = readlink(real_path, buf, size - 1);
                  if (res == -1)
                  {
                        retval = -errno;
                        break;
                  }
                  buf[res] = '\0';
                  retval = 0;
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_opendir(const char *path, struct fuse_file_info *fi)
{
      DIR *dp;
      char *root_name = alloca( strlen(path) );
      mount_list_t *mount;
      char *real_path = alloca( max_path_out_len(path) );
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = 0;
                  break;
            case PROC_PATH_PROXY_DIR:
                  dp = opendir(real_path);
                  if (dp == NULL) {
                        retval = -errno;
                        break;
                  }
                  fi->fh = (unsigned long) dp;
                  if(mount)
                        dir_list_add(&mount->dir_list, dp);
                  retval = 0;
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static inline DIR *get_dirp(struct fuse_file_info *fi)
{
      return (DIR *) (uintptr_t) fi->fh;
}

static int afuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                       off_t offset, struct fuse_file_info *fi)
{
      DIR *dp = get_dirp(fi);
      struct dirent *de;
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount, *next;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;

            case PROC_PATH_ROOT_DIR:
                  filler(buf, ".", NULL, 0);
                  filler(buf, "..", NULL, 0);
                  for(mount = mount_list; mount; mount = next)
                  {
                        next = mount->next;
                        /* Check for dead mounts. */
                        if (!check_mount(mount))
                              do_umount(mount);
                        else
                              filler(buf, mount->root_name, NULL, 0);
                  }
                  mount = NULL;
                  retval = 0;
                  break;

            case PROC_PATH_PROXY_DIR:
                  seekdir(dp, offset);
                  while ((de = readdir(dp)) != NULL) {
                        struct stat st;
                        memset(&st, 0, sizeof(st));
                        st.st_ino = de->d_ino;
                        st.st_mode = de->d_type << 12;
                        if (filler(buf, de->d_name, &st, telldir(dp)))
                              break;
                  }
                  retval = 0;
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_releasedir(const char *path, struct fuse_file_info *fi)
{
      DIR *dp = get_dirp(fi);
      mount_list_t *mount;
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      int retval;

      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;

            case PROC_PATH_ROOT_DIR:
                  retval = 0;
                  break;

            case PROC_PATH_PROXY_DIR:
                  if (mount)
                        dir_list_remove(&mount->dir_list, dp);
                  closedir(dp);
                  retval = 0;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_mknod(const char *path, mode_t mode, dev_t rdev)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;
      fprintf(stderr, "> Mknod\n");

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;

            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;

            case PROC_PATH_PROXY_DIR:
                  if (S_ISFIFO(mode))
                        retval = get_retval(mkfifo(real_path, mode));
                  else
                        retval = get_retval(mknod(real_path, mode, rdev));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_mkdir(const char *path, mode_t mode)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      int retval;
      mount_list_t *mount;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(mkdir(real_path, mode));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_unlink(const char *path)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(unlink(real_path));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_rmdir(const char *path)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  if (!extract_root_name(path, root_name))
                  {
                        /* Unmount */
                        if (mount->dir_list || mount->fd_list)
                              retval = -EBUSY;
                        else
                        {
                              do_umount(mount);
                              mount = NULL;
                              retval = 0;
                        }
                  } else
                        retval = get_retval(rmdir(real_path));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_symlink(const char *from, const char *to)
{
      char *root_name_to = alloca( strlen(to) );
      char *real_to_path = alloca( max_path_out_len(to) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(to, real_to_path, root_name_to, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(symlink(from, real_to_path));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_rename(const char *from, const char *to)
{
      char *root_name_from = alloca( strlen(from) );
      char *root_name_to = alloca( strlen(to) );
      char *real_from_path = alloca( max_path_out_len(from) );
      char *real_to_path = alloca( max_path_out_len(to) );
      mount_list_t *mount_from, *mount_to = NULL;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(from, real_from_path, root_name_from, 0, &mount_from) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;

            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;

            case PROC_PATH_PROXY_DIR:
                  switch( process_path(to, real_to_path, root_name_to, 0, &mount_to) )
                  {
                        case PROC_PATH_FAILED:
                              retval = -ENXIO;
                              break;

                        case PROC_PATH_ROOT_DIR:
                              retval = -ENOTSUP;
                              break;

                        case PROC_PATH_PROXY_DIR:
                              retval = get_retval(rename(real_from_path, real_to_path));
                              break;
                  }
                  break;
      }
      if (mount_to)
            update_auto_unmount(mount_to);
      if (mount_from && mount_from != mount_to)
            update_auto_unmount(mount_from);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_link(const char *from, const char *to)
{
      char *root_name_from = alloca( strlen(from) );
      char *root_name_to = alloca( strlen(to) );
      char *real_from_path = alloca( max_path_out_len(from) );
      char *real_to_path = alloca( max_path_out_len(to) );
      mount_list_t *mount_to = NULL, *mount_from;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(from, real_from_path, root_name_from, 0, &mount_from) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  switch( process_path(to, real_to_path, root_name_to, 0, &mount_to) )
                  {
                        case PROC_PATH_FAILED:
                              retval = -ENXIO;
                              break;
                        case PROC_PATH_ROOT_DIR:
                              retval = -ENOTSUP;
                              break;
                        case PROC_PATH_PROXY_DIR:
                              retval = get_retval(link(real_from_path, real_to_path));
                              break;
                  }
                  break;
      }
      if (mount_to)
            update_auto_unmount(mount_to);
      if (mount_from && mount_from != mount_to)
            update_auto_unmount(mount_from);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_chmod(const char *path, mode_t mode)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(chmod(real_path, mode));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_chown(const char *path, uid_t uid, gid_t gid)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(lchown(real_path, uid, gid));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_truncate(const char *path, off_t size)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(truncate(real_path, size));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}


static int afuse_utime(const char *path, struct utimbuf *buf)
{
      int res;
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(utime(real_path, buf));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}


static int afuse_open(const char *path, struct fuse_file_info *fi)
{
      int fd;
      char *root_name = alloca( strlen(path) );
      mount_list_t *mount;
      char *real_path = alloca( max_path_out_len(path) );
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOENT;
                  break;
            case PROC_PATH_PROXY_DIR:
                  fd = open(real_path, fi->flags);
                  if (fd == -1) {
                        retval = -errno;
                        break;
                  }

                  fi->fh = fd;
                  if(mount)
                        fd_list_add(&mount->fd_list, fd);
                  retval = 0;
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_read(const char *path, char *buf, size_t size, off_t offset,
                      struct fuse_file_info *fi)
{
      int res;

      (void)path;
      res = pread(fi->fh, buf, size, offset);
      if (res == -1)
            res = -errno;

      return res;
}

static int afuse_write(const char *path, const char *buf, size_t size,
                       off_t offset, struct fuse_file_info *fi)
{
      int res;

      (void) path;
      res = pwrite(fi->fh, buf, size, offset);
      if (res == -1)
            res = -errno;

      if(user_options.flush_writes)
            fsync(fi->fh);

      return res;
}


static int afuse_release(const char *path, struct fuse_file_info *fi)
{
      char *root_name = alloca( strlen(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      extract_root_name(path, root_name);
      mount = find_mount(root_name);
      retval = get_retval(close(fi->fh));

      if(mount)
      {
            fd_list_remove(&mount->fd_list, fi->fh);
            update_auto_unmount(mount);
      }

      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_fsync(const char *path, int isdatasync,
                       struct fuse_file_info *fi)
{
      int res;
      (void) path;

      #ifndef HAVE_FDATASYNC
      (void) isdatasync;
      #else
      if (isdatasync)
            res = fdatasync(fi->fh);
      else
      #endif
            res = fsync(fi->fh);
      return get_retval(res);
}

#if FUSE_VERSION >= 25
static int afuse_access(const char *path, int mask)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(access(real_path, mask));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_ftruncate(const char *path, off_t size,
                           struct fuse_file_info *fi)
{
      (void) path;
      return get_retval(ftruncate(fi->fh, size));
}

static int afuse_create(const char *path, mode_t mode, struct fuse_file_info *fi)
{
      int fd;
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  fd = open(real_path, fi->flags, mode);
                  if (fd == -1)
                  {
                        retval = -errno;
                        break;
                  }
                  fi->fh = fd;
                  retval = 0;
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_fgetattr(const char *path, struct stat *stbuf,
                          struct fuse_file_info *fi)
{
      (void) path;

      return get_retval(fstat(fi->fh, stbuf));
}
#endif


#if FUSE_VERSION >= 25
static int afuse_statfs(const char *path, struct statvfs *stbuf)
#else
static int afuse_statfs(const char *path, struct statfs *stbuf)
#endif
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;

            case PROC_PATH_ROOT_DIR:
#if FUSE_VERSION >= 25
                  stbuf->f_namemax = 0x7fffffff;
                  stbuf->f_frsize  = 512;
#else
                  stbuf->f_namelen = 0x7fffffff;
#endif
                  stbuf->f_bsize   = 1024;
                  stbuf->f_blocks  = 0;
                  stbuf->f_bfree   = 0;
                  stbuf->f_bavail  = 0;
                  stbuf->f_files   = 0;
                  stbuf->f_ffree   = 0;
                  retval = 0;
                  break;

            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(statvfs(real_path, stbuf));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

void afuse_destroy(void *p)
{
      shutdown();
}

#ifdef HAVE_SETXATTR
/* xattr operations are optional and can safely be left unimplemented */
static int afuse_setxattr(const char *path, const char *name, const char *value,
                          size_t size, int flags)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOENT;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(lsetxattr(real_path, name, value, size, flags));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_getxattr(const char *path, const char *name, char *value,
                                      size_t size)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(lgetxattr(real_path, name, value, size));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_listxattr(const char *path, char *list, size_t size)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 1, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(llistxattr(real_path, list, size));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}

static int afuse_removexattr(const char *path, const char *name)
{
      char *root_name = alloca( strlen(path) );
      char *real_path = alloca( max_path_out_len(path) );
      mount_list_t *mount;
      int retval;
      BLOCK_SIGALRM;

      switch( process_path(path, real_path, root_name, 0, &mount) )
      {
            case PROC_PATH_FAILED:
                  retval = -ENXIO;
                  break;
            case PROC_PATH_ROOT_DIR:
                  retval = -ENOTSUP;
                  break;
            case PROC_PATH_PROXY_DIR:
                  retval = get_retval(lremovexattr(real_path, name));
                  break;
      }
      if (mount)
            update_auto_unmount(mount);
      UNBLOCK_SIGALRM;
      return retval;
}
#endif /* HAVE_SETXATTR */

static struct fuse_operations afuse_oper = {
      .getattr     = afuse_getattr,
      .readlink    = afuse_readlink,
      .opendir     = afuse_opendir,
      .readdir     = afuse_readdir,
      .releasedir  = afuse_releasedir,
      .mknod       = afuse_mknod,
      .mkdir       = afuse_mkdir,
      .symlink     = afuse_symlink,
      .unlink      = afuse_unlink,
      .rmdir       = afuse_rmdir,
      .rename      = afuse_rename,
      .link        = afuse_link,
      .chmod       = afuse_chmod,
      .chown       = afuse_chown,
      .truncate    = afuse_truncate,
      .utime       = afuse_utime,
      .open        = afuse_open,
      .read        = afuse_read,
      .write       = afuse_write,
      .release     = afuse_release,
      .fsync       = afuse_fsync,
      .statfs      = afuse_statfs,
#if FUSE_VERSION >= 25
      .access      = afuse_access,
      .create      = afuse_create,
      .ftruncate   = afuse_ftruncate,
      .fgetattr    = afuse_fgetattr,
#endif
      .destroy     = afuse_destroy,
#ifdef HAVE_SETXATTR
      .setxattr    = afuse_setxattr,
      .getxattr    = afuse_getxattr,
      .listxattr   = afuse_listxattr,
      .removexattr = afuse_removexattr,
#endif
};


enum {
      KEY_HELP,
      KEY_FLUSHWRITES
};

#define AFUSE_OPT(t, p, v) { t, offsetof(struct user_options_t, p), v }

static struct fuse_opt afuse_opts[] = {
      AFUSE_OPT("mount_template=%s", mount_command_template, 0),
      AFUSE_OPT("unmount_template=%s", unmount_command_template, 0),

      AFUSE_OPT("timeout=%Lu", auto_unmount_delay, 0),

      FUSE_OPT_KEY("flushwrites",     KEY_FLUSHWRITES),
      FUSE_OPT_KEY("-h",     KEY_HELP),
      FUSE_OPT_KEY("--help", KEY_HELP),

      FUSE_OPT_END
};

static void usage(const char *progname)
{
      fprintf(stderr,
"Usage: %s mountpoint [options]\n"
"\n"
"    -o opt,[opt...]        mount options\n"
"    -h   --help            print help\n"
"    -V   --version         print FUSE version information\n"
"\n"
"afuse options:\n"
"    -o mount_template=CMD    template for CMD to execute to mount (*)\n"
"    -o unmount_template=CMD  template for CMD to execute to unmount (*) (**)\n"
"    -o timeout=TIMEOUT       automatically unmount after TIMEOUT seconds\n"
"    -o flushwrites           flushes data to disk for all file writes\n"
"\n\n"
" (*) - When executed, %%r and %%m are expanded in templates to the root\n"
"       directory name for the new mount point, and the actual directory to\n"
"       mount onto respectively to mount onto. Both templates are REQUIRED.\n"
"\n"
" (**) - The unmount command must perform a lazy unmount operation. E.g. the\n"
"        -u -z options to fusermount, or -l for regular mount.\n"
"\n", progname);
}

static int afuse_opt_proc(void *data, const char *arg, int key,
                          struct fuse_args *outargs)
{
      (void) arg;

      switch(key)
      {
            case KEY_HELP:
                  usage(outargs->argv[0]);
                  fuse_opt_add_arg(outargs, "-ho");
                  fuse_main(outargs->argc, outargs->argv, &afuse_oper);
                  exit(1);

            case KEY_FLUSHWRITES:
                  user_options.flush_writes = true;
                  return 0;

            default:
                  return 1;
      }
}

int main(int argc, char *argv[])
{
      struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
      char *temp_dir_name = my_malloc(strlen(TMP_DIR_TEMPLATE));
      strcpy(temp_dir_name, TMP_DIR_TEMPLATE);

      if(fuse_opt_parse(&args, &user_options, afuse_opts, afuse_opt_proc) == -1)
            return 1;

      // !!FIXME!! force single-threading for now as data structures are not locked
      fuse_opt_add_arg(&args, "-s");

      // Adjust user specified timeout from seconds to microseconds as required
      user_options.auto_unmount_delay *= 1000000;

      auto_unmount_ph_init(&auto_unmount_ph);

      /**
       * Install the SIGALRM signal handler.
       */
      {
            struct sigaction act;
            act.sa_handler = &handle_auto_unmount_timer;
            sigemptyset(&act.sa_mask);
            act.sa_flags = 0;
            while (sigaction(SIGALRM, &act, NULL) == -1 && errno == EINTR)
                  continue;
      }

      // Check for required parameters
      if(!user_options.mount_command_template || !user_options.unmount_command_template) {
            fprintf(stderr, "(Un)Mount command templates missing.\n\n");
            usage(argv[0]);
            fuse_opt_add_arg(&args, "-ho");
            fuse_main(args.argc, args.argv, &afuse_oper);

            return 1;
      }

      if( !(mount_point_directory = mkdtemp(temp_dir_name)) ) {
            fprintf(stderr, "Failed to create temporary mount point dir.\n");
            return 1;
      }

      {
            struct stat buf;
            if (lstat(mount_point_directory, &buf) == -1) {
                  fprintf(stderr, "Failed to stat temporary mount point dir.\n");
                  return 1;
            }
            mount_point_dev = buf.st_dev;
      }

      umask(0);


      // !!FIXME!! death by signal doesn't unmount fs
      return fuse_main(args.argc, args.argv, &afuse_oper);
}

/*
  Local Variables:
  c-basic-offset: 4
  indent-tabs-mode: t
  End:
 */

Generated by  Doxygen 1.6.0   Back to index