Logo Search packages:      
Sourcecode: unionfs-fuse version File versions  Download package

opts.c

/*
*  C Implementation: opts.c
*
* Option parser
*
* License: BSD-style license
* Copyright: Radek Podgorny <radek@podgorny.cz>,
*            Bernd Schubert <bernd-schubert@gmx.de>
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "opts.h"
#include "stats.h"
#include "version.h"
#include "string.h"

/**
 * Set the maximum number of open files
 */
int set_max_open_files(const char *arg)
{
      struct rlimit rlim;
      unsigned long max_files;
      if (sscanf(arg, "max_files=%ld\n", &max_files) != 1) {
            fprintf(stderr, "%s Converting %s to number failed, aborting!\n",
                  __func__, arg);
            exit(1);
      }
      rlim.rlim_cur = max_files;
      rlim.rlim_max = max_files;
      if (setrlimit(RLIMIT_NOFILE, &rlim)) {
            fprintf(stderr, "%s: Setting the maximum number of files failed: %s\n",
                  __func__, strerror(errno));
            exit(1);
      }

      return 0;
}


uopt_t uopt;

void uopt_init() {
      memset(&uopt, 0, sizeof(uopt_t)); // initialize options with zeros first

      // so far nothing more required, so far initializing with zeros sufficient
}

/**
 * Take a relative path as argument and return the absolute path by using the
 * current working directory. The return string is malloc'ed with this function.
 */
char *make_absolute(char *relpath) {
      // Already an absolute path
      if (*relpath == '/') return relpath;

      char cwd[PATHLEN_MAX];
      if (!getcwd(cwd, PATHLEN_MAX)) {
            perror("Unable to get current working directory");
            return NULL;
      }

      size_t cwdlen = strlen(cwd);
      if (!cwdlen) {
            fprintf(stderr, "Zero-sized length of CWD!\n");
            return NULL;
      }

      // 3 due to: +1 for '/' between cwd and relpath
      //           +1 for trailing '/'
      int abslen = cwdlen + strlen(relpath) + 2;
      if (abslen > PATHLEN_MAX) {
            fprintf(stderr, "Absolute path too long!\n");
            return NULL;
      }

      char *abspath = malloc(abslen);
      if (abspath == NULL) {
            fprintf(stderr, "%s: malloc failed\n", __func__);
            exit(1); // still at early stage, we can abort
      }

      return abspath;
}

/**
 * Add a trailing slash at the end of a branch. So functions using this
 * path don't have to care about this slash themselves.
 **/
char *add_trailing_slash(char *path) {
      int len = strlen(path);
      if (path[len - 1] == '/') {
            return path; // no need to add a slash, already there
      }

      path = realloc(path, len + 2); // +1 for '/' and +1 for '\0'
      if (!path) {
            fprintf(stderr, "%s: realloc() failed, aborting\n", __func__);
            exit(1); // still very early stage, we can abort here
      }

      strcat(path, "/");
      return path;
}

/**
 * Add a given branch and its options to the array of available branches.
 * example branch string "branch1=RO" or "/path/path2=RW"
 */
static void add_branch(char *branch) {
      uopt.branches = realloc(uopt.branches, (uopt.nbranches+1) * sizeof(branch_entry_t));
      if (uopt.branches == NULL) {
            fprintf(stderr, "%s: realloc failed\n", __func__);
            exit(1); // still at early stage, we can't abort
      }

      char *res;
      char **ptr = (char **)&branch;

      res = strsep(ptr, "=");
      if (!res) return;

      // for string manipulations it is important to copy the string, otherwise
      // make_absolute() and add_trailing_slash() will corrupt our input (parse string)
      uopt.branches[uopt.nbranches].path = strdup(res);
      uopt.branches[uopt.nbranches].rw = 0;

      res = strsep(ptr, "=");
      if (res) {
            if (strcasecmp(res, "rw") == 0) {
                  uopt.branches[uopt.nbranches].rw = 1;
            } else if (strcasecmp(res, "ro") == 0) {
                  // no action needed here
            } else {
                  fprintf(stderr, "Failed to parse RO/RW flag, setting RO.\n");
                  // no action needed here either
            }
      }

      uopt.nbranches++;
}

/**
 * Options without any -X prefix, so these options define our branch paths.
 * example arg string: "branch1=RW:branch2=RO:branch3=RO"
 */
static int parse_branches(const char *arg) {
      // the last argument is  our mountpoint, don't take it as branch!
      if (uopt.nbranches) return 0;

      // We don't free the buf as parts of it may go to branches
      char *buf = strdup(arg);
      char **ptr = (char **)&buf;
      char *branch;
      while ((branch = strsep(ptr, ROOT_SEP)) != NULL) {
            if (strlen(branch) == 0) continue;

            add_branch(branch);
      }

      free(buf);

      return uopt.nbranches;
}

/**
  * fuse passes arguments with the argument prefix, e.g.
  * "-o chroot=/path/to/chroot/" will give us "chroot=/path/to/chroot/"
  * and we need to cut off the "chroot=" part
  * NOTE: If the user specifies a relative path of the branches
  *       to the chroot, it is absolutely required
  *       -o chroot=path is provided before specifying the braches!
  */
char * get_chroot(const char *arg)
{
      char *str = index(arg, '=');

      if (!str) {
            fprintf(stderr, "-o chroot parameter not properly specified, aborting!\n");
            exit(1); // still early phase, we can abort
      }

      if (strlen(str) < 3) {
            fprintf(stderr, "Chroot path has not sufficient characters, aborting!\n");
            exit(1);
      }

      str++; // just jump over the '='

      // copy of the given parameter, just in case something messes around
      // with command line parameters later on
      str = strdup(str);
      if (!str) {
            fprintf(stderr, "strdup failed: %s Aborting!\n", strerror(errno));
            exit(1);
      }
      return str;
}

static void print_help(const char *progname) {
      printf(
      "unionfs-fuse version "VERSION"\n"
      "by Radek Podgorny <radek@podgorny.cz>\n"
      "\n"
      "Usage: %s [options] branch[=RO/RW][:branch...] mountpoint\n"
      "The first argument is a colon separated list of directories to merge\n"
      "\n"
      "general options:\n"
      "    -o opt,[opt...]        mount options\n"
      "    -h   --help            print help\n"
      "    -V   --version         print version\n"
      "\n"
      "UnionFS options:\n"
      "    -o cow                 enable copy-on-write\n"
      "    -o stats               show statistics in the file 'stats' under the\n"
      "                           mountpoint\n"
      "    -o statfs_omit_ro      do not count blocks of ro-branches\n"
      "    -o chroot=path         chroot into this path. Use this if you \n"
        "                           want to have a union of \"/\" \n"
      "    -o max_files=number    Increase the maximum number of open files\n"
      "\n",
      progname);
}

/**
  * This method is to post-process options once we know all of them
  */
void unionfs_post_opts(void) {
      // chdir to the given chroot, we
      if (uopt.chroot) {
            int res = chdir(uopt.chroot);
            if (res) {
                  fprintf(stderr, "Chdir to %s failed: %s ! Aborting!\n",
                          uopt.chroot, strerror(errno));
                  exit(1);
            }
      }

      // Make the pathes absolute and add trailing slashes
      int i;
      for (i = 0; i<uopt.nbranches; i++) {
            // if -ochroot= is specified, the path has to be given absolute
            // or relative to the chroot, so no need to make it absolute
            // also won't work, since we are not yet in the chroot here
            if (!uopt.chroot) {
                  uopt.branches[i].path = make_absolute(uopt.branches[i].path);
            }
            uopt.branches[i].path = add_trailing_slash(uopt.branches[i].path);

            // Prevent accidental umounts. Especially system shutdown scripts tend
            // to umount everything they can. If we don't have an open file descriptor,
            // this might cause unexpected behaviour.
            char path[PATHLEN_MAX];

            if (!uopt.chroot) {
                  BUILD_PATH(path, uopt.branches[i].path);
            } else {
                  BUILD_PATH(path, uopt.chroot, uopt.branches[i].path);
            }

            int fd = open(path, O_RDONLY);
            if (fd == -1) {
                  fprintf(stderr, "\nFailed to open %s: %s. Aborting!\n\n",
                        path, strerror(errno));
                  exit(1);
            }
            uopt.branches[i].fd = fd;
      }
}

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

      int res = 0; // for general purposes

      switch (key) {
            case FUSE_OPT_KEY_NONOPT:
                  res = parse_branches(arg);
                  if (res > 0) return 0;
                  uopt.retval = 1;
                  return 1;
            case KEY_STATS:
                  uopt.stats_enabled = 1;
                  return 0;
            case KEY_COW:
                  uopt.cow_enabled = true;
                  return 0;
            case KEY_STATFS_OMIT_RO:
                  uopt.statfs_omit_ro = true;
                  return 0;
            case KEY_NOINITGROUPS:
                  // option only for compatibility with older versions
                  return 0;
            case KEY_CHROOT:
                  uopt.chroot = get_chroot(arg);
                  return 0;
            case KEY_MAX_FILES:
                  set_max_open_files(arg);
                  return 0;
            case KEY_HELP:
                  print_help(outargs->argv[0]);
                  fuse_opt_add_arg(outargs, "-ho");
                  uopt.doexit = 1;
                  return 0;
            case KEY_VERSION:
                  printf("unionfs-fuse version: "VERSION"\n");
                  uopt.doexit = 1;
                  return 1;
            default:
                  uopt.retval = 1;
                  return 1;
      }
}

Generated by  Doxygen 1.6.0   Back to index