]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
losetup: Add support for logical block size
authorStanislav Brabec <sbrabec@suse.cz>
Tue, 26 Sep 2017 14:14:51 +0000 (16:14 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 27 Sep 2017 12:30:02 +0000 (14:30 +0200)
Kernel since 4.14 supports setting of logical block size[1]. It allows to
create loop devices that report logical block size different from 512.

Add support for this feature to losetup.

References:
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/block/loop.c?id=89e4fdecb51cf5535867026274bc97de9480ade5

[kzak@redhat.com: - fix loopcxt_get_blocksize()
                  - remove lo_blocksize from loop_info64]

Signed-off-by: Stanislav Brabec <sbrabec@suse.cz>
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Hannes Reinecke <hare@suse.com>
Cc: Omar Sandoval <osandov@fb.com>
Cc: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Karel Zak <kzak@redhat.com>
include/loopdev.h
lib/loopdev.c
sys-utils/losetup.8
sys-utils/losetup.c

index 953d2db89584d0c74725a4e3bbdc7594b431e67b..121fb846bddf6be2766c755c70eabd51b89bd2aa 100644 (file)
@@ -24,6 +24,7 @@
 /* #define LOOP_CHANGE_FD      0x4C06 */
 #define LOOP_SET_CAPACITY      0x4C07
 #define LOOP_SET_DIRECT_IO     0x4C08
+#define LOOP_SET_BLOCK_SIZE    0x4C09
 
 /* /dev/loop-control interface */
 #ifndef LOOP_CTL_ADD
@@ -173,11 +174,13 @@ int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset);
 int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
 int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
 int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+int loopcxt_set_blocksize(struct loopdev_cxt *lc, uint64_t blocksize);
 
 extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
 extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
 extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
 extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
+extern int loopcxt_get_blocksize(struct loopdev_cxt *lc, uint64_t *blocksize);
 extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
 extern int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type);
 extern const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc);
index 8c653a361daa168b0134918d2dedc6c81627a5b1..66fa4f6694a8b7029c30bb7b962ab2e505f10f63 100644 (file)
@@ -735,6 +735,38 @@ int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset)
        return rc;
 }
 
+/*
+ * @lc: context
+ * @blocksize: returns logical blocksize for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_blocksize(struct loopdev_cxt *lc, uint64_t *blocksize)
+{
+       struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+       int rc = -EINVAL;
+
+       if (sysfs)
+               rc = sysfs_read_u64(sysfs, "queue/logical_block_size", blocksize);
+
+       /* Fallback based on BLKSSZGET ioctl */
+       if (rc) {
+               int fd = loopcxt_get_fd(lc);
+               int sz = 0;
+
+               if (fd < 0)
+                       return -EINVAL;
+               rc = blkdev_get_sector_size(fd, &sz);
+               if (rc)
+                       return rc;
+
+               *blocksize = sz;
+       }
+
+       DBG(CXT, ul_debugobj(lc, "get_blocksize [rc=%d]", rc));
+       return rc;
+}
+
 /*
  * @lc: context
  * @sizelimit: returns size limit for the given device
@@ -1398,6 +1430,24 @@ int loopcxt_set_dio(struct loopdev_cxt *lc, unsigned long use_dio)
        return 0;
 }
 
+int loopcxt_set_blocksize(struct loopdev_cxt *lc, unsigned long blocksize)
+{
+       int fd = loopcxt_get_fd(lc);
+
+       if (fd < 0)
+               return -EINVAL;
+
+       /* Kernels prior to v4.14 don't support this ioctl */
+       if (ioctl(fd, LOOP_SET_BLOCK_SIZE, blocksize) < 0) {
+               int rc = -errno;
+               DBG(CXT, ul_debugobj(lc, "LOOP_SET_BLOCK_SIZE failed: %m"));
+               return rc;
+       }
+
+       DBG(CXT, ul_debugobj(lc, "logical block size set"));
+       return 0;
+}
+
 int loopcxt_delete_device(struct loopdev_cxt *lc)
 {
        int fd = loopcxt_get_fd(lc);
index 98a8e70d6e53bec9508c33433c9fa357009c81e4..570704d725fb6bdf91123ac6e858a2a2661acb2f 100644 (file)
@@ -116,6 +116,9 @@ The data start is moved \fIoffset\fP bytes into the specified file or device.
 .IP "\fB\-\-sizelimit \fIsize\fP"
 The data end is set to no more than \fIsize\fP bytes after the data start.
 .TP
+.BR \-b , " \-\-logical-blocksize " \fIsize
+Set the logical block size of the loop device in bytes (since Linux 4.14).
+.TP
 .BR \-c , " \-\-set\-capacity " \fIloopdev
 Force the loop driver to reread the size of the file associated with the
 specified loop device.
index bbff98389f710405c3ea1673e0c21f1e27b27f5f..368a0b90a7e87ce7f03dea8800facf02ec2c1db0 100644 (file)
@@ -36,6 +36,7 @@ enum {
        A_FIND_FREE,            /* find first unused */
        A_SET_CAPACITY,         /* set device capacity */
        A_SET_DIRECT_IO,        /* set accessing backing file by direct io */
+       A_SET_BLOCKSIZE,        /* set logical block size of the loop device */
 };
 
 enum {
@@ -50,6 +51,7 @@ enum {
        COL_RO,
        COL_SIZELIMIT,
        COL_DIO,
+       COL_BLOCKSIZE,
 };
 
 /* basic output flags */
@@ -76,6 +78,7 @@ static struct colinfo infos[] = {
        [COL_SIZELIMIT]   = { "SIZELIMIT",    5, SCOLS_FL_RIGHT, N_("size limit of the file in bytes")},
        [COL_MAJMIN]      = { "MAJ:MIN",      3, 0, N_("loop device major:minor number")},
        [COL_DIO]         = { "DIO",          1, SCOLS_FL_RIGHT, N_("access backing file with direct-io")},
+       [COL_BLOCKSIZE]   = { "BLOCKSIZE",    4, SCOLS_FL_RIGHT, N_("logical block size in bytes")},
 };
 
 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
@@ -280,6 +283,10 @@ static int set_scols_data(struct loopdev_cxt *lc, struct libscols_line *ln)
                case COL_PARTSCAN:
                        p = loopcxt_is_partscan(lc) ? "1" : "0";
                        break;
+               case COL_BLOCKSIZE:
+                       if (loopcxt_get_blocksize(lc, &x) == 0)
+                               xasprintf(&np, "%jd", x);
+                       break;
                default:
                        return -EINVAL;
                }
@@ -404,6 +411,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(USAGE_SEPARATOR, out);
        fputs(_(" -o, --offset <num>            start at offset <num> into file\n"), out);
        fputs(_("     --sizelimit <num>         device is limited to <num> bytes of the file\n"), out);
+       fputs(_(" -b  --logical-blocksize <num> set the logical block size to <num>\n"), out);
        fputs(_(" -P, --partscan                create a partitioned loop device\n"), out);
        fputs(_(" -r, --read-only               set up a read-only loop device\n"), out);
        fputs(_("     --direct-io[=<on|off>]    open backing file with O_DIRECT\n"), out);
@@ -566,11 +574,11 @@ int main(int argc, char **argv)
        struct loopdev_cxt lc;
        int act = 0, flags = 0, no_overlap = 0, c;
        char *file = NULL;
-       uint64_t offset = 0, sizelimit = 0;
+       uint64_t offset = 0, sizelimit = 0, blocksize = 0;
        int res = 0, showdev = 0, lo_flags = 0;
        char *outarg = NULL;
        int list = 0;
-       unsigned long use_dio = 0, set_dio = 0;
+       unsigned long use_dio = 0, set_dio = 0, set_blocksize = 0;
 
        enum {
                OPT_SIZELIMIT = CHAR_MAX + 1,
@@ -589,6 +597,7 @@ int main(int argc, char **argv)
                { "associated",   required_argument, NULL, 'j'           },
                { "json",         no_argument,       NULL, 'J'           },
                { "list",         no_argument,       NULL, 'l'           },
+               { "logical-blocksize", required_argument, NULL, 'b'      },
                { "noheadings",   no_argument,       NULL, 'n'           },
                { "offset",       required_argument, NULL, 'o'           },
                { "output",       required_argument, NULL, 'O'           },
@@ -620,7 +629,7 @@ int main(int argc, char **argv)
        if (loopcxt_init(&lc, 0))
                err(EXIT_FAILURE, _("failed to initialize loopcxt"));
 
-       while ((c = getopt_long(argc, argv, "ac:d:Dfhj:JlLno:O:PrvV",
+       while ((c = getopt_long(argc, argv, "ab:c:d:Dfhj:JlLno:O:PrvV",
                                longopts, NULL)) != -1) {
 
                err_exclusive_options(c, longopts, excl, excl_st);
@@ -629,6 +638,10 @@ int main(int argc, char **argv)
                case 'a':
                        act = A_SHOW;
                        break;
+               case 'b':
+                       set_blocksize = 1;
+                       blocksize = strtosize_or_err(optarg, _("failed to parse logical block size"));
+                       break;
                case 'c':
                        act = A_SET_CAPACITY;
                        if (!is_loopdev(optarg) ||
@@ -727,6 +740,7 @@ int main(int argc, char **argv)
                columns[ncolumns++] = COL_RO;
                columns[ncolumns++] = COL_BACK_FILE;
                columns[ncolumns++] = COL_DIO;
+               columns[ncolumns++] = COL_BLOCKSIZE;
        }
 
        if (act == A_FIND_FREE && optind < argc) {
@@ -747,12 +761,14 @@ int main(int argc, char **argv)
                /*
                 * losetup [--list] <device>
                 * OR
-                * losetup --direct-io[=off] <device>
+                * losetup {--direct-io[=off]|--logical-blocksize=size}... <device>
                 */
-               if (!set_dio)
+               if (!(set_dio || set_blocksize))
                        act = A_SHOW_ONE;
-               else
+               if (set_dio)
                        act = A_SET_DIRECT_IO;
+               if (set_blocksize)
+                       act = A_SET_BLOCKSIZE;
                if (!is_loopdev(argv[optind]) ||
                    loopcxt_set_device(&lc, argv[optind]))
                        err(EXIT_FAILURE, _("%s: failed to use device"),
@@ -799,8 +815,8 @@ int main(int argc, char **argv)
                        if (showdev)
                                printf("%s\n", loopcxt_get_device(&lc));
                        warn_size(file, sizelimit);
-                       if (set_dio)
-                               goto lo_set_dio;
+                       if (set_dio || set_blocksize)
+                               goto lo_set_post;
                }
                break;
        case A_DELETE:
@@ -853,11 +869,20 @@ int main(int argc, char **argv)
                                loopcxt_get_device(&lc));
                break;
        case A_SET_DIRECT_IO:
- lo_set_dio:
-               res = loopcxt_set_dio(&lc, use_dio);
-               if (res)
-                       warn(_("%s: set direct io failed"),
-                               loopcxt_get_device(&lc));
+       case A_SET_BLOCKSIZE:
+ lo_set_post:
+               if (set_dio) {
+                       res = loopcxt_set_dio(&lc, use_dio);
+                       if (res)
+                               warn(_("%s: set direct io failed"),
+                                       loopcxt_get_device(&lc));
+               }
+               if (set_blocksize) {
+                       res = loopcxt_set_blocksize(&lc, blocksize);
+                       if (res)
+                               warn(_("%s: set logical block size failed"),
+                                       loopcxt_get_device(&lc));
+               }
                break;
        default:
                warnx(_("bad usage"));