]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
fstrim: add --listed-in <file[:file ..]>
authorKarel Zak <kzak@redhat.com>
Fri, 22 May 2020 12:39:31 +0000 (14:39 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 22 May 2020 12:48:31 +0000 (14:48 +0200)
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 <kzak@redhat.com>
bash-completion/fstrim
sys-utils/fstrim.8
sys-utils/fstrim.c
sys-utils/fstrim.service.in

index 075c0817c63b92fb4e0ed7421c08aeb0f97575ea..4465a92e715b8ec9919b77128467852faa9b6294 100644 (file)
@@ -17,6 +17,7 @@ _fstrim_module()
                -*)
                        OPTS="--all
                                --fstab
+                               --listed-in
                                --quiet-unsupported
                                --offset
                                --length
index f8e4ebc6ff557cf41591f96f3b8b49fdaf7370ee..52199eb1ead3e5e1c6a5418443c2af61c9b61a58 100644 (file)
@@ -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
index de1ef52e87eeef69ca3ef9a8f9134d6efbe86a59..fef33b044f0328bf75231fff677d2b3b85813fc0 100644 (file)
 #include <linux/fs.h>
 
 #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 <libmount.h>
 
@@ -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 <list>   trim filesystems listed in specified files\n"), out);
        fputs(_(" -o, --offset <num>       the offset in bytes to start discarding from\n"), out);
        fputs(_(" -l, --length <num>       the number of bytes to discard\n"), out);
        fputs(_(" -m, --minimum <num>      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;
index 38472d99379ec55bf2c317ebabfa812c5d31e877..11e6e95c71c5ff4632c6e17c0d7eae06a5708876 100644 (file)
@@ -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