Skip to contentSkip to conversation

Btrfs-progs 4.8, fix build on 32bit, ioctl cleanups

PATCH New David Sterba 1 participant 5 messages Oct 6, 2016

Subsystems (2)
lore
Share & tools
lore

David Sterba

author maintainer
#0 lore raw

cover Btrfs-progs 4.8, fix build on 32bit, ioctl cleanups

So this series should fix the 32bit build problems, tested on gcc 6.2.1. I've added test of checking the ioctl numbers: "make test-ioctl" that will build 32bit and 64bit versions (needs compiler for both).

The ioctl compatibility reasoning is in the changelogs. The goal is to keep everything working (send, receive), so there's no visible change, but more testing would be welcome of course.

The patchset is now in devel, on top of 4.8.

David Sterba (4):
btrfs-progs: ioctl: pack structures
btrfs-progs: ioctl: add 32bit compat for SET_RECEIVED_SUBVOL
btrfs-progs: ioctl: add 64bit compat for SEND
btrfs-progs: tests: make the ioctl-test actually useful

 Makefile.in  |  27 +++++-
 ioctl-test.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
 ioctl.h      |  79 ++++++++++++++++--
 3 files changed, 335 insertions(+), 37 deletions(-)
Signature · 1 line
2.10.0
author maintainer
#1 lore raw

PATCH 1/4 btrfs-progs: ioctl: pack structures

Add the packed attribute to several structures so we avoid any accidental size changes due to compiler-dependent padding.

Expected alignment is 8 bytes, padded where needed.

---
 ioctl.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
ioctl.h +5 -5
index a7235c00c1a0..d0c06657f0c0 100644
@@ -149,7 +149,7 @@ struct btrfs_ioctl_scrub_args {
 	struct btrfs_scrub_progress progress;	/* out */
 	/* pad to 1k */
 	__u64 unused[(1024-32-sizeof(struct btrfs_scrub_progress))/8];
-};
+} __attribute__((packed));
 BUILD_ASSERT(sizeof(struct btrfs_ioctl_scrub_args) == 1024);
 
 #define BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS	0
@@ -160,7 +160,7 @@ struct btrfs_ioctl_dev_replace_start_params {
 						 * above */
 	__u8 srcdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1];	/* in */
 	__u8 tgtdev_name[BTRFS_DEVICE_PATH_NAME_MAX + 1];	/* in */
-};
+} __attribute__((packed, aligned (8)));
 BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_start_params) == 2072);
 
 #define BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED	0
@@ -175,7 +175,7 @@ struct btrfs_ioctl_dev_replace_status_params {
 	__u64 time_stopped;	/* out, seconds since 1-Jan-1970 */
 	__u64 num_write_errors;	/* out */
 	__u64 num_uncorrectable_read_errors;	/* out */
-};
+} __attribute__((packed));
 BUILD_ASSERT(sizeof(struct btrfs_ioctl_dev_replace_status_params) == 48);
 
 #define BTRFS_IOCTL_DEV_REPLACE_CMD_START			0
@@ -556,7 +556,7 @@ BUILD_ASSERT(sizeof(struct btrfs_ioctl_qgroup_create_args) == 16);
 struct btrfs_ioctl_timespec {
 	__u64 sec;
 	__u32 nsec;
-};
+} __attribute__((packed, aligned (8)));
 
 struct btrfs_ioctl_received_subvol_args {
 	char	uuid[BTRFS_UUID_SIZE];	/* in */
@@ -601,7 +601,7 @@ struct btrfs_ioctl_send_args {
 	__u64 parent_root;		/* in */
 	__u64 flags;			/* in */
 	__u64 reserved[4];		/* in */
-};
+} __attribute__((packed));
 /*
  * Size of structure depends on pointer width, was not caught.  Kernel handles
  * pointer width differences transparently
Signature · 1 line
2.10.0
author maintainer
#2 lore raw

PATCH 2/4 btrfs-progs: ioctl: add 32bit compat for SET_RECEIVED_SUBVOL

In reply to David Sterba

The ioctl value of SET_RECEIVED_SUBVOL will be different on 32bit userspace and 64bit kernel. This is transparently handled on the kernel side. We now define the same structure so we can verify the ioctl value.

The defined structure and ioctl should not be used. It is exists only for testing purposes, documenting the mess and as a warning for the future.

---
 ioctl.h | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
ioctl.h +38 -0
index d0c06657f0c0..6baa51ab1724 100644
@@ -570,6 +570,38 @@ struct btrfs_ioctl_received_subvol_args {
 BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args) == 200);
 
 /*
+ * If we have a 32-bit userspace and 64-bit kernel, then the UAPI
+ * structures are incorrect, as the timespec structure from userspace
+ * is 4 bytes too small. We define these alternatives here for backward
+ * compatibility, the kernel understands both values.
+ */
+
+/*
+ * Structure size is different on 32bit and 64bit, has some padding if the
+ * structure is embedded. Packing makes sure the size is same on both, but will
+ * be misaligned on 64bit.
+ *
+ * NOTE: do not use in your code, this is for testing only
+ */
+struct btrfs_ioctl_timespec_32 {
+	__u64 sec;
+	__u32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_ioctl_received_subvol_args_32 {
+	char	uuid[BTRFS_UUID_SIZE];	/* in */
+	__u64	stransid;		/* in */
+	__u64	rtransid;		/* out */
+	struct btrfs_ioctl_timespec_32 stime; /* in */
+	struct btrfs_ioctl_timespec_32 rtime; /* out */
+	__u64	flags;			/* in */
+	__u64	reserved[16];		/* in */
+} __attribute__ ((__packed__));
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_received_subvol_args_32) == 192);
+
+#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED 1
+
+/*
  * Caller doesn't want file data in the send stream, even if the
  * search of clone sources doesn't find an extent. UPDATE_EXTENT
  * commands will be sent instead of WRITE commands.
@@ -723,6 +755,12 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
 					struct btrfs_ioctl_logical_ino_args)
 #define BTRFS_IOC_SET_RECEIVED_SUBVOL _IOWR(BTRFS_IOCTL_MAGIC, 37, \
 				struct btrfs_ioctl_received_subvol_args)
+
+#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32_COMPAT_DEFINED
+#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \
+				struct btrfs_ioctl_received_subvol_args_32)
+#endif
+
 #define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
 #define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
 				     struct btrfs_ioctl_vol_args)
Signature · 1 line
2.10.0
author maintainer
#3 lore raw

PATCH 3/4 btrfs-progs: ioctl: add 64bit compat for SEND

In reply to David Sterba

The ioctl value of SEND will be different on 32bit userspace and 64bit kernel due to different pointer type width, that unfortunatelly made it into the structure definition.

To maintain backward compatibility, we must do it in the 64bit->32bit way, because we don't have the kernel side workardound like SET_RECEIVED_SUBVOL has. Changing value of SEND would then break existing users of the raw ioctl.

The compatibility structure and ioctl should not be used, exists for documentation, and testing.

---
 ioctl.h | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)
ioctl.h +29 -2
index 6baa51ab1724..f7233da90637 100644
@@ -635,8 +635,8 @@ struct btrfs_ioctl_send_args {
 	__u64 reserved[4];		/* in */
 } __attribute__((packed));
 /*
- * Size of structure depends on pointer width, was not caught.  Kernel handles
- * pointer width differences transparently
+ * Size of structure depends on pointer width, was not caught in the early
+ * days.  Kernel handles pointer width differences transparently.
  */
 BUILD_ASSERT(sizeof(__u64 *) == 8
 	     ? sizeof(struct btrfs_ioctl_send_args) == 72
@@ -644,6 +644,28 @@ BUILD_ASSERT(sizeof(__u64 *) == 8
 		? sizeof(struct btrfs_ioctl_send_args) == 68
 		: 0));
 
+/*
+ * Different pointer width leads to structure size change. Kernel should accept
+ * both ioctl values (derived from the structures) for backward compatibility.
+ * Size of this structure is same on 32bit and 64bit though.
+ *
+ * NOTE: do not use in your code, this is for testing only
+ */
+struct btrfs_ioctl_send_args_64 {
+	__s64 send_fd;			/* in */
+	__u64 clone_sources_count;	/* in */
+	union {
+		__u64 __user *clone_sources;	/* in */
+		__u64 __clone_sources_alignment;
+	};
+	__u64 parent_root;		/* in */
+	__u64 flags;			/* in */
+	__u64 reserved[4];		/* in */
+} __attribute__((packed));
+BUILD_ASSERT(sizeof(struct btrfs_ioctl_send_args_64) == 72);
+
+#define BTRFS_IOC_SEND_64_COMPAT_DEFINED 1
+
 /* Error codes as returned by the kernel */
 enum btrfs_err_code {
 	notused,
@@ -761,6 +783,11 @@ static inline char *btrfs_err_str(enum btrfs_err_code err_code)
 				struct btrfs_ioctl_received_subvol_args_32)
 #endif
 
+#ifdef BTRFS_IOC_SEND_64_COMPAT_DEFINED
+#define BTRFS_IOC_SEND_64 _IOW(BTRFS_IOCTL_MAGIC, 38, \
+		struct btrfs_ioctl_send_args_64)
+#endif
+
 #define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
 #define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \
 				     struct btrfs_ioctl_vol_args)
Signature · 1 line
2.10.0
author maintainer
#4 lore raw

PATCH 4/4 btrfs-progs: tests: make the ioctl-test actually useful

In reply to David Sterba

Enhance the test to verify ioctl uniqueness, compare the defined values against the hardcoded expected values, and take care of the 32bit/64bit compatibility workarounds.

---
 Makefile.in  |  27 +++++-
 ioctl-test.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 263 insertions(+), 30 deletions(-)
Makefile.in +24 -3
index 983b8b9a6193..48ff54ca60f2 100644
@@ -387,9 +387,30 @@ quick-test: $(objects) $(libs) quick-test.o
 	@echo "    [LD]     $@"
 	$(Q)$(CC) $(CFLAGS) -o quick-test $(objects) $(libs) quick-test.o $(LDFLAGS) $(LIBS)
 
-ioctl-test: $(objects) $(libs) ioctl-test.o
-	@echo "    [LD]     $@"
-	$(Q)$(CC) $(CFLAGS) -o ioctl-test $(objects) $(libs) ioctl-test.o $(LDFLAGS) $(LIBS)
+ioctl-test-64.o: ioctl-test.c ioctl.h kerncompat.h ctree.h
+	@echo "    [CC64]   $@"
+	$(Q)$(CC) $(CFLAGS) -m64 -c $< -o $@
+
+ioctl-test-32.o: ioctl-test.c ioctl.h kerncompat.h ctree.h
+	@echo "    [CC32]   $@"
+	$(Q)$(CC) $(CFLAGS) -m32 -c $< -o $@
+
+ioctl-test-32: ioctl-test-32.o
+	@echo "    [LD32]   $@"
+	$(Q)$(CC) $(CFLAGS) -m32 -o $@ $< $(LDFLAGS)
+	@echo "   ?[PAHOLE] $@"
+	-$(Q)pahole $@ > [email protected]
+
+ioctl-test-64: ioctl-test-64.o
+	@echo "    [LD64]   $@"
+	$(Q)$(CC) $(CFLAGS) -m64 -o $@ $< $(LDFLAGS)
+	@echo "   ?[PAHOLE] $@"
+	-$(Q)pahole $@ > [email protected]
+
+test-ioctl: ioctl-test-32 ioctl-test-64
+	@echo "    [TEST/ioctl]"
+	@$(Q)./ioctl-test-32 > ioctl-test-32.log
+	@$(Q)./ioctl-test-64 > ioctl-test-64.log
 
 send-test: $(objects) $(libs) send-test.o
 	@echo "    [LD]     $@"
ioctl-test.c +239 -27

276 lines not on this page · raw message

Signature · 1 line
2.10.0