/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * ocfs2ne.c * * ocfs2 tune utility. * * Copyright (C) 2004, 2008 Oracle. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2 as published by the Free Software Foundation. * * 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 _GNU_SOURCE /* for getopt_long and O_DIRECT */ #include #include #include #include #include #include #include "ocfs2/ocfs2.h" #include "libocfs2ne.h" /* * Why do we have a list of option structures will callbacks instead of * a simple switch() statement? Because the ocfs2ne option set has grown * over time, and there are a few operations that can be triggered by * more than one option. For example, -M {cluster|local} is really just * clearing or setting the fs feature 'local'. * * For most argument-free operations, they'll just specify their name and * val. Options with arguments will mostly use generic_handle_arg() as * their ->opt_handle(). * * If you are adding a new feature flag, do not add an option here. It * should be handled by --fs-features. Just write a tunefs_feature in * ocfs2ne_feature_.c and add it to the list ocfs2ne_features.c. * If you are adding an operation, make its option something that stands on * its own and can use generic_handle_arg() if it needs an argument. */ struct tunefs_option { struct option opt_option; /* For getopt_long(). If there is no short option, set .val to CHAR_MAX. A unique value will be inserted by the code. */ struct tunefs_operation *opt_op; /* Operation associated with this option. This needs to be set if the option has no ->opt_handle() or is using generic_handle_arg(). If set, opt_op will be added to the run_list when this option is seen. */ char *opt_help; /* Help string */ int opt_set; /* Was this option seen */ int (*opt_handle)(struct tunefs_option *opt, char *arg); void *opt_private; }; /* * ocfs2ne lumps all journal options as name[=value] arguments underneath * '-J'. They end up being tunefs_operations, and we link them up here. */ struct tunefs_journal_option { char *jo_name; char *jo_help; struct tunefs_operation *jo_op; }; /* Things to run */ struct tunefs_run { struct list_head tr_list; struct tunefs_operation *tr_op; }; extern struct tunefs_operation list_sparse_op; extern struct tunefs_operation query_op; extern struct tunefs_operation reset_uuid_op; extern struct tunefs_operation features_op; extern struct tunefs_operation resize_volume_op; extern struct tunefs_operation set_journal_size_op; extern struct tunefs_operation set_label_op; extern struct tunefs_operation set_slot_count_op; extern struct tunefs_operation update_cluster_stack_op; extern struct tunefs_operation cloned_volume_op; extern struct tunefs_operation set_usrquota_sync_interval_op; extern struct tunefs_operation set_grpquota_sync_interval_op; /* List of operations we're going to run */ static LIST_HEAD(tunefs_run_list); /* Number of operations we're going to run */ static int tunefs_op_count; /* Progress display for tunefs operations */ static struct tools_progress *tunefs_op_progress; static struct tunefs_journal_option set_journal_size_option = { .jo_name = "size", .jo_help = "size=", .jo_op = &set_journal_size_op, }; /* The list of all supported journal options */ static struct tunefs_journal_option *tunefs_journal_options[] = { &set_journal_size_option, NULL, }; /* * Operations are intended to run in the order we see them in the * command-line arguments. As each option is seen, the operation is * added with tunefs_append_operation(). * * There are two exceptions. First, special-cased options (pretty much * the feature) will end up at the end because we can't process them * until we've seen all command-line arguments. * * Second, resize is the only user of tunefs_prepend_operation(). We want * to grow the filesystem *before* we do anything that might require space! */ static errcode_t tunefs_append_operation(struct tunefs_operation *op) { errcode_t err; struct tunefs_run *run; err = ocfs2_malloc0(sizeof(struct tunefs_run), &run); if (!err) { run->tr_op = op; list_add_tail(&run->tr_list, &tunefs_run_list); tunefs_op_count++; } return err; } static errcode_t tunefs_prepend_operation(struct tunefs_operation *op) { errcode_t err; struct tunefs_run *run; err = ocfs2_malloc0(sizeof(struct tunefs_run), &run); if (!err) { run->tr_op = op; list_add(&run->tr_list, &tunefs_run_list); tunefs_op_count++; } return err; } static void print_usage(int rc); static int handle_help(struct tunefs_option *opt, char *arg) { print_usage(0); return 1; } static int handle_version(struct tunefs_option *opt, char *arg) { tools_version(); exit(0); return 1; } static int handle_verbosity(struct tunefs_option *opt, char *arg) { int rc = 0; switch (opt->opt_option.val) { case 'v': tools_verbose(); break; case 'q': tools_quiet(); break; default: errorf("Invalid option to handle_verbosity: %c\n", opt->opt_option.val); rc = 1; break; } /* More than one -v or -q is valid */ opt->opt_set = 0; return rc; } static int handle_interactive(struct tunefs_option *opt, char *arg) { tools_interactive(); return 0; } static int handle_progress(struct tunefs_option *opt, char *arg) { tools_progress_enable(); return 0; } static int handle_answer(struct tunefs_option *opt, char *arg) { int rc = 0; switch (opt->opt_option.val) { case 'y': tools_interactive_yes(); break; case 'n': tools_interactive_no(); break; default: errorf("Invalid option to handle_answer: %c\n", opt->opt_option.val); rc = 1; break; } return rc; } /* * Plain operations just want to have their ->to_parse_option() called. * Their tunefs_option can use this function if they set opt_op to the * tunefs_operation. */ static int generic_handle_arg(struct tunefs_option *opt, char *arg) { struct tunefs_operation *op = opt->opt_op; if (!op->to_parse_option) { errorf("Option \"%s\" claims it has an argument, but " "operation \"%s\" isn't expecting one\n", opt->opt_option.name, op->to_name); return 1; } return op->to_parse_option(op, arg); } /* * Store a copy of the argument on opt_private. * * For example, the multiple options setting fs_features want to save off * their feature string. They use this function directly or indirectly. */ static int strdup_handle_arg(struct tunefs_option *opt, char *arg) { char *ptr = NULL; if (arg) { ptr = strdup(arg); if (!ptr) { errorf("Unable to allocate memory while processing " "options\n"); return 1; } } opt->opt_private = ptr; return 0; } static int mount_type_handle_arg(struct tunefs_option *opt, char *arg) { int rc = 0; if (!arg) { errorf("No mount type specified\n"); rc = 1; } else if (!strcmp(arg, "local")) rc = strdup_handle_arg(opt, "local"); else if (!strcmp(arg, "cluster")) rc = strdup_handle_arg(opt, "nolocal"); else { errorf("Invalid mount type: \"%s\"\n", arg); rc = 1; } return rc; } static int backup_super_handle_arg(struct tunefs_option *opt, char *arg) { return strdup_handle_arg(opt, "backup-super"); } static struct tunefs_journal_option *find_journal_option(char *name) { int i; struct tunefs_journal_option *jopt; for (i = 0; tunefs_journal_options[i]; i++) { jopt = tunefs_journal_options[i]; if (!strcmp(name, jopt->jo_name)) return jopt; } return NULL; } /* derived from e2fsprogs */ static int handle_journal_arg(struct tunefs_option *opt, char *arg) { errcode_t err; int i, rc = 0; char *options, *token, *next, *p, *val; int journal_usage = 0; struct tunefs_journal_option *jopt; if (arg) { options = strdup(arg); if (!options) { tcom_err(TUNEFS_ET_NO_MEMORY, "while processing journal options"); return 1; } } else options = NULL; for (token = options; token && *token; token = next) { p = strchr(token, ','); next = NULL; if (p) { *p = '\0'; next = p + 1; } val = strchr(token, '='); if (val) { *val = '\0'; val++; } jopt = find_journal_option(token); if (!jopt) { errorf("Unknown journal option: \"%s\"\n", token); journal_usage++; continue; } if (jopt->jo_op->to_parse_option) { if (jopt->jo_op->to_parse_option(jopt->jo_op, val)) { journal_usage++; continue; } } else if (val) { errorf("Journal option \"%s\" does not accept " "arguments\n", token); journal_usage++; continue; } err = tunefs_append_operation(jopt->jo_op); if (err) { tcom_err(err, "while processing journal options"); rc = 1; break; } } if (journal_usage) { verbosef(VL_ERR, "Valid journal options are:\n"); for (i = 0; tunefs_journal_options[i]; i++) verbosef(VL_ERR, "\t%s\n", tunefs_journal_options[i]->jo_help); rc = 1; } free(options); return rc; } static struct tunefs_option help_option = { .opt_option = { .name = "help", .val = 'h', }, .opt_handle = handle_help, }; static struct tunefs_option version_option = { .opt_option = { .name = "version", .val = 'V', }, .opt_handle = handle_version, }; static struct tunefs_option verbose_option = { .opt_option = { .name = "verbose", .val = 'v', }, .opt_help = "-v|--verbose (increases verbosity; more than one permitted)", .opt_handle = handle_verbosity, }; static struct tunefs_option quiet_option = { .opt_option = { .name = "quiet", .val = 'q', }, .opt_help = "-q|--quiet (decreases verbosity; more than one permitted)", .opt_handle = handle_verbosity, }; static struct tunefs_option interactive_option = { .opt_option = { .name = "interactive", .val = 'i', }, .opt_help = "-i|--interactive", .opt_handle = handle_interactive, }; static struct tunefs_option progress_option = { .opt_option = { .name = "progress", .val = 'p', }, .opt_help = "-p|--progress", .opt_handle = handle_progress, }; static struct tunefs_option yes_option = { .opt_option = { .name = "yes", .val = 'y', }, .opt_help = "-y|--yes", .opt_handle = handle_answer, }; static struct tunefs_option no_option = { .opt_option = { .name = "no", .val = 'n', }, .opt_help = "-n|--no", .opt_handle = handle_answer, }; static struct tunefs_option query_option = { .opt_option = { .name = "query", .val = 'Q', .has_arg = 1, }, .opt_help = "-Q|--query ", .opt_handle = &generic_handle_arg, .opt_op = &query_op, }; static struct tunefs_option list_sparse_option = { .opt_option = { .name = "list-sparse", .val = CHAR_MAX, }, .opt_help = " --list-sparse", .opt_op = &list_sparse_op, }; static struct tunefs_option reset_uuid_option = { .opt_option = { .name = "uuid-reset", .val = 'U', .has_arg = 2, }, .opt_help = "-U|--uuid-reset[=new-uuid]", .opt_handle = &generic_handle_arg, .opt_op = &reset_uuid_op, }; static struct tunefs_option update_cluster_stack_option = { .opt_option = { .name = "update-cluster-stack", .val = CHAR_MAX, }, .opt_help = " --update-cluster-stack", .opt_op = &update_cluster_stack_op, }; static struct tunefs_option cloned_volume_option = { .opt_option = { .name = "cloned-volume", .val = CHAR_MAX, .has_arg = 2, }, .opt_help = " --cloned-volume[=new-label]", .opt_op = &cloned_volume_op, }; static struct tunefs_option set_slot_count_option = { .opt_option = { .name = "node-slots", .val = 'N', .has_arg = 1, }, .opt_help = "-N|--node-slots ", .opt_handle = generic_handle_arg, .opt_op = &set_slot_count_op, }; static struct tunefs_option set_label_option = { .opt_option = { .name = "label", .val = 'L', .has_arg = 1, }, .opt_help = "-L|--label