diff options
-rw-r--r-- | drivers/block/loop/loop_main.c | 130 | ||||
-rw-r--r-- | include/uapi/linux/loop.h | 5 |
2 files changed, 123 insertions, 12 deletions
diff --git a/drivers/block/loop/loop_main.c b/drivers/block/loop/loop_main.c index 1150956974fb..f37ed6e51a45 100644 --- a/drivers/block/loop/loop_main.c +++ b/drivers/block/loop/loop_main.c @@ -1146,9 +1146,22 @@ loop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg) { struct loop_info info; struct loop_info64 info64; + int err; - if (copy_from_user(&info, arg, sizeof (struct loop_info))) + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) return -EFAULT; + + if (info.lo_flags & LO_FLAGS_FILE_FMT) { + /* copy everything from the user space */ + err = copy_from_user(&info, arg, sizeof(info)); + if (err) + return -EFAULT; + } + loop_info64_from_old(&info, &info64); return loop_set_status(lo, &info64); } @@ -1157,9 +1170,22 @@ static int loop_set_status64(struct loop_device *lo, const struct loop_info64 __user *arg) { struct loop_info64 info64; + int err; - if (copy_from_user(&info64, arg, sizeof (struct loop_info64))) + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info64, arg, + sizeof(info64) - sizeof(info64.lo_file_fmt_type)); + if (err) return -EFAULT; + + if (info64.lo_flags & LO_FLAGS_FILE_FMT) { + /* copy everything from the user space */ + err = copy_from_user(&info64, arg, sizeof(info64)); + if (err) + return -EFAULT; + } + return loop_set_status(lo, &info64); } @@ -1167,15 +1193,37 @@ static int loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) { struct loop_info info; struct loop_info64 info64; + int lo_flags; int err; if (!arg) return -EINVAL; + + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) + return -EFAULT; + + lo_flags = info.lo_flags; + err = loop_get_status(lo, &info64); if (!err) err = loop_info64_to_old(&info64, &info); - if (!err && copy_to_user(arg, &info, sizeof(info))) - err = -EFAULT; + + if (lo_flags & LO_FLAGS_FILE_FMT) { + /* copy entire structure to user space because file format + * support is available */ + err = copy_to_user(arg, &info, sizeof(info)); + } else { + /* copy normal structure to user space */ + err = copy_to_user(arg, &info, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + } + + if (err) + return -EFAULT; return err; } @@ -1183,13 +1231,37 @@ loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) { static int loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) { struct loop_info64 info64; + u32 lo_flags; int err; if (!arg) return -EINVAL; + + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info64, arg, + sizeof(info64) - sizeof(info64.lo_file_fmt_type)); + if (err) + return -EFAULT; + + lo_flags = info64.lo_flags; + err = loop_get_status(lo, &info64); - if (!err && copy_to_user(arg, &info64, sizeof(info64))) - err = -EFAULT; + if (err) + return -EFAULT; + + if (lo_flags & LO_FLAGS_FILE_FMT) { + /* copy entire structure to user space because file format + * support is available */ + err = copy_to_user(arg, &info64, sizeof(info64)); + } else { + /* copy normal structure to user space */ + err = copy_to_user(arg, &info64, + sizeof(info64) - sizeof(info64.lo_file_fmt_type)); + } + + if (err) + return -EFAULT; return err; } @@ -1337,7 +1409,8 @@ struct compat_loop_info { unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ compat_ulong_t lo_init[2]; char reserved[4]; -}; + compat_int_t lo_file_fmt_type; +} __attribute__((packed)); /* * Transfer 32-bit compatibility structure in userspace to 64-bit loop info @@ -1348,10 +1421,22 @@ loop_info64_from_compat(const struct compat_loop_info __user *arg, struct loop_info64 *info64) { struct compat_loop_info info; + int err; - if (copy_from_user(&info, arg, sizeof(info))) + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) return -EFAULT; + if (info.lo_flags & LO_FLAGS_FILE_FMT) { + /* copy everything from the user space */ + err = copy_from_user(&info, arg, sizeof(info)); + if (err) + return -EFAULT; + } + memset(info64, 0, sizeof(*info64)); info64->lo_number = info.lo_number; info64->lo_device = info.lo_device; @@ -1364,6 +1449,7 @@ loop_info64_from_compat(const struct compat_loop_info __user *arg, info64->lo_flags = info.lo_flags; info64->lo_init[0] = info.lo_init[0]; info64->lo_init[1] = info.lo_init[1]; + info64->lo_file_fmt_type = info.lo_file_fmt_type; if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE); else @@ -1381,6 +1467,17 @@ loop_info64_to_compat(const struct loop_info64 *info64, struct compat_loop_info __user *arg) { struct compat_loop_info info; + compat_int_t lo_flags; + int err; + + /* backward compatibility: copy everything except the file format type + * field */ + err = copy_from_user(&info, arg, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + if (err) + return -EFAULT; + + lo_flags = info.lo_flags; memset(&info, 0, sizeof(info)); info.lo_number = info64->lo_number; @@ -1393,6 +1490,7 @@ loop_info64_to_compat(const struct loop_info64 *info64, info.lo_flags = info64->lo_flags; info.lo_init[0] = info64->lo_init[0]; info.lo_init[1] = info64->lo_init[1]; + info.lo_file_fmt_type = info64->lo_file_fmt_type; if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI) memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE); else @@ -1405,11 +1503,23 @@ loop_info64_to_compat(const struct loop_info64 *info64, info.lo_inode != info64->lo_inode || info.lo_offset != info64->lo_offset || info.lo_init[0] != info64->lo_init[0] || - info.lo_init[1] != info64->lo_init[1]) + info.lo_init[1] != info64->lo_init[1] || + info.lo_file_fmt_type != info64->lo_file_fmt_type) return -EOVERFLOW; - if (copy_to_user(arg, &info, sizeof(info))) + if (lo_flags & LO_FLAGS_FILE_FMT) { + /* copy entire structure to user space because file format + * support is available */ + err = copy_to_user(arg, &info, sizeof(info)); + } else { + /* copy normal structure to user space */ + err = copy_to_user(arg, &info, + sizeof(info) - sizeof(info.lo_file_fmt_type)); + } + + if (err) return -EFAULT; + return 0; } diff --git a/include/uapi/linux/loop.h b/include/uapi/linux/loop.h index f2cf1ff2ec1b..07a9db82d2bd 100644 --- a/include/uapi/linux/loop.h +++ b/include/uapi/linux/loop.h @@ -23,6 +23,7 @@ enum { LO_FLAGS_AUTOCLEAR = 4, LO_FLAGS_PARTSCAN = 8, LO_FLAGS_DIRECT_IO = 16, + LO_FLAGS_FILE_FMT = 32 /* indicates file format support */ }; #include <asm/posix_types.h> /* for __kernel_old_dev_t */ @@ -60,7 +61,7 @@ struct loop_info64 { __u8 lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ __u64 lo_init[2]; __u32 lo_file_fmt_type; -}; +} __attribute__((packed)); /* * Loop filter types @@ -85,7 +86,7 @@ struct loop_info64 { #define LO_FILE_FMT_QCOW 1 #define LO_FILE_FMT_VDI 2 #define LO_FILE_FMT_VMDK 3 -#define MAX_LO_FILE_FMT 5 +#define MAX_LO_FILE_FMT LO_FILE_FMT_VMDK + 1 /* * IOCTL commands --- we will commandeer 0x4C ('L') |