From: Karel Zak Date: Fri, 22 May 2020 12:39:31 +0000 (+0200) Subject: fstrim: add --listed-in X-Git-Tag: v2.36-rc1~71 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9995da01e0a17f0a381cd691fb9c371cf4a6b4f4;p=thirdparty%2Futil-linux.git fstrim: add --listed-in This new option works like --all but it allows to specify multiple files with filesystems to make fstrim configuration more portable between distributions. For example: fstrim --listed-in /etc/fstab:/proc/self/mountinfo forces fstrim to try fstab and if unsuccessful than try mountinfo. Addresses: https://github.com/karelzak/util-linux/issues/1019 Signed-off-by: Karel Zak --- diff --git a/bash-completion/fstrim b/bash-completion/fstrim index 075c0817c6..4465a92e71 100644 --- a/bash-completion/fstrim +++ b/bash-completion/fstrim @@ -17,6 +17,7 @@ _fstrim_module() -*) OPTS="--all --fstab + --listed-in --quiet-unsupported --offset --length diff --git a/sys-utils/fstrim.8 b/sys-utils/fstrim.8 index f8e4ebc6ff..52199eb1ea 100644 --- a/sys-utils/fstrim.8 +++ b/sys-utils/fstrim.8 @@ -72,6 +72,10 @@ to discard. If the specified value extends past the end of the filesystem, .B fstrim will stop at the filesystem size boundary. The default value extends to the end of the filesystem. +.IP "\fB\-I, \-\-listed\-in\fP \fIlist\fP" +Specifies a colon-separated list of files in fstab or kernel mountinfo +format. All missing or empty files are silently ignored. The evaluation of the +\fIlist\fP stops after first non-empty file. For example: \fB--listed-in /etc/fstab:/proc/self/mountinfo\fR. .IP "\fB\-m, \-\-minimum\fP \fIminimum-size\fP" Minimum contiguous free range to discard, in bytes. (This value is internally rounded up to a multiple of the filesystem block size.) Free ranges smaller diff --git a/sys-utils/fstrim.c b/sys-utils/fstrim.c index de1ef52e87..fef33b044f 100644 --- a/sys-utils/fstrim.c +++ b/sys-utils/fstrim.c @@ -38,11 +38,13 @@ #include #include "nls.h" +#include "xalloc.h" #include "strutils.h" #include "c.h" #include "closestream.h" #include "pathnames.h" #include "sysfs.h" +#include "optutils.h" #include @@ -61,7 +63,6 @@ struct fstrim_control { unsigned int verbose : 1, quiet_unsupp : 1, - fstab : 1, dryrun : 1; }; @@ -228,13 +229,12 @@ static int uniq_fs_source_cmp( } /* - * fstrim --all follows "mount -a" return codes: - * - * 0 = all success + * -1 = tab empty + * 0 = all success * 32 = all failed * 64 = some failed, some success */ -static int fstrim_all(struct fstrim_control *ctl) +static int fstrim_all_from_file(struct fstrim_control *ctl, const char *filename) { struct libmnt_fs *fs; struct libmnt_iter *itr; @@ -242,29 +242,31 @@ static int fstrim_all(struct fstrim_control *ctl) struct libmnt_cache *cache = NULL; struct path_cxt *wholedisk = NULL; int cnt = 0, cnt_err = 0; - const char *filename = _PATH_PROC_MOUNTINFO; - - mnt_init_debug(0); - ul_path_init_debug(); - - if (ctl->fstab) - filename = mnt_get_fstab_path(); + int fstab = 0; tab = mnt_new_table_from_file(filename); if (!tab) err(MNT_EX_FAIL, _("failed to parse %s"), filename); + if (mnt_table_is_empty(tab)) { + mnt_unref_table(tab); + return -1; + } + + if (streq_paths(filename, "/etc/fstab")) + fstab = 1; + /* de-duplicate by mountpoints */ mnt_table_uniq_fs(tab, 0, uniq_fs_target_cmp); - if (ctl->fstab) { + if (fstab) { char *rootdev = NULL; cache = mnt_new_cache(); if (!cache) err(MNT_EX_FAIL, _("failed to initialize libmount cache")); - /* Make sure we trim also root FS on --fstab */ + /* Make sure we trim also root FS on fstab */ if (mnt_table_find_target(tab, "/", MNT_ITER_FORWARD) == NULL && mnt_guess_system_root(0, cache, &rootdev) == 0) { @@ -374,6 +376,36 @@ static int fstrim_all(struct fstrim_control *ctl) return MNT_EX_SUCCESS; } +/* + * fstrim --all follows "mount -a" return codes: + * + * 0 = all success + * 32 = all failed + * 64 = some failed, some success + */ +static int fstrim_all(struct fstrim_control *ctl, const char *tabs) +{ + char *list = xstrdup(tabs); + char *file; + int rc = MNT_EX_FAIL; + + mnt_init_debug(0); + ul_path_init_debug(); + + for (file = strtok(list, ":"); file; file = strtok(NULL, ":")) { + struct stat st; + + if (stat(file, &st) < 0 || !S_ISREG(st.st_mode)) + continue; + + rc = fstrim_all_from_file(ctl, file); + if (rc >= 0) + break; /* stop after first non-empty file */ + } + free(list); + return rc; +} + static void __attribute__((__noreturn__)) usage(void) { FILE *out = stdout; @@ -385,8 +417,9 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_("Discard unused blocks on a mounted filesystem.\n"), out); fputs(USAGE_OPTIONS, out); - fputs(_(" -a, --all trim all supported mounted filesystems\n"), out); - fputs(_(" -A, --fstab trim all supported mounted filesystems from /etc/fstab\n"), out); + fputs(_(" -a, --all trim mounted filesystems\n"), out); + fputs(_(" -A, --fstab trim filesystems from /etc/fstab\n"), out); + fputs(_(" -I, --listed-in trim filesystems listed in specified files\n"), out); fputs(_(" -o, --offset the offset in bytes to start discarding from\n"), out); fputs(_(" -l, --length the number of bytes to discard\n"), out); fputs(_(" -m, --minimum the minimum extent length to discard\n"), out); @@ -407,6 +440,7 @@ static void __attribute__((__noreturn__)) usage(void) int main(int argc, char **argv) { char *path = NULL; + char *tabs = NULL; int c, rc, all = 0; struct fstrim_control ctl = { .range = { .len = ULLONG_MAX } @@ -419,6 +453,7 @@ int main(int argc, char **argv) { "all", no_argument, NULL, 'a' }, { "fstab", no_argument, NULL, 'A' }, { "help", no_argument, NULL, 'h' }, + { "listed-in", required_argument, NULL, 'I' }, { "version", no_argument, NULL, 'V' }, { "offset", required_argument, NULL, 'o' }, { "length", required_argument, NULL, 'l' }, @@ -429,18 +464,33 @@ int main(int argc, char **argv) { NULL, 0, NULL, 0 } }; + static const ul_excl_t excl[] = { /* rows and cols in ASCII order */ + { 'A','I','a' }, + { 0 } + }; + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; + setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); close_stdout_atexit(); - while ((c = getopt_long(argc, argv, "Aahl:m:no:Vv", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "AahI:l:m:no:Vv", longopts, NULL)) != -1) { + + err_exclusive_options(c, longopts, excl, excl_st); + switch(c) { case 'A': - ctl.fstab = 1; - /* fallthrough */ + all = 1; + tabs = _PATH_MNTTAB; /* fstab */ + break; case 'a': all = 1; + tabs = _PATH_PROC_MOUNTINFO; /* mountinfo */ + break; + case 'I': + all = 1; + tabs = optarg; break; case 'n': ctl.dryrun = 1; @@ -484,7 +534,7 @@ int main(int argc, char **argv) } if (all) - return fstrim_all(&ctl); /* MNT_EX_* codes */ + return fstrim_all(&ctl, tabs); /* MNT_EX_* codes */ if (!is_directory(path, 0)) return EXIT_FAILURE; diff --git a/sys-utils/fstrim.service.in b/sys-utils/fstrim.service.in index 38472d9937..11e6e95c71 100644 --- a/sys-utils/fstrim.service.in +++ b/sys-utils/fstrim.service.in @@ -1,12 +1,11 @@ [Unit] Description=Discard unused blocks on filesystems from /etc/fstab Documentation=man:fstrim(8) -ConditionPathExists=/etc/fstab ConditionVirtualization=!container [Service] Type=oneshot -ExecStart=@sbindir@/fstrim --fstab --verbose --quiet-unsupported +ExecStart=@sbindir@/fstrim --listed-in /etc/fstab:/proc/self/mountinfo --verbose --quiet-unsupported PrivateDevices=no PrivateNetwork=yes PrivateUsers=no