Discussion:
[PATCH 2/2] lssu: add option to print count of live blocks
Ryusuke Konishi
2014-02-09 18:48:34 UTC
Permalink
This adds new options "-l" and "-p" to lssu command.

"-l" option prints count and ratio of live blocks for in-use segments,
and "-p" option allows users to specify a protection period which is
used to judge whether blocks are live or not.

Signed-off-by: Ryusuke Konishi <konishi.ryusuke-***@public.gmane.org>
---
bin/Makefile.am | 2 +
bin/lssu.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++-------
man/lssu.1 | 14 +++-
3 files changed, 235 insertions(+), 32 deletions(-)

diff --git a/bin/Makefile.am b/bin/Makefile.am
index 9d63d06..1a7de64 100644
--- a/bin/Makefile.am
+++ b/bin/Makefile.am
@@ -14,6 +14,8 @@ dumpseg_SOURCES = dumpseg.c
lscp_SOURCES = lscp.c

lssu_SOURCES = lssu.c
+lssu_LDADD = $(LDADD) $(top_builddir)/lib/libnilfsgc.la \
+ $(top_builddir)/lib/libparser.la

mkcp_SOURCES = mkcp.c
mkcp_LDADD = $(LDADD) $(LIB_POSIX_SEM)
diff --git a/bin/lssu.c b/bin/lssu.c
index 443910e..a452d19 100644
--- a/bin/lssu.c
+++ b/bin/lssu.c
@@ -37,79 +37,195 @@
#include <string.h>
#endif /* HAVE_STRING_H */

+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME */
+
#if HAVE_TIME_H
#include <time.h>
#endif /* HAVE_TIME_H */

#include <errno.h>
#include "nilfs.h"
+#include "nilfs_gc.h"
+#include "cnoconv.h"
+#include "parser.h"

#ifdef _GNU_SOURCE
#include <getopt.h>
const static struct option long_option[] = {
{"all", no_argument, NULL, 'a'},
{"index", required_argument, NULL, 'i'},
+ {"latest-usage", no_argument, NULL, 'l' },
{"lines", required_argument, NULL, 'n'},
+ {"protection-period", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};

-#define LSSU_USAGE "Usage: %s [OPTION]... [DEVICE]\n" \
- " -a, --all\t\tdo not hide clean segments\n" \
- " -i, --index\t\tstart index\n" \
- " -n, --lines\t\toutput lines\n" \
- " -h, --help\t\tdisplay this help and exit\n" \
- " -V, --version\t\tdisplay version and exit\n"
+#define LSSU_USAGE \
+ "Usage: %s [OPTION]... [DEVICE]\n" \
+ " -a, --all\t\t\tdo not hide clean segments\n" \
+ " -h, --help\t\t\tdisplay this help and exit\n" \
+ " -i, --index\t\t\tskip index segments at start of inputs\n" \
+ " -l, --latest-usage\t\tprint usage status of the moment\n" \
+ " -n, --lines\t\t\tlist only lines input segments\n" \
+ " -p, --protection-period\tspecify protection period\n" \
+ " -V, --version\t\t\tdisplay version and exit\n"
#else /* !_GNU_SOURCE */
#include <unistd.h>
-#define LSSU_USAGE "Usage: %s [-ahV] [-i index] [-n lines] [device]\n"
+#define LSSU_USAGE \
+ "Usage: %s [-alhV] [-i index] [-n lines] [-p period] [device]\n"
#endif /* _GNU_SOURCE */

#define LSSU_BUFSIZE 128
#define LSSU_NSEGS 512

+enum lssu_mode {
+ LSSU_MODE_NORMAL,
+ LSSU_MODE_LATEST_USAGE,
+};
+
+struct lssu_format {
+ char *header;
+ char *body;
+};
+
+const static struct lssu_format lssu_format[] = {
+ {
+ " SEGNUM DATE TIME STAT NBLOCKS\n",
+ "%20llu %s %c%c%c %10u\n"
+ },
+ {
+ " SEGNUM DATE TIME STAT NBLOCKS"
+ " NLIVEBLOCKS\n",
+ "%17llu %s %c%c%c %10u %10u (%3u%%)\n"
+ }
+};
+
+static char *progname;
+
+static int all;
+static int latest;
+static int disp_mode; /* display mode */
+static nilfs_cno_t protcno;
static __u64 param_index;
static __u64 param_lines;
-static struct nilfs_suinfo suinfos[LSSU_NSEGS];

+static size_t blocks_per_segment;
+static struct nilfs_suinfo suinfos[LSSU_NSEGS];

static void lssu_print_header(void)
{
- printf(" SEGNUM DATE TIME STAT NBLOCKS\n");
+ printf(lssu_format[disp_mode].header);
+}
+
+static ssize_t lssu_get_latest_usage(struct nilfs *nilfs,
+ __u64 segnum, __u64 protseq,
+ nilfs_cno_t protcno)
+{
+ struct nilfs_reclaim_stat stat;
+ struct nilfs_reclaim_params params = {
+ .flags = NILFS_RECLAIM_PARAM_PROTSEQ,
+ .protseq = protseq
+ };
+ __u64 segnums[1];
+ int ret;
+
+ if (protcno == NILFS_CNO_MAX) {
+ params.flags |= NILFS_RECLAIM_PARAM_PROTCNO;
+ params.protcno = protcno;
+ }
+
+ memset(&stat, 0, sizeof(stat));
+ segnums[0] = segnum;
+
+ ret = nilfs_assess_segment(nilfs, segnums, 1, &params, &stat);
+ if (ret < 0)
+ return -1;
+
+ if (stat.protected_segs > 0)
+ return -2;
+
+ return stat.live_blks;
}

-static ssize_t lssu_print_suinfo(__u64 segnum, ssize_t nsi, int all)
+static ssize_t lssu_print_suinfo(struct nilfs *nilfs, __u64 segnum,
+ ssize_t nsi, __u64 protseq)
{
struct tm tm;
time_t t;
char timebuf[LSSU_BUFSIZE];
- ssize_t i, n = 0;
+ ssize_t i, n = 0, ret;
+ int ratio;
+ size_t nliveblks;

for (i = 0; i < nsi; i++, segnum++) {
- if (all || !nilfs_suinfo_clean(&suinfos[i])) {
- t = (time_t)suinfos[i].sui_lastmod;
- if (t != 0) {
- localtime_r(&t, &tm);
- strftime(timebuf, LSSU_BUFSIZE, "%F %T", &tm);
- } else
- snprintf(timebuf, LSSU_BUFSIZE,
- "---------- --:--:--");
-
- printf("%20llu %s %c%c%c %10u\n",
+ if (!all && nilfs_suinfo_clean(&suinfos[i]))
+ continue;
+
+ t = (time_t)suinfos[i].sui_lastmod;
+ if (t != 0) {
+ localtime_r(&t, &tm);
+ strftime(timebuf, LSSU_BUFSIZE, "%F %T", &tm);
+ } else
+ snprintf(timebuf, LSSU_BUFSIZE,
+ "---------- --:--:--");
+
+ switch (disp_mode) {
+ case LSSU_MODE_NORMAL:
+ printf(lssu_format[disp_mode].body,
(unsigned long long)segnum,
timebuf,
nilfs_suinfo_active(&suinfos[i]) ? 'a' : '-',
nilfs_suinfo_dirty(&suinfos[i]) ? 'd' : '-',
nilfs_suinfo_error(&suinfos[i]) ? 'e' : '-',
suinfos[i].sui_nblocks);
- n++;
+ break;
+ case LSSU_MODE_LATEST_USAGE:
+ nliveblks = 0;
+ ratio = 0;
+
+ if (!nilfs_suinfo_dirty(&suinfos[i]) ||
+ nilfs_suinfo_error(&suinfos[i]))
+ goto skip_scan;
+
+ ret = lssu_get_latest_usage(nilfs, segnum, protseq,
+ protcno);
+ if (ret >= 0) {
+ nliveblks = ret;
+ ratio = (ret * 100 + 99) / blocks_per_segment;
+ } else if (ret == -2) {
+ nliveblks = suinfos[i].sui_nblocks;
+ ratio = 100;
+ } else {
+ fprintf(stderr,
+ "%s: failed to get usage: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+
+ skip_scan:
+ printf(lssu_format[disp_mode].body,
+ (unsigned long long)segnum,
+ timebuf,
+ nilfs_suinfo_active(&suinfos[i]) ? 'a' : '-',
+ nilfs_suinfo_dirty(&suinfos[i]) ? 'd' : '-',
+ nilfs_suinfo_error(&suinfos[i]) ? 'e' : '-',
+ suinfos[i].sui_nblocks, nliveblks, ratio);
+ break;
}
+ n++;
}
return n;
}

-static int lssu_list_suinfo(struct nilfs *nilfs, int all)
+static int lssu_list_suinfo(struct nilfs *nilfs)
{
struct nilfs_sustat sustat;
__u64 segnum, rest, count;
@@ -128,23 +244,66 @@ static int lssu_list_suinfo(struct nilfs *nilfs, int all)
if (nsi < 0)
return 1;

- n = lssu_print_suinfo(segnum, nsi, all);
+ n = lssu_print_suinfo(nilfs, segnum, nsi, sustat.ss_prot_seq);
segnum += nsi;
}

return 0;
}

+static int lssu_get_protcno(struct nilfs *nilfs,
+ unsigned long protection_period,
+ nilfs_cno_t *protcnop)
+{
+ struct nilfs_cnoconv *cnoconv;
+ struct timeval tv;
+ __u64 prottime;
+ int ret;
+
+ if (protection_period == ULONG_MAX) {
+ *protcnop = NILFS_CNO_MAX;
+ return 0;
+ }
+
+ ret = gettimeofday(&tv, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "%s: cannot get current time: %m\n", progname);
+ return -1;
+ }
+ prottime = tv.tv_sec - protection_period;
+
+ cnoconv = nilfs_cnoconv_create(nilfs);
+ if (!cnoconv) {
+ fprintf(stderr,
+ "%s: cannot create checkpoint number converter: %m\n",
+ progname);
+ return -1;
+ }
+
+ ret = nilfs_cnoconv_time2cno(cnoconv, prottime, protcnop);
+ if (ret < 0) {
+ fprintf(stderr,
+ "%s: cannot convert protectoin time to checkpoint "
+ "number: %m\n",
+ progname);
+ }
+
+ nilfs_cnoconv_destroy(cnoconv);
+ return ret;
+}
+
int main(int argc, char *argv[])
{
struct nilfs *nilfs;
- char *dev, *progname;
- int c, all, status;
+ char *dev;
+ int c, status;
+ int open_flags;
+ unsigned long protection_period = ULONG_MAX;
+ int ret;
#ifdef _GNU_SOURCE
int option_index;
#endif /* _GNU_SOURCE */

- all = 0;
opterr = 0;
progname = strrchr(argv[0], '/');
if (progname == NULL)
@@ -153,10 +312,10 @@ int main(int argc, char *argv[])
progname++;

#ifdef _GNU_SOURCE
- while ((c = getopt_long(argc, argv, "ai:n:hV",
+ while ((c = getopt_long(argc, argv, "ai:ln:hp:V",
long_option, &option_index)) >= 0) {
#else /* !_GNU_SOURCE */
- while ((c = getopt(argc, argv, "ai:n:hV")) >= 0) {
+ while ((c = getopt(argc, argv, "ai:ln:hp:V")) >= 0) {
#endif /* _GNU_SOURCE */

switch (c) {
@@ -166,12 +325,30 @@ int main(int argc, char *argv[])
case 'i':
param_index = (__u64)atoll(optarg);
break;
+ case 'l':
+ latest = 1;
+ break;
case 'n':
param_lines = (__u64)atoll(optarg);
break;
case 'h':
fprintf(stderr, LSSU_USAGE, progname);
exit(0);
+ case 'p':
+ ret = nilfs_parse_protection_period(
+ optarg, &protection_period);
+ if (!ret)
+ break;
+
+ if (errno == ERANGE) {
+ fprintf(stderr, "too large period: %s\n",
+ optarg);
+ } else {
+ fprintf(stderr,
+ "invalid protection period: %s\n",
+ optarg);
+ }
+ exit(1);
case 'V':
printf("%s (%s %s)\n", progname, PACKAGE,
PACKAGE_VERSION);
@@ -192,14 +369,26 @@ int main(int argc, char *argv[])
exit(1);
}

- nilfs = nilfs_open(dev, NULL, NILFS_OPEN_RDONLY);
+ open_flags = NILFS_OPEN_RDONLY;
+ if (latest)
+ open_flags |= NILFS_OPEN_RAW | NILFS_OPEN_GCLK;
+
+ nilfs = nilfs_open(dev, NULL, open_flags);
if (nilfs == NULL) {
fprintf(stderr, "%s: %s: cannot open NILFS\n",
progname, dev);
exit(1);
}

- status = lssu_list_suinfo(nilfs, all);
+ if (latest) {
+ blocks_per_segment = nilfs_get_blocks_per_segment(nilfs);
+ disp_mode = LSSU_MODE_LATEST_USAGE;
+ ret = lssu_get_protcno(nilfs, protection_period, &protcno);
+ if (ret < 0)
+ exit(1);
+ }
+
+ status = lssu_list_suinfo(nilfs);

nilfs_close(nilfs);
exit(status);
diff --git a/man/lssu.1 b/man/lssu.1
index 00cfcfa..2b58f8f 100644
--- a/man/lssu.1
+++ b/man/lssu.1
@@ -1,7 +1,7 @@
.\" Copyright (C) 2007-2012 Nippon Telegraph and Telephone Corporation.
.\" Written by Ryusuke Konishi <konishi.ryusuke-***@public.gmane.org>
.\"
-.TH LSSU 1 "May 2011" "nilfs-utils version 2.1"
+.TH LSSU 1 "Feb 2014" "nilfs-utils version 2.1"
.SH NAME
lssu \- list usage state of NILFS2 segments
.SH SYNOPSIS
@@ -27,9 +27,17 @@ Display help message and exit.
\fB\-i \fIindex\fR, \fB\-\-index\fR=\fIindex\fR
Skip \fIindex\fP segments at start of input.
.TP
+\fB\-l\fR, \fB\-\-latest-usage\fR
+Print usage status of the moment.
+.TP
\fB\-n \fIlines\fR, \fB\-\-lines\fR=\fIlines\fR
List only \fIlines\fP input segments.
.TP
+\fB\-p \fIperiod\fR, \fB\-\-protection-period\fR=\fIperiod\fR
+Specify protection period. This option is used when printing usage
+status of the moment (with \fB\-l\fR option) to test if each
+block in segments is protected and is not reclaimable.
+.TP
\fB\-V\fR, \fB\-\-version\fR
Display version and exit.
.SH "FIELD DESCRIPTION"
@@ -63,6 +71,10 @@ error. NILFS2 avoids allocating the segments with this flag.
.TP
.B NBLOCKS
Number of in-use blocks of the segment.
+.TP
+.B NLIVEBLOCKS (optional)
+Number and ratio of in-use blocks of the moment. This field is
+displayed when \fB\-l\fR option is specified.
.SH AUTHOR
Koji Sato
.SH AVAILABILITY
--
1.7.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Ryusuke Konishi
2014-02-09 18:48:33 UTC
Permalink
This renames checkpoint number library "lib/cno.c" to "lib/parser.c",
and moves a parser routine of a protection period option included in
nilfs-clean.c into parser.c to make it available from other programs.

Signed-off-by: Ryusuke Konishi <konishi.ryusuke-***@public.gmane.org>
---
bin/Makefile.am | 4 +-
bin/chcp.c | 2 +-
bin/rmcp.c | 2 +-
include/Makefile.am | 2 +-
include/cno.h | 17 -----
include/parser.h | 19 +++++
lib/Makefile.am | 10 +--
lib/cno.c | 101 --------------------------
lib/parser.c | 154 ++++++++++++++++++++++++++++++++++++++++
sbin/nilfs-clean/Makefile.am | 3 +-
sbin/nilfs-clean/nilfs-clean.c | 69 ++++--------------
11 files changed, 200 insertions(+), 183 deletions(-)
delete mode 100644 include/cno.h
create mode 100644 include/parser.h
delete mode 100644 lib/cno.c
create mode 100644 lib/parser.c

diff --git a/bin/Makefile.am b/bin/Makefile.am
index 6623022..9d63d06 100644
--- a/bin/Makefile.am
+++ b/bin/Makefile.am
@@ -7,7 +7,7 @@ LDADD = $(top_builddir)/lib/libnilfs.la
bin_PROGRAMS = chcp dumpseg lscp lssu mkcp rmcp

chcp_SOURCES = chcp.c
-chcp_LDADD = $(LDADD) $(LIB_POSIX_SEM) $(top_builddir)/lib/libcno.la
+chcp_LDADD = $(LDADD) $(LIB_POSIX_SEM) $(top_builddir)/lib/libparser.la

dumpseg_SOURCES = dumpseg.c

@@ -19,6 +19,6 @@ mkcp_SOURCES = mkcp.c
mkcp_LDADD = $(LDADD) $(LIB_POSIX_SEM)

rmcp_SOURCES = rmcp.c
-rmcp_LDADD = $(LDADD) $(top_builddir)/lib/libcno.la
+rmcp_LDADD = $(LDADD) $(top_builddir)/lib/libparser.la

EXTRA_DIST = .gitignore
diff --git a/bin/chcp.c b/bin/chcp.c
index 5cf8c22..e1d31fd 100644
--- a/bin/chcp.c
+++ b/bin/chcp.c
@@ -49,7 +49,7 @@
#include <errno.h>
#include <signal.h>
#include "nilfs.h"
-#include "cno.h"
+#include "parser.h"


#define CHCP_MODE_CP "cp"
diff --git a/bin/rmcp.c b/bin/rmcp.c
index 26d1282..af9eeb0 100644
--- a/bin/rmcp.c
+++ b/bin/rmcp.c
@@ -49,7 +49,7 @@

#include <errno.h>
#include "nilfs.h"
-#include "cno.h"
+#include "parser.h"


#ifdef _GNU_SOURCE
diff --git a/include/Makefile.am b/include/Makefile.am
index 1b6d812..2b46bb5 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,6 +1,6 @@
## Makefile.am

include_HEADERS = nilfs.h nilfs2_fs.h nilfs_cleaner.h
-noinst_HEADERS = realpath.h nls.h cno.h nilfs_feature.h \
+noinst_HEADERS = realpath.h nls.h parser.h nilfs_feature.h \
vector.h nilfs_gc.h cnoconv.h cleaner_msg.h cleaner_exec.h \
pathnames.h
diff --git a/include/cno.h b/include/cno.h
deleted file mode 100644
index cb4b74b..0000000
--- a/include/cno.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * cno.h - checkpoint number library
- *
- * Copyright (C) 2005-2012 Nippon Telegraph and Telephone Corporation.
- *
- * This program can be redistributed under the terms of the GNU Lesser
- * General Public License.
- */
-
-#ifndef NILFS_CNO_H
-#define NILFS_CNO_H
-
-extern nilfs_cno_t nilfs_parse_cno(const char *arg, char **endptr, int base);
-extern int nilfs_parse_cno_range(const char *arg, __u64 *start, __u64 *end,
- int base);
-
-#endif /* NILFS_CNO_H */
diff --git a/include/parser.h b/include/parser.h
new file mode 100644
index 0000000..7795678
--- /dev/null
+++ b/include/parser.h
@@ -0,0 +1,19 @@
+/*
+ * parser.h - NILFS parser library
+ *
+ * Copyright (C) 2005-2012 Nippon Telegraph and Telephone Corporation.
+ *
+ * This program can be redistributed under the terms of the GNU Lesser
+ * General Public License.
+ */
+
+#ifndef NILFS_PARSER_H
+#define NILFS_PARSER_H
+
+extern nilfs_cno_t nilfs_parse_cno(const char *arg, char **endptr, int base);
+extern int nilfs_parse_cno_range(const char *arg, __u64 *start, __u64 *end,
+ int base);
+extern int nilfs_parse_protection_period(const char *arg,
+ unsigned long *period);
+
+#endif /* NILFS_PARSER_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 0b9fbf1..82ba3c3 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,7 +1,7 @@
## Makefile.am

lib_LTLIBRARIES = libnilfs.la libnilfsgc.la libnilfscleaner.la
-noinst_LTLIBRARIES = librealpath.la libnilfsfeature.la libcno.la \
+noinst_LTLIBRARIES = librealpath.la libnilfsfeature.la libparser.la \
libmountchk.la libcrc32.la libcleanerexec.la

librealpath_la_SOURCES = realpath.c
@@ -14,10 +14,10 @@ libnilfsfeature_la_CFLAGS = -Wall -fPIC
libnilfsfeature_la_CPPFLAGS = -I$(top_srcdir)/include
libnilfsfeature_la_LDFLAGS = -static

-libcno_la_SOURCES = cno.c
-libcno_la_CFLAGS = -Wall -fPIC
-libcno_la_CPPFLAGS = -I$(top_srcdir)/include
-libcno_la_LDFLAGS = -static
+libparser_la_SOURCES = parser.c
+libparser_la_CFLAGS = -Wall -fPIC
+libparser_la_CPPFLAGS = -I$(top_srcdir)/include
+libparser_la_LDFLAGS = -static

libmountchk_la_SOURCES = ismounted.c
libmountchk_la_CFLAGS = -Wall -fPIC
diff --git a/lib/cno.c b/lib/cno.c
deleted file mode 100644
index df143cb..0000000
--- a/lib/cno.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * cno.c - NILFS checkpoint number parser
- *
- * Copyright (C) 2009-2012 Nippon Telegraph and Telephone Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Written by Ryusuke Konishi <konishi.ryusuke-***@public.gmane.org>.
- */
-
-#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_STRING_H
-#include <string.h>
-#endif /* HAVE_STRING_H */
-
-#include <assert.h>
-#include <ctype.h>
-#include "nilfs.h"
-
-nilfs_cno_t nilfs_parse_cno(const char *arg, char **endptr, int base)
-{
- /* ensure the number we are about to parse is not negative, which
- * strtoull() will happily accept and cast to an unsigned value. */
- while (isspace(*arg))
- arg++;
- if (*arg == '-')
- return NILFS_CNO_MAX;
-
- return strtoull(arg, endptr, base);
-}
-
-int nilfs_parse_cno_range(const char *arg, __u64 *start, __u64 *end, int base)
-{
- const char *delim;
- char *endptr;
- nilfs_cno_t cno, cno2;
-
- assert(arg && *arg != '\0');
-
- delim = strstr(arg, "..");
- if (delim && delim == arg) {
- if (arg[2] != '\0') {
- /* ..yyy */
- cno = nilfs_parse_cno(arg + 2, &endptr, base);
- if (cno < NILFS_CNO_MAX && *endptr == '\0') {
- /* ..CNO */
- *start = NILFS_CNO_MIN;
- *end = cno;
- return 0;
- }
- }
- } else if (!delim) {
- /* xxx */
- cno = nilfs_parse_cno(arg, &endptr, base);
- if (cno < NILFS_CNO_MAX && *endptr == '\0') {
- /* CNO */
- *start = *end = cno;
- return 0;
- }
- } else {
- /* xxx..yyy */
- cno = nilfs_parse_cno(arg, &endptr, base);
- if (cno < NILFS_CNO_MAX && endptr == delim) {
- if (delim[2] == '\0') {
- /* CNO.. */
- *start = cno;
- *end = NILFS_CNO_MAX;
- return 0;
- }
- cno2 = nilfs_parse_cno(delim + 2, &endptr, base);
- if (cno2 < NILFS_CNO_MAX && *endptr == '\0') {
- /* CNO..CNO */
- *start = cno;
- *end = cno2;
- return 0;
- }
- }
- }
- return -1; /* parse error */
-}
diff --git a/lib/parser.c b/lib/parser.c
new file mode 100644
index 0000000..44153dc
--- /dev/null
+++ b/lib/parser.c
@@ -0,0 +1,154 @@
+/*
+ * parser.c - NILFS parser library
+ *
+ * Copyright (C) 2009-2012 Nippon Telegraph and Telephone Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Written by Ryusuke Konishi <konishi.ryusuke-***@public.gmane.org>.
+ */
+
+#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_STRING_H
+#include <string.h>
+#endif /* HAVE_STRING_H */
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_LIMITS_H */
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include "nilfs.h"
+
+nilfs_cno_t nilfs_parse_cno(const char *arg, char **endptr, int base)
+{
+ /* ensure the number we are about to parse is not negative, which
+ * strtoull() will happily accept and cast to an unsigned value. */
+ while (isspace(*arg))
+ arg++;
+ if (*arg == '-')
+ return NILFS_CNO_MAX;
+
+ return strtoull(arg, endptr, base);
+}
+
+int nilfs_parse_cno_range(const char *arg, __u64 *start, __u64 *end, int base)
+{
+ const char *delim;
+ char *endptr;
+ nilfs_cno_t cno, cno2;
+
+ assert(arg && *arg != '\0');
+
+ delim = strstr(arg, "..");
+ if (delim && delim == arg) {
+ if (arg[2] != '\0') {
+ /* ..yyy */
+ cno = nilfs_parse_cno(arg + 2, &endptr, base);
+ if (cno < NILFS_CNO_MAX && *endptr == '\0') {
+ /* ..CNO */
+ *start = NILFS_CNO_MIN;
+ *end = cno;
+ return 0;
+ }
+ }
+ } else if (!delim) {
+ /* xxx */
+ cno = nilfs_parse_cno(arg, &endptr, base);
+ if (cno < NILFS_CNO_MAX && *endptr == '\0') {
+ /* CNO */
+ *start = *end = cno;
+ return 0;
+ }
+ } else {
+ /* xxx..yyy */
+ cno = nilfs_parse_cno(arg, &endptr, base);
+ if (cno < NILFS_CNO_MAX && endptr == delim) {
+ if (delim[2] == '\0') {
+ /* CNO.. */
+ *start = cno;
+ *end = NILFS_CNO_MAX;
+ return 0;
+ }
+ cno2 = nilfs_parse_cno(delim + 2, &endptr, base);
+ if (cno2 < NILFS_CNO_MAX && *endptr == '\0') {
+ /* CNO..CNO */
+ *start = cno;
+ *end = cno2;
+ return 0;
+ }
+ }
+ }
+ return -1; /* parse error */
+}
+
+int nilfs_parse_protection_period(const char *arg, unsigned long *period)
+{
+ unsigned long long val;
+ char *endptr;
+ int ret = 0;
+
+ val = strtoull(arg, &endptr, 10);
+ if (endptr == arg) {
+ errno = EINVAL;
+ ret = -1;
+ goto out;
+ } else if (endptr[0] != '\0' && endptr[1] == '\0' && val < ULONG_MAX) {
+ switch (endptr[0]) {
+ case 's':
+ break;
+ case 'm':
+ val *= 60;
+ break;
+ case 'h':
+ val *= 3600;
+ break;
+ case 'd':
+ val *= 86400;
+ break;
+ case 'w':
+ val *= 604800;
+ break;
+ case 'M':
+ val *= 2678400;
+ break;
+ case 'Y':
+ val *= 31536000;
+ break;
+ default:
+ ret = -1;
+ goto out;
+ }
+ }
+ if (val >= ULONG_MAX) {
+ errno = ERANGE;
+ ret = -1;
+ goto out;
+ }
+ *period = val;
+out:
+ return ret;
+}
diff --git a/sbin/nilfs-clean/Makefile.am b/sbin/nilfs-clean/Makefile.am
index 930464b..05b0510 100644
--- a/sbin/nilfs-clean/Makefile.am
+++ b/sbin/nilfs-clean/Makefile.am
@@ -3,7 +3,8 @@
AM_CFLAGS = -Wall
AM_CPPFLAGS = -I$(top_srcdir)/include
LDADD = $(top_builddir)/lib/libnilfs.la \
- $(top_builddir)/lib/libnilfscleaner.la
+ $(top_builddir)/lib/libnilfscleaner.la \
+ $(top_builddir)/lib/libparser.la

sbin_PROGRAMS = nilfs-clean

diff --git a/sbin/nilfs-clean/nilfs-clean.c b/sbin/nilfs-clean/nilfs-clean.c
index 0f4a02c..4a92abf 100644
--- a/sbin/nilfs-clean/nilfs-clean.c
+++ b/sbin/nilfs-clean/nilfs-clean.c
@@ -63,6 +63,7 @@
#include "nls.h"
#include "nilfs.h"
#include "nilfs_cleaner.h"
+#include "parser.h"

#ifdef _GNU_SOURCE
#include <getopt.h>
@@ -336,56 +337,6 @@ static void nilfs_clean_usage(void)
fprintf(stderr, NILFS_CLEAN_USAGE, progname);
}

-static int nilfs_clean_parse_protection_period(const char *arg)
-{
- unsigned long long period;
- char *endptr;
- int ret = 0;
-
- period = strtoull(arg, &endptr, 10);
- if (endptr == arg) {
- myprintf(_("Error: invalid protection period: %s\n"), arg);
- ret = -1;
- goto out;
- } else if (endptr[0] != '\0' && endptr[1] == '\0' &&
- period < ULONG_MAX) {
- switch (endptr[0]) {
- case 's':
- break;
- case 'm':
- period *= 60;
- break;
- case 'h':
- period *= 3600;
- break;
- case 'd':
- period *= 86400;
- break;
- case 'w':
- period *= 604800;
- break;
- case 'M':
- period *= 2678400;
- break;
- case 'Y':
- period *= 31536000;
- break;
- default:
- ret = -1;
- goto out;
- }
- }
- if (period >= ULONG_MAX) {
- myprintf(_("Error: too large period: %s\n"), arg);
- errno = ERANGE;
- ret = -1;
- goto out;
- }
- protection_period = period;
-out:
- return ret;
-}
-
static int nilfs_clean_parse_gcspeed(const char *arg)
{
unsigned long nsegs;
@@ -473,7 +424,7 @@ static void nilfs_clean_parse_options(int argc, char *argv[])
#ifdef _GNU_SOURCE
int option_index;
#endif /* _GNU_SOURCE */
- int c;
+ int c, ret;

#ifdef _GNU_SOURCE
while ((c = getopt_long(argc, argv, "bc::hlm:p:qrsS:vV",
@@ -502,9 +453,19 @@ static void nilfs_clean_parse_options(int argc, char *argv[])
exit(EXIT_FAILURE);
break;
case 'p':
- if (nilfs_clean_parse_protection_period(optarg) < 0)
- exit(EXIT_FAILURE);
- break;
+ ret = nilfs_parse_protection_period(
+ optarg, &protection_period);
+ if (!ret)
+ break;
+
+ if (errno == ERANGE) {
+ myprintf(_("Error: too large period: %s\n"),
+ optarg);
+ } else {
+ myprintf(_("Error: invalid protection period: "
+ "%s\n"), optarg);
+ }
+ exit(EXIT_FAILURE);
case 'q':
clean_cmd = NILFS_CLEAN_CMD_SHUTDOWN;
break;
--
1.7.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to majordomo-***@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Loading...