Hi all,

the commands "btrfs filesystem show" and "btrfs device scan" look at the /dev
directory (and it subdirectories) for every block devices.
This is a slow process because floppy and cdrom are also checked. Moreover,
as highlighted by Helmut, if udev is not used, the /dev directory is populated
by high number of non-existant devices, which slow the process.

My patch changes the behaviour of these commands. The list of the devices are
extracted from /proc/partitions, and on the basis of the file 
/etc/btrfs.devices some devices may be skipped.

The file /etc/btrfs.devices contains a list of devices which have to be skipped
(if the line starts with '!') or to be evaluated. Shell wildcard may be used.

If the file doesn't exists the default is to skip cdroms (/dev/cdrom*) and 
floppies (/dev/fd*).

To revert to the old behaviour use the switch '--all-devices'.

Comment are welcome.

Regards
G.Baroncelli


 btrfs.c        |   10 +-
 btrfs.devices  |   24 ++++++
 btrfs_cmds.c   |   39 +++++++++--
 man/btrfs.8.in |   22 ++++--
 utils.c        |  196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 utils.h        |    5 +
 6 files changed, 279 insertions(+), 17 deletions(-)



diff --git a/btrfs.c b/btrfs.c
index 46314cf..d850e60 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -83,9 +83,10 @@ static struct Command commands[] = {
                "will occupe all available space on the device."
        },
        { do_show_filesystem, 999,
-         "filesystem show", "[<uuid>|<label>]\n"
+         "filesystem show", "[--all-devices][<uuid>|<label>]\n"
                "Show the info of a btrfs filesystem. If no <uuid> or <label>\n"
-               "is passed, info of all the btrfs filesystem are shown."
+               "is passed, info of all the btrfs filesystem are shown.\n"
+               "If --all-devices is passed, all devices are scanned."
        },
        { do_df_filesystem, 1,
          "filesystem df", "<path>\n"
@@ -96,9 +97,10 @@ static struct Command commands[] = {
                "Balance the chunks across the device."
        },
        { do_scan,
-         999, "device scan", "[<device> [<device>..]\n"
+         999, "device scan", "[--all-devices|<device> [<device>..]\n"
                "Scan all device for or the passed device for a btrfs\n"
-               "filesystem."
+               "filesystem.\n"
+               "If --all-devices is passed, all devices are scanned."
        },
        { do_add_volume, -2,
          "device add", "<dev> [<dev>..] <path>\n"
diff --git a/btrfs.devices b/btrfs.devices
new file mode 100644
index 0000000..ffeb8b6
--- /dev/null
+++ b/btrfs.devices
@@ -0,0 +1,24 @@
+#
+#      This file lists the devices which have to be skipped or not
+#      during the command 'btrfs filesystem show' and 'btrfs device scan'
+#
+#      These lines may contain a shell wildcard pattern (*,?,[]).
+#
+#      If a line starts with "!" and matches a device, the device is skipped
+#      If a line matches a device, the device is evaluated
+#      If a device is not matched by any line, the device is evaluated.
+#
+#      The lines starting with "#" are comments. The lines empty are
+#      ignored 
+#
+
+
+
+# There are two default rules which are added automatically
+#
+# skip floppy
+# !/dev/fd*
+#
+# skip cdrom
+# !/dev/sr*
+#
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index 8031c58..a573008 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -529,11 +529,25 @@ int do_fssync(int argc, char **argv)
 int do_scan(int argc, char **argv)
 {
        int     i, fd;
-       if(argc<=1){
+       int     checklist = 1;
+       int     devstart = 1;
+
+       if( argc >= 2 && !strcmp(argv[1],"--all-devices")){
+
+               if( argc >2 ){
+                       fprintf(stderr, "ERROR: too may arguments\n");
+                        return 22;
+                }
+
+               checklist = 0;
+               devstart += 1;
+       }
+
+       if(argc<=devstart){
                int ret;
 
                printf("Scanning for Btrfs filesystems\n");
-               ret = btrfs_scan_one_dir("/dev", 1);
+               ret = btrfs_scan_block_devices(1, checklist);
                if (ret){
                        fprintf(stderr, "ERROR: error %d while scanning\n", 
ret);
                        return 18;
@@ -547,7 +561,7 @@ int do_scan(int argc, char **argv)
                return 10;
        }
 
-       for( i = 1 ; i < argc ; i++ ){
+       for( i = devstart ; i < argc ; i++ ){
                struct btrfs_ioctl_vol_args args;
                int ret;
 
@@ -666,15 +680,30 @@ int do_show_filesystem(int argc, char **argv)
        struct list_head *all_uuids;
        struct btrfs_fs_devices *fs_devices;
        struct list_head *cur_uuid;
-       char *search = argv[1];
+       char *search=0;
        int ret;
+       int checklist = 1;
+       int searchstart = 1;
 
-       ret = btrfs_scan_one_dir("/dev", 0);
+       if( argc >= 2 && !strcmp(argv[1],"--all-devices")){
+               checklist = 0;
+               searchstart += 1;
+       }
+
+       if( argc > searchstart+1 ){
+               fprintf(stderr, "ERROR: too many arguments\n");
+               return 22;
+       }       
+
+       ret = btrfs_scan_block_devices(0, checklist);
        if (ret){
                fprintf(stderr, "ERROR: error %d while scanning\n", ret);
                return 18;
        }
 
+       if(searchstart < argc)
+               search = argv[searchstart];
+
        all_uuids = btrfs_scanned_uuids();
        list_for_each(cur_uuid, all_uuids) {
                fs_devices = list_entry(cur_uuid, struct btrfs_fs_devices,
diff --git a/man/btrfs.8.in b/man/btrfs.8.in
index 26ef982..77b13f6 100644
--- a/man/btrfs.8.in
+++ b/man/btrfs.8.in
@@ -21,9 +21,9 @@ btrfs \- control a btrfs filesystem
 .PP
 \fBbtrfs\fP \fBfilesystem resize\fP\fI [+/\-]<size>[gkm]|max <filesystem>\fP
 .PP
-\fBbtrfs\fP \fBdevice scan\fP\fI [<device> [<device>..]]\fP
+\fBbtrfs\fP \fBfilesystem show\fP\fI [--all-devices] <uuid>|<label> 
[<uuid>|<label>...]\fP
 .PP
-\fBbtrfs\fP \fBdevice show\fP\fI <dev>|<label> [<dev>|<label>...]\fP
+\fBbtrfs\fP \fBdevice scan\fP\fI [--all-devices][<device> [<device>..]]\fP
 .PP
 \fBbtrfs\fP \fBdevice balance\fP\fI <path> \fP
 .PP
@@ -106,9 +106,10 @@ is returned by the \fBsubvolume list\fR command.
 Defragment files and/or directories.
 .TP
 
-\fBdevice scan\fR \fI[<device> [<device>..]]\fR
+\fBdevice scan\fR \fI[--all-devices][<device> [<device>..]]\fR
 Scan devices for a btrfs filesystem. If no devices are passed, \fBbtrfs\fR 
scans
-all the block devices.
+all the block devices. If --all-devices is passed, the file /etc/btrfs.devices
+are ignored and all devices are scanned.
 .TP
 
 \fBfilesystem sync\fR\fI <path> \fR
@@ -138,9 +139,10 @@ can expand the partition before enlarging the filesystem 
and shrink the
 partition after reducing the size of the filesystem.
 .TP
 
-\fBfilesystem show\fR [<uuid>|<label>]\fR
+\fBfilesystem show\fR [--all-devices][<uuid>|<label>]\fR
 Show the btrfs filesystem with some additional info. If no UUID or label is
-passed, \fBbtrfs\fR show info of all the btrfs filesystem.
+passed, \fBbtrfs\fR show info of all the btrfs filesystem. If --all-devices
+is passed, the file /etc/btrfs.devices are ignored and all devices are scanned.
 .TP
 
 \fBdevice balance\fR \fI<path>\fR
@@ -160,6 +162,14 @@ Remove device(s) from a filesystem identified by 
\fI<path>\fR.
 \fBbtrfs\fR returns a zero exist status if it succeeds. Non zero is returned in
 case of failure.
 
+.SH FILES
+The file \fB/etc/btrfs.devices\fR contains a list of devices which have to be
+skipped (or not) when the commands \fBbtrfs filesystem show\fR and
+\fBbtrfs device scan\fR are executed. If a line starts with '!', and matches
+a device, this device is skipped. If a line matches a device, this device is
+evaluated. If this file doesn't exists, the default rules are to skip floppy
+and cdrom.The shell wildcard may be used.
+
 .SH AVAILABILITY
 .B btrfs
 is part of btrfs-progs. Btrfs filesystem is currently under heavy development,
diff --git a/utils.c b/utils.c
index fd894f3..6112137 100644
--- a/utils.c
+++ b/utils.c
@@ -35,6 +35,8 @@
 #include <linux/major.h>
 #include <linux/kdev_t.h>
 #include <limits.h>
+#include <ctype.h>
+#include <fnmatch.h>
 #include "kerncompat.h"
 #include "radix-tree.h"
 #include "ctree.h"
@@ -833,7 +835,185 @@ void btrfs_register_one_device(char *fname)
        close(fd);
 }
 
-int btrfs_scan_one_dir(char *dirname, int run_ioctl)
+
+static char **device_checklist = 0;
+static int  device_checklist_count = 0;
+/*
+ *     Default device black list:
+ *     If the line starts with a "!" the device has to be skipped
+ *     otherwise the device is OK
+ */
+static char *default_checklist[] = {
+       "!/dev/fd*",
+       "!/dev/sr*",
+       0
+};
+/* add an entry in the checklist */
+static int device_checklist_add(char *entry)
+{
+       char    *cpy;
+       char    **res;
+
+       device_checklist_count += 1;
+       res = realloc(device_checklist, 
+                               sizeof(char*)*device_checklist_count);
+       if( !res ){
+               free(device_checklist);
+               device_checklist_count = 0;
+               return -1;
+       }
+       device_checklist = res;
+
+       cpy = strdup(entry);
+       if( !cpy ){
+                free(device_checklist);
+                device_checklist_count = 0;
+                return -1;
+        }
+
+       device_checklist[device_checklist_count-1] = cpy;
+       return 0;
+}
+
+/* 
+ * init the check list on teh basis of the default check list
+ * and the check list stored in the file "fn"
+ */
+static int init_device_checklist(char *fn)
+{
+
+       char    buf[1024];
+       int     i;
+       FILE    *fp;
+
+       if(device_checklist_count)
+               return 0;
+
+       if( fn == 0 )
+               return 0;       /* no extra rules provided */
+
+       fp = fopen(fn, "r");
+       if(!fp)
+               return 0;       /* the file doesn't exist */
+
+       while(fgets(buf,1023, fp)){
+               char    *p = buf;
+               char    *l;
+               while (isblank(*p)) p++;
+               l = p;
+               while( *l != '\n' && *l != '\r' && *l ) l++;
+               *l = 0;
+
+               if( *p == '#' || *p == 0 )
+                       continue;
+
+               if(device_checklist_add(p)){
+                       fclose(fp);
+                       return -2;
+               }
+       }
+       fclose(fp);
+
+       for(i=0; default_checklist[i] ; i++ )
+               if(device_checklist_add(default_checklist[i]))
+                       return -3;
+
+       
+       return 0;
+}
+
+/* 
+ * This function test if "dev" has to be skipped on the basis of the
+ * checklist; return values:
+ *     0 -> skip the deive
+ *     1 -> teh device is ok
+ */
+static int test_device(char *dev )
+{
+       int     i;
+       for( i = 0 ; i < device_checklist_count ; i++ ){
+               int     res;
+               char    *rule;
+
+               rule = device_checklist[i];
+
+               if( rule[0] == '!' ){
+                       res = 0;        /* if match, skip the device */
+                       rule++;
+               }else{
+                       res = 1;        /* if match, the device is ok */
+               }
+
+               if( !fnmatch(rule, dev, 0 ) )
+                       return res;
+
+       }
+       return 1;
+}
+
+int btrfs_scan_block_devices(int run_ioctl, int checklist)
+{
+
+       struct stat st;
+       int ret;
+       int fd;
+       struct btrfs_fs_devices *tmp_devices;
+       u64 num_devices;
+       FILE *proc_partitions;
+       int i;
+       char buf[1024];
+       char name[100], fullpath[110];
+
+       if(checklist)
+               init_device_checklist(BTRFSDEVICELIST);
+
+       proc_partitions = fopen("/proc/partitions","r");
+       if (!proc_partitions) {
+               fprintf(stderr, "Unable to open '/proc/partitions' for 
scanning\n");
+               return -ENOENT;
+       }
+       /* skip the header */
+       for(i=0; i < 2 ; i++)
+               if(!fgets(buf, 1023, proc_partitions)){
+               fprintf(stderr, "Unable to read '/proc/partitions' for 
scanning\n");
+               fclose(proc_partitions);
+               return -ENOENT;
+       }
+
+       strcpy(fullpath,"/dev/");
+       while(fgets(buf, 1023, proc_partitions)) {
+
+               i = sscanf(buf," %*d %*d %*d %99s", fullpath+5);
+               ret = lstat(fullpath, &st);
+               if (ret < 0) {
+                       fprintf(stderr, "failed to stat %s\n", fullpath);
+                       continue;
+               }
+               if (!S_ISBLK(st.st_mode)) {
+                       continue;
+               }
+               if (checklist && !test_device(fullpath)){
+                       continue;
+               }
+               fd = open(fullpath, O_RDONLY);
+               if (fd < 0) {
+                       fprintf(stderr, "failed to read %s\n", fullpath);
+                       continue;
+               }
+               ret = btrfs_scan_one_device(fd, fullpath, &tmp_devices,
+                                           &num_devices,
+                                           BTRFS_SUPER_INFO_OFFSET);
+               if (ret == 0 && run_ioctl > 0) {
+                       btrfs_register_one_device(fullpath);
+               }
+               close(fd);
+       }
+
+       fclose(proc_partitions);
+       return 0;
+}
+
+int btrfs_scan_one_dir_checklist(char *dirname, int run_ioctl, int checklist)
 {
        DIR *dirp = NULL;
        struct dirent *dirent;
@@ -848,6 +1028,9 @@ int btrfs_scan_one_dir(char *dirname, int run_ioctl)
        struct btrfs_fs_devices *tmp_devices;
        u64 num_devices;
 
+       if(checklist)
+               init_device_checklist(BTRFSDEVICELIST);
+
        INIT_LIST_HEAD(&pending_list);
 
        pending = malloc(sizeof(*pending));
@@ -867,7 +1050,8 @@ again:
        }
        dirp = opendir(dirname);
        if (!dirp) {
-               fprintf(stderr, "Unable to open /sys/block for scanning\n");
+               fprintf(stderr, "Unable to open '%s' for scanning\n", 
+                       dirname);
                return -ENOENT;
        }
        while(1) {
@@ -900,6 +1084,9 @@ again:
                if (!S_ISBLK(st.st_mode)) {
                        continue;
                }
+               if (checklist && !test_device(fullpath)){
+                       continue;
+               }
                fd = open(fullpath, O_RDONLY);
                if (fd < 0) {
                        fprintf(stderr, "failed to read %s\n", fullpath);
@@ -929,6 +1116,11 @@ fail:
        return ret;
 }
 
+int btrfs_scan_one_dir(char *dirname, int run_ioctl)
+{
+       return btrfs_scan_one_dir_checklist(dirname, run_ioctl, 0);
+}
+
 int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
                        int run_ioctls)
 {
diff --git a/utils.h b/utils.h
index 9dce5b0..b815f9b 100644
--- a/utils.h
+++ b/utils.h
@@ -36,8 +36,13 @@ int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, 
u64 
total_devs,
                        int run_ioctls);
 void btrfs_register_one_device(char *fname);
 int btrfs_scan_one_dir(char *dirname, int run_ioctl);
+int btrfs_scan_one_dir_checklist(char *dirname, int run_ioctl, int checklist);
 int check_mounted(const char *devicename);
 int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
                                 int super_offset);
 char *pretty_sizes(u64 size);
+int btrfs_scan_block_devices(int run_ioctl, int checklist);
+
+#define BTRFSDEVICELIST "/etc/btrfs.devices"
+
 #endif


-- 
gpg key@ keyserver.linux.it: Goffredo Baroncelli (ghigo) <kreij...@inwind.it>
Key fingerprint = 4769 7E51 5293 D36C 814E  C054 BF04 F161 3DC5 0512

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to