LOOP_SET_STATUS(64) will actually allow some lo_flags to be modified; in
particular, LO_FLAGS_AUTOCLEAR can be set and cleared, whereas
LO_FLAGS_PARTSCAN can be set to request a partition scan. Make this
explicit by updating the UAPI to include the flags that can be
set/cleared using this ioctl.

The implementation can then blindly take over the passed in flags,
and use the previous flags for those flags that can't be set / cleared
using LOOP_SET_STATUS.

Signed-off-by: Martijn Coenen <m...@android.com>
---
 drivers/block/loop.c      | 19 +++++++++++++------
 include/uapi/linux/loop.h | 10 ++++++++--
 2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index f172a64333aa..cfbdd99fdb1a 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1049,9 +1049,7 @@ loop_set_status_from_info(struct loop_device *lo,
        lo->transfer = xfer->transfer;
        lo->ioctl = xfer->ioctl;
 
-       if ((lo->lo_flags & LO_FLAGS_AUTOCLEAR) !=
-            (info->lo_flags & LO_FLAGS_AUTOCLEAR))
-               lo->lo_flags ^= LO_FLAGS_AUTOCLEAR;
+       lo->lo_flags = info->lo_flags;
 
        lo->lo_encrypt_key_size = info->lo_encrypt_key_size;
        lo->lo_init[0] = info->lo_init[0];
@@ -1338,6 +1336,7 @@ loop_set_status(struct loop_device *lo, const struct 
loop_info64 *info)
        int err;
        struct block_device *bdev;
        kuid_t uid = current_uid();
+       int prev_lo_flags;
        bool partscan = false;
        bool size_changed = false;
        loff_t validated_size;
@@ -1381,10 +1380,19 @@ loop_set_status(struct loop_device *lo, const struct 
loop_info64 *info)
                goto out_unfreeze;
        }
 
+       prev_lo_flags = lo->lo_flags;
+
        err = loop_set_status_from_info(lo, info);
        if (err)
                goto out_unfreeze;
 
+       /* Mask out flags that can't be set using LOOP_SET_STATUS. */
+       lo->lo_flags &= ~LOOP_SET_STATUS_SETTABLE_FLAGS;
+       /* For those flags, use the previous values instead */
+       lo->lo_flags |= prev_lo_flags & ~LOOP_SET_STATUS_SETTABLE_FLAGS;
+       /* For flags that can't be cleared, use previous values too */
+       lo->lo_flags |= prev_lo_flags & ~LOOP_SET_STATUS_CLEARABLE_FLAGS;
+
        if (size_changed)
                loop_set_size(lo, validated_size);
 
@@ -1396,9 +1404,8 @@ loop_set_status(struct loop_device *lo, const struct 
loop_info64 *info)
 out_unfreeze:
        blk_mq_unfreeze_queue(lo->lo_queue);
 
-       if (!err && (info->lo_flags & LO_FLAGS_PARTSCAN) &&
-            !(lo->lo_flags & LO_FLAGS_PARTSCAN)) {
-               lo->lo_flags |= LO_FLAGS_PARTSCAN;
+       if (!err && (lo->lo_flags & LO_FLAGS_PARTSCAN) &&
+            !(prev_lo_flags & LO_FLAGS_PARTSCAN)) {
                lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN;
                bdev = lo->lo_device;
                partscan = true;
diff --git a/include/uapi/linux/loop.h b/include/uapi/linux/loop.h
index 080a8df134ef..6b32fee80ce0 100644
--- a/include/uapi/linux/loop.h
+++ b/include/uapi/linux/loop.h
@@ -25,6 +25,12 @@ enum {
        LO_FLAGS_DIRECT_IO      = 16,
 };
 
+/* LO_FLAGS that can be set using LOOP_SET_STATUS(64) */
+#define LOOP_SET_STATUS_SETTABLE_FLAGS (LO_FLAGS_AUTOCLEAR | LO_FLAGS_PARTSCAN)
+
+/* LO_FLAGS that can be cleared using LOOP_SET_STATUS(64) */
+#define LOOP_SET_STATUS_CLEARABLE_FLAGS (LO_FLAGS_AUTOCLEAR)
+
 #include <asm/posix_types.h>   /* for __kernel_old_dev_t */
 #include <linux/types.h>       /* for __u64 */
 
@@ -37,7 +43,7 @@ struct loop_info {
        int                lo_offset;
        int                lo_encrypt_type;
        int                lo_encrypt_key_size;         /* ioctl w/o */
-       int                lo_flags;                    /* ioctl r/o */
+       int                lo_flags;
        char               lo_name[LO_NAME_SIZE];
        unsigned char      lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
        unsigned long      lo_init[2];
@@ -53,7 +59,7 @@ struct loop_info64 {
        __u32              lo_number;                   /* ioctl r/o */
        __u32              lo_encrypt_type;
        __u32              lo_encrypt_key_size;         /* ioctl w/o */
-       __u32              lo_flags;                    /* ioctl r/o */
+       __u32              lo_flags;
        __u8               lo_file_name[LO_NAME_SIZE];
        __u8               lo_crypt_name[LO_NAME_SIZE];
        __u8               lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
-- 
2.26.2.303.gf8c07b1a785-goog

Reply via email to