mirror of
https://github.com/nilfs-dev/nilfs-utils.git
synced 2026-01-26 13:43:15 +00:00
Update nilfs-clean to accept a regular file or directory path on the target filesystem in addition to a block device node. This change is implemented within the private library libcleaner. The functions nilfs_cleaner_launch() and nilfs_cleaner_open() in lib/cleaner_ctl.c are modified to automatically resolve the backing block device using the internal helper nilfs_lookup_device() if a filesystem node is passed. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com>
604 lines
14 KiB
C
604 lines
14 KiB
C
/*
|
|
* nilfs-clean.c - run garbage collector for nilfs2 volume
|
|
*
|
|
* Licensed under GPLv2: the complete text of the GNU General Public
|
|
* License can be found in COPYING file of the nilfs-utils package.
|
|
*
|
|
* Copyright (C) 2011-2012 Nippon Telegraph and Telephone Corporation.
|
|
*
|
|
* Credits:
|
|
* Ryusuke Konishi <konishi.ryusuke@gmail.com>
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#include <stdio.h>
|
|
|
|
#if HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif /* HAVE_STDLIB_H */
|
|
|
|
#if HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif /* HAVE_SYS_TYPES_H */
|
|
|
|
#if HAVE_LINUX_TYPES_H
|
|
#include <linux/types.h>
|
|
#endif /* HAVE_LINUX_TYPES_H */
|
|
|
|
#if HAVE_ERR_H
|
|
#include <err.h>
|
|
#endif /* HAVE_ERR_H */
|
|
|
|
#if HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif /* HAVE_FCNTL_H */
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif /* HAVE_UNISTD_H */
|
|
|
|
#if HAVE_SYS_IOCTL_H
|
|
#include <sys/ioctl.h>
|
|
#endif /* HAVE_SYS_IOCTL_H */
|
|
|
|
#if HAVE_LIMITS_H
|
|
#include <limits.h>
|
|
#endif /* HAVE_LIMITS_H */
|
|
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif /* HAVE_SYS_STRING_H */
|
|
|
|
#if HAVE_SYSLOG_H
|
|
#include <syslog.h> /* LOG_ERR, and so forth */
|
|
#endif /* HAVE_SYSLOG_H */
|
|
|
|
#if HAVE_TIME_H
|
|
#include <time.h> /* timespec, nanosleep() */
|
|
#endif /* HAVE_TIME_H */
|
|
|
|
#include <sys/stat.h>
|
|
#include <setjmp.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h> /* va_start, va_end, vfprintf */
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include "nls.h"
|
|
#include "nilfs.h"
|
|
#include "compat.h" /* getprogname() */
|
|
#include "nilfs_cleaner.h"
|
|
#include "parser.h"
|
|
#include "util.h"
|
|
|
|
#ifdef _GNU_SOURCE
|
|
#include <getopt.h>
|
|
static const struct option long_option[] = {
|
|
{"break", no_argument, NULL, 'b'},
|
|
{"reload", optional_argument, NULL, 'c'},
|
|
{"help", no_argument, NULL, 'h'},
|
|
{"status", no_argument, NULL, 'l'},
|
|
{"protection-period", required_argument, NULL, 'p'},
|
|
{"quit", no_argument, NULL, 'q'},
|
|
{"resume", no_argument, NULL, 'r'},
|
|
{"stop", no_argument, NULL, 'b'},
|
|
{"suspend", no_argument, NULL, 's'},
|
|
{"speed", required_argument, NULL, 'S'},
|
|
{"min-reclaimable-blocks", required_argument, NULL, 'm'},
|
|
{"verbose", no_argument, NULL, 'v'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
#define NILFS_CLEAN_USAGE \
|
|
"Usage: %s [options] [device|node]\n" \
|
|
" -b, --break,--stop\tstop running cleaner\n" \
|
|
" -c, --reload[=CONFFILE]\n" \
|
|
" \t\treload config\n" \
|
|
" -h, --help\t\tdisplay this help and exit\n" \
|
|
" -l, --status\t\tdisplay cleaner status\n" \
|
|
" -p, --protection-period=SECONDS\n" \
|
|
" \t\tspecify protection period\n" \
|
|
" -m, --min-reclaimable-blocks=COUNT[%%]\n" \
|
|
" \t\tset minimum number of reclaimable blocks\n" \
|
|
" \t\tbefore a segment can be cleaned\n" \
|
|
" -q, --quit\t\tshutdown cleaner\n" \
|
|
" -r, --resume\t\tresume cleaner\n" \
|
|
" -s, --suspend\t\tsuspend cleaner\n" \
|
|
" -S, --speed=COUNT[/SECONDS]\n" \
|
|
" \t\tset GC speed\n" \
|
|
" -v, --verbose\t\tverbose mode\n" \
|
|
" -V, --version\t\tdisplay version and exit\n"
|
|
#else
|
|
#define NILFS_CLEAN_USAGE \
|
|
"Usage: %s [-b] [-c [conffile]] [-h] [-l] [-m blocks]\n" \
|
|
" [-p protection-period] [-q] [-r] [-s] [-S gc-speed]\n" \
|
|
" [-v] [-V] [device|node]\n"
|
|
#endif /* _GNU_SOURCE */
|
|
|
|
|
|
enum {
|
|
NILFS_CLEAN_CMD_RUN,
|
|
NILFS_CLEAN_CMD_INFO,
|
|
NILFS_CLEAN_CMD_SUSPEND,
|
|
NILFS_CLEAN_CMD_RESUME,
|
|
NILFS_CLEAN_CMD_RELOAD,
|
|
NILFS_CLEAN_CMD_STOP,
|
|
NILFS_CLEAN_CMD_SHUTDOWN,
|
|
};
|
|
|
|
/* GC speed parameters */
|
|
#define NILFS_CLEAN_DEFAULT_GC_SPEED (1280ULL << 20) /* 1.28 GiB/s */
|
|
#define NILFS_CLEAN_MAX_SEGMENTS_PER_CALL 32
|
|
#define NILFS_CLEAN_MIN_SEGMENTS_PER_CALL 1
|
|
#define NILFS_CLEAN_DEFAULT_NSEGMENTS_PER_CALL 16
|
|
|
|
/* options */
|
|
static int show_version_only;
|
|
static int verbose;
|
|
static int clean_cmd = NILFS_CLEAN_CMD_RUN;
|
|
static const char *conffile;
|
|
|
|
static unsigned long protection_period = ULONG_MAX;
|
|
static unsigned int nsegments_per_clean = 0; /* to be adjusted */
|
|
static struct timespec cleaning_interval = { 0, 100000000 }; /* 100 msec */
|
|
static unsigned long min_reclaimable_blocks = ULONG_MAX;
|
|
static unsigned char min_reclaimable_blocks_unit = NILFS_CLEANER_ARG_UNIT_NONE;
|
|
|
|
static sigjmp_buf nilfs_clean_env;
|
|
static struct nilfs_cleaner *nilfs_cleaner;
|
|
|
|
/* cleaner parameter */
|
|
|
|
static void nilfs_clean_logger(int priority, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if ((verbose && priority > LOG_INFO) || priority >= LOG_INFO)
|
|
return;
|
|
va_start(args, fmt);
|
|
vwarnx(fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
static void nilfs_clean_escape(int signum)
|
|
{
|
|
siglongjmp(nilfs_clean_env, 1);
|
|
}
|
|
|
|
/**
|
|
* nilfs_clean_adjust_speed - adjust the GC speed instructed to cleanerd
|
|
* @cleaner: nilfs_cleaner struct
|
|
*
|
|
* nilfs_clean_adjust_speed() attempts to adjust the GC speed based on
|
|
* filesystem layout information, etc. Even if adjustment is not possible due
|
|
* to overflow or other reasons, it will not fail and set a default value.
|
|
*/
|
|
static void nilfs_clean_adjust_speed(struct nilfs_cleaner *cleaner)
|
|
{
|
|
const char *device = nilfs_cleaner_device(cleaner);
|
|
struct nilfs *nilfs;
|
|
uint64_t blocks_per_segment;
|
|
uint64_t interval_ms, nbytes, nsegs;
|
|
size_t block_size;
|
|
|
|
nilfs = nilfs_open(device, NULL, NILFS_OPEN_RDONLY | NILFS_OPEN_RAW);
|
|
if (unlikely(!nilfs))
|
|
goto fallback;
|
|
|
|
block_size = nilfs_get_block_size(nilfs);
|
|
blocks_per_segment = nilfs_get_blocks_per_segment(nilfs);
|
|
nilfs_close(nilfs);
|
|
|
|
interval_ms = (cleaning_interval.tv_sec * 1000 +
|
|
cleaning_interval.tv_nsec / 1000000);
|
|
|
|
if (unlikely(!block_size || !blocks_per_segment || !interval_ms))
|
|
goto fallback;
|
|
|
|
if (unlikely(blocks_per_segment > UINT64_MAX / block_size))
|
|
goto fallback;
|
|
|
|
nbytes = blocks_per_segment * block_size;
|
|
/* = Number of bytes per segment */
|
|
if (unlikely(nbytes > UINT64_MAX / 1000))
|
|
goto fallback;
|
|
|
|
nbytes = (nbytes * 1000) / interval_ms;
|
|
/* = Number of bytes to clean per second */
|
|
if (unlikely(!nbytes))
|
|
goto fallback;
|
|
|
|
nsegs = NILFS_CLEAN_DEFAULT_GC_SPEED / nbytes;
|
|
/* = Number of segments to clean per call */
|
|
nsegments_per_clean = max_t(unsigned int,
|
|
min_t(uint64_t, nsegs,
|
|
NILFS_CLEAN_MAX_SEGMENTS_PER_CALL),
|
|
NILFS_CLEAN_MIN_SEGMENTS_PER_CALL);
|
|
return;
|
|
|
|
fallback:
|
|
errno = 0;
|
|
nsegments_per_clean = NILFS_CLEAN_DEFAULT_NSEGMENTS_PER_CALL;
|
|
}
|
|
|
|
static int nilfs_clean_do_run(struct nilfs_cleaner *cleaner)
|
|
{
|
|
struct nilfs_cleaner_args args;
|
|
int ret;
|
|
|
|
if (!nsegments_per_clean)
|
|
nilfs_clean_adjust_speed(cleaner);
|
|
|
|
args.npasses = 1;
|
|
args.nsegments_per_clean = nsegments_per_clean;
|
|
args.cleaning_interval = cleaning_interval.tv_sec;
|
|
args.cleaning_interval_nsec = cleaning_interval.tv_nsec;
|
|
args.valid = (NILFS_CLEANER_ARG_NPASSES |
|
|
NILFS_CLEANER_ARG_CLEANING_INTERVAL |
|
|
NILFS_CLEANER_ARG_NSEGMENTS_PER_CLEAN);
|
|
|
|
if (protection_period != ULONG_MAX) {
|
|
args.protection_period = protection_period;
|
|
args.valid |= NILFS_CLEANER_ARG_PROTECTION_PERIOD;
|
|
}
|
|
|
|
if (min_reclaimable_blocks != ULONG_MAX) {
|
|
args.min_reclaimable_blocks = min_reclaimable_blocks;
|
|
args.min_reclaimable_blocks_unit = min_reclaimable_blocks_unit;
|
|
args.valid |= NILFS_CLEANER_ARG_MIN_RECLAIMABLE_BLOCKS;
|
|
}
|
|
|
|
ret = nilfs_cleaner_run(cleaner, &args, NULL);
|
|
if (unlikely(ret < 0)) {
|
|
warn(_("cannot run cleaner"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_do_getinfo(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int cleaner_status;
|
|
int ret;
|
|
|
|
ret = nilfs_cleaner_get_status(cleaner, &cleaner_status);
|
|
if (ret < 0) {
|
|
warn(_("cannot get cleaner status"));
|
|
return -1;
|
|
}
|
|
switch (cleaner_status) {
|
|
case NILFS_CLEANER_STATUS_IDLE:
|
|
puts(_("idle"));
|
|
break;
|
|
case NILFS_CLEANER_STATUS_RUNNING:
|
|
puts(_("running"));
|
|
break;
|
|
case NILFS_CLEANER_STATUS_SUSPENDED:
|
|
puts(_("suspended"));
|
|
break;
|
|
default:
|
|
printf(_("%d (unknown)\n"), cleaner_status);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_do_suspend(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int ret;
|
|
|
|
ret = nilfs_cleaner_suspend(cleaner);
|
|
if (unlikely(ret < 0)) {
|
|
warn(_("suspend failed"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_do_resume(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int ret;
|
|
|
|
ret = nilfs_cleaner_resume(cleaner);
|
|
if (unlikely(ret < 0)) {
|
|
warn(_("resume failed"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_do_reload(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int ret;
|
|
|
|
ret = nilfs_cleaner_reload(cleaner, conffile);
|
|
if (unlikely(ret < 0)) {
|
|
warn(_("reload failed"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_do_stop(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int ret;
|
|
|
|
ret = nilfs_cleaner_stop(cleaner);
|
|
if (unlikely(ret < 0)) {
|
|
warn(_("stop failed"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_do_shutdown(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int ret;
|
|
|
|
ret = nilfs_cleaner_shutdown(cleaner);
|
|
if (unlikely(ret < 0)) {
|
|
warn(_("shutdown failed"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int nilfs_clean_request(struct nilfs_cleaner *cleaner)
|
|
{
|
|
int status = EXIT_FAILURE;
|
|
int ret;
|
|
|
|
switch (clean_cmd) {
|
|
case NILFS_CLEAN_CMD_RUN:
|
|
ret = nilfs_clean_do_run(cleaner);
|
|
break;
|
|
case NILFS_CLEAN_CMD_INFO:
|
|
ret = nilfs_clean_do_getinfo(cleaner);
|
|
break;
|
|
case NILFS_CLEAN_CMD_SUSPEND:
|
|
ret = nilfs_clean_do_suspend(cleaner);
|
|
break;
|
|
case NILFS_CLEAN_CMD_RESUME:
|
|
ret = nilfs_clean_do_resume(cleaner);
|
|
break;
|
|
case NILFS_CLEAN_CMD_RELOAD:
|
|
ret = nilfs_clean_do_reload(cleaner);
|
|
break;
|
|
case NILFS_CLEAN_CMD_STOP:
|
|
ret = nilfs_clean_do_stop(cleaner);
|
|
break;
|
|
case NILFS_CLEAN_CMD_SHUTDOWN:
|
|
ret = nilfs_clean_do_shutdown(cleaner);
|
|
break;
|
|
default:
|
|
goto out;
|
|
}
|
|
if (likely(!ret))
|
|
status = EXIT_SUCCESS;
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
static int nilfs_do_clean(const char *device)
|
|
{
|
|
struct sigaction act, oldact[3];
|
|
int status = EXIT_FAILURE;
|
|
|
|
nilfs_cleaner = nilfs_cleaner_open(device, NULL,
|
|
NILFS_CLEANER_OPEN_QUEUE);
|
|
if (unlikely(!nilfs_cleaner))
|
|
goto out;
|
|
|
|
if (!sigsetjmp(nilfs_clean_env, 1)) {
|
|
memset(&act, 0, sizeof(act));
|
|
sigfillset(&act.sa_mask);
|
|
act.sa_handler = nilfs_clean_escape;
|
|
act.sa_flags = 0;
|
|
|
|
if (unlikely(sigaction(SIGINT, &act, &oldact[0]) < 0 ||
|
|
sigaction(SIGTERM, &act, &oldact[1]) < 0 ||
|
|
sigaction(SIGHUP, &act, &oldact[2]) < 0))
|
|
siglongjmp(nilfs_clean_env, 1);
|
|
|
|
status = nilfs_clean_request(nilfs_cleaner);
|
|
|
|
sigaction(SIGINT, &oldact[0], NULL);
|
|
sigaction(SIGTERM, &oldact[1], NULL);
|
|
sigaction(SIGHUP, &oldact[2], NULL);
|
|
}
|
|
|
|
nilfs_cleaner_close(nilfs_cleaner);
|
|
out:
|
|
return status;
|
|
}
|
|
|
|
static void nilfs_clean_usage(FILE *stream)
|
|
{
|
|
fprintf(stream, NILFS_CLEAN_USAGE, getprogname());
|
|
}
|
|
|
|
static int nilfs_clean_parse_gcspeed(const char *arg)
|
|
{
|
|
unsigned long nsegs;
|
|
unsigned long interval;
|
|
double interval2;
|
|
char *endptr, *endptr2;
|
|
|
|
nsegs = strtoul(arg, &endptr, 10);
|
|
if (endptr == arg || (endptr[0] != '/' && endptr[0] != '\0'))
|
|
goto failed;
|
|
|
|
if (nsegs == ULONG_MAX)
|
|
goto failed_too_large;
|
|
|
|
if (endptr[0] == '/') {
|
|
interval = strtoul(&endptr[1], &endptr2, 10);
|
|
if (endptr2 == &endptr[1])
|
|
goto failed;
|
|
if (endptr2[0] == '.') {
|
|
errno = 0;
|
|
interval2 = strtod(&endptr[1], &endptr2);
|
|
if (endptr2 == &endptr[1] || endptr2[0] != '\0')
|
|
goto failed;
|
|
if (errno == ERANGE)
|
|
goto failed_too_large;
|
|
cleaning_interval.tv_sec = interval;
|
|
cleaning_interval.tv_nsec =
|
|
(interval2 - interval) * 1000000000;
|
|
} else if (endptr2[0] != '\0') {
|
|
goto failed;
|
|
} else {
|
|
if (interval == ULONG_MAX)
|
|
goto failed_too_large;
|
|
cleaning_interval.tv_sec = interval;
|
|
cleaning_interval.tv_nsec = 0;
|
|
}
|
|
} else {
|
|
cleaning_interval.tv_sec = 1;
|
|
cleaning_interval.tv_nsec = 0;
|
|
}
|
|
nsegments_per_clean = nsegs;
|
|
return 0;
|
|
failed:
|
|
warnx(_("invalid gc speed: %s"), arg);
|
|
return -1;
|
|
|
|
failed_too_large:
|
|
warnx(_("value too large: %s"), arg);
|
|
return -1;
|
|
}
|
|
|
|
static int nilfs_clean_parse_min_reclaimable(const char *arg)
|
|
{
|
|
unsigned long blocks;
|
|
char *endptr;
|
|
|
|
blocks = strtoul(arg, &endptr, 10);
|
|
if (endptr == arg || (endptr[0] != '\0' && endptr[0] != '%')) {
|
|
warnx(_("invalid reclaimable blocks: %s"), arg);
|
|
return -1;
|
|
}
|
|
|
|
if (blocks == ULONG_MAX) {
|
|
warnx(_("value too large: %s"), arg);
|
|
return -1;
|
|
}
|
|
|
|
if (endptr[0] == '%') {
|
|
if (blocks > 100) {
|
|
warnx(_("percent value can't be > 100: %s"), arg);
|
|
return -1;
|
|
}
|
|
min_reclaimable_blocks_unit = NILFS_CLEANER_ARG_UNIT_PERCENT;
|
|
} else {
|
|
min_reclaimable_blocks_unit = NILFS_CLEANER_ARG_UNIT_NONE;
|
|
}
|
|
|
|
min_reclaimable_blocks = blocks;
|
|
return 0;
|
|
}
|
|
|
|
static void nilfs_clean_parse_options(int argc, char *argv[])
|
|
{
|
|
#ifdef _GNU_SOURCE
|
|
int option_index;
|
|
#endif /* _GNU_SOURCE */
|
|
int c, ret;
|
|
|
|
#ifdef _GNU_SOURCE
|
|
while ((c = getopt_long(argc, argv, "bc::hlm:p:qrsS:vV",
|
|
long_option, &option_index)) >= 0) {
|
|
#else
|
|
while ((c = getopt(argc, argv, "bc::hlm:p:qrsS:vV")) >= 0) {
|
|
#endif /* _GNU_SOURCE */
|
|
switch (c) {
|
|
case 'b':
|
|
clean_cmd = NILFS_CLEAN_CMD_STOP;
|
|
break;
|
|
case 'c':
|
|
if (optarg != NULL)
|
|
conffile = optarg;
|
|
clean_cmd = NILFS_CLEAN_CMD_RELOAD;
|
|
break;
|
|
case 'h':
|
|
nilfs_clean_usage(stdout);
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
case 'l':
|
|
clean_cmd = NILFS_CLEAN_CMD_INFO;
|
|
break;
|
|
case 'm':
|
|
if (nilfs_clean_parse_min_reclaimable(optarg) < 0)
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
case 'p':
|
|
ret = nilfs_parse_protection_period(
|
|
optarg, &protection_period);
|
|
if (!ret)
|
|
break;
|
|
|
|
if (errno == ERANGE)
|
|
errx(EXIT_FAILURE,
|
|
_("too large period: %s"), optarg);
|
|
else
|
|
errx(EXIT_FAILURE,
|
|
_("invalid protection period: %s"),
|
|
optarg);
|
|
case 'q':
|
|
clean_cmd = NILFS_CLEAN_CMD_SHUTDOWN;
|
|
break;
|
|
case 'r':
|
|
clean_cmd = NILFS_CLEAN_CMD_RESUME;
|
|
break;
|
|
case 's':
|
|
clean_cmd = NILFS_CLEAN_CMD_SUSPEND;
|
|
break;
|
|
case 'S':
|
|
if (nilfs_clean_parse_gcspeed(optarg) < 0)
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'V':
|
|
show_version_only = 1;
|
|
break;
|
|
default:
|
|
nilfs_clean_usage(stderr);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct stat statbuf;
|
|
char *device = NULL;
|
|
int status;
|
|
|
|
nilfs_cleaner_logger = nilfs_clean_logger;
|
|
|
|
|
|
nilfs_clean_parse_options(argc, argv);
|
|
if (show_version_only) {
|
|
printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION);
|
|
status = EXIT_SUCCESS;
|
|
goto out;
|
|
}
|
|
|
|
if (optind < argc) {
|
|
device = argv[optind++];
|
|
|
|
if (stat(device, &statbuf) < 0)
|
|
err(EXIT_FAILURE, _("cannot find '%s'"), device);
|
|
}
|
|
if (optind < argc)
|
|
errx(EXIT_FAILURE, _("too many arguments."));
|
|
|
|
status = nilfs_do_clean(device);
|
|
out:
|
|
exit(status);
|
|
}
|