Welcome to little lamb

Code » anopa » master » tree

[master] / src / utils / aa-mount.c

/*
 * anopa - Copyright (C) 2015-2017 Olivier Brunel
 *
 * aa-mount.c
 * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
 *
 * This file is part of anopa.
 *
 * anopa is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * anopa 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.
 *
 * You should have received a copy of the GNU General Public License along with
 * anopa. If not, see http://www.gnu.org/licenses/
 */

#include <getopt.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <skalibs/stralloc.h>
#include <skalibs/bytestr.h>
#include <anopa/common.h>
#include <anopa/output.h>
#include "mount-constants.h"

struct mnt_opt
{
    const char *name;
    size_t len;
    unsigned long value;
    enum {
        OP_ADD,
        OP_REM,
        OP_SET
    } op;
};

/* special: we don't own sa */
static int
add_option (stralloc *sa, unsigned long *flags, char const *options)
{
#define mnt_opt(name, op, val)  { name, strlen (name), val, OP_##op }
    struct mnt_opt mnt_options[] = {
        mnt_opt ("defaults",        SET, MS_MGC_VAL),
        mnt_opt ("ro",              ADD, MS_RDONLY),
        mnt_opt ("rw",              REM, MS_RDONLY),
        mnt_opt ("bind",            ADD, MS_BIND),
        mnt_opt ("move",            ADD, MS_MOVE),
        mnt_opt ("async",           REM, MS_SYNCHRONOUS),
        mnt_opt ("atime",           REM, MS_NOATIME),
        mnt_opt ("noatime",         ADD, MS_NOATIME),
        mnt_opt ("dev",             REM, MS_NODEV),
        mnt_opt ("nodev",           ADD, MS_NODEV),
        mnt_opt ("diratime",        REM, MS_NODIRATIME),
        mnt_opt ("nodiratime",      ADD, MS_NODIRATIME),
        mnt_opt ("dirsync",         ADD, MS_DIRSYNC),
        mnt_opt ("exec",            REM, MS_NOEXEC),
        mnt_opt ("noexec",          ADD, MS_NOEXEC),
        mnt_opt ("mand",            ADD, MS_MANDLOCK),
        mnt_opt ("nomand",          REM, MS_MANDLOCK),
        mnt_opt ("relatime",        ADD, MS_RELATIME),
        mnt_opt ("norelatime",      REM, MS_RELATIME),
        mnt_opt ("strictatime",     ADD, MS_STRICTATIME),
        mnt_opt ("nostrictatime",   REM, MS_STRICTATIME),
        mnt_opt ("suid",            REM, MS_NOSUID),
        mnt_opt ("nosuid",          ADD, MS_NOSUID),
        mnt_opt ("remount",         ADD, MS_REMOUNT),
        mnt_opt ("sync",            ADD, MS_SYNCHRONOUS)
    };
#undef mnt_opt
    size_t nb = sizeof (mnt_options) / sizeof (*mnt_options);

    for (;;)
    {
        size_t e;
        size_t i;

        e = str_chr (options, ',');
        for (i = 0; i < nb; ++i)
        {
            if (e == mnt_options[i].len
                    && !str_diffn (options, mnt_options[i].name, e))
            {
                switch (mnt_options[i].op)
                {
                    case OP_ADD:
                        *flags |= mnt_options[i].value;
                        break;

                    case OP_REM:
                        *flags &= ~mnt_options[i].value;
                        break;

                    case OP_SET:
                        *flags = mnt_options[i].value;
                        break;
                }
                break;
            }
        }
        if (i >= nb)
        {
            /* add user option as-is */
            if ((sa->len > 0 && !stralloc_catb (sa, ",", 1))
                    || !stralloc_catb (sa, options, e))
                return 0;
        }

        options += e;
        if (*options == '\0')
            return 1;
        ++options;
    }
}

static void
dieusage (int rc)
{
    aa_die_usage (rc, "[OPTION...] DEVICE MOUNTPOINT",
            " -D, --double-output           Enable double-output mode\n"
            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
            " -B, --bind                    Remount a subtree somewhere else\n"
            " -M, --move                    Move a subtree to some other place\n"
            " -t, --type FSTYPE             Use FSTYPE as type of filesystem\n"
            " -o, --options OPTIONS         Use OPTIONS as mount options\n"
            " -r, --read-only               Mount read-only\n"
            " -w, --read-write              Mount read-write\n"
            " -d, --mkdir                   Create MOUNTPOINT before mounting\n"
            " -h, --help                    Show this help screen and exit\n"
            " -V, --version                 Show version information and exit\n"
            );
}

int
main (int argc, char * const argv[])
{
    PROG = "aa-mount";
    stralloc sa = STRALLOC_ZERO;
    unsigned long flags = MS_MGC_VAL;
    const char *fstype = NULL;
    int mk = 0;

    for (;;)
    {
        struct option longopts[] = {
            { "bind",               no_argument,        NULL,   'B' },
            { "double-output",      no_argument,        NULL,   'D' },
            { "mkdir",              no_argument,        NULL,   'd' },
            { "help",               no_argument,        NULL,   'h' },
            { "move",               no_argument,        NULL,   'M' },
            { "log-file",           required_argument,  NULL,   'O' },
            { "options",            required_argument,  NULL,   'o' },
            { "read-only",          no_argument,        NULL,   'r' },
            { "type",               required_argument,  NULL,   't' },
            { "version",            no_argument,        NULL,   'V' },
            { "read-write",         no_argument,        NULL,   'w' },
            { NULL, 0, 0, 0 }
        };
        int c;

        c = getopt_long (argc, argv, "BDdhMO:o:rt:Vw", longopts, NULL);
        if (c == -1)
            break;
        switch (c)
        {
            case 'B':
                if (!add_option (&sa, &flags, "bind"))
                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                break;

            case 'D':
                aa_set_double_output (1);
                break;

            case 'd':
                mk = 1;
                break;

            case 'h':
                dieusage (RC_OK);

            case 'M':
                if (!add_option (&sa, &flags, "move"))
                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                break;

            case 'O':
                aa_set_log_file_or_die (optarg);
                break;

            case 'o':
                if (!add_option (&sa, &flags, optarg))
                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                break;

            case 'r':
                if (!add_option (&sa, &flags, "ro"))
                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                break;

            case 't':
                fstype = optarg;
                break;

            case 'V':
                aa_die_version ();

            case 'w':
                if (!add_option (&sa, &flags, "rw"))
                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                break;

            default:
                dieusage (RC_FATAL_USAGE);
        }
    }
    argc -= optind;
    argv += optind;

    if (argc < 2)
        dieusage (RC_FATAL_USAGE);

    if (!stralloc_0 (&sa))
        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
    if (mk && mkdir (argv[1], 0755) < 0 && errno != EEXIST)
        aa_strerr_diefu4sys (RC_FATAL_IO, "mkdir ", argv[1], " to mount ", argv[0]);
    if (mount (argv[0], argv[1], fstype, flags, sa.s) < 0)
        aa_strerr_diefu4sys (RC_FATAL_IO, "mount ", argv[0], " on ", argv[1]);

    return RC_OK;
}