package/base-files: caldata: work around dd's limitation
authorThibaut VARÈNE <hacks@slashdirt.org>
Sat, 16 May 2020 20:18:15 +0000 (22:18 +0200)
committerKoen Vandeputte <koen.vandeputte@ncentric.com>
Thu, 28 May 2020 09:22:22 +0000 (11:22 +0200)
tl;dr: dd will silently truncate the output if reading from special
files (e.g. sysfs attributes) with a too large bs parameter.

This problem was exposed on some RouterBOARD ipq40xx devices which use a
caldata payload which is larger than PAGE_SIZE, contrary to all other
currently supported RouterBOARD devices: the caldata would fail to
properly load with the current scripts.

Background: dd doesn't seem to correctly handle read() results that
return less than requested data. sysfs attributes have a kernel exchange
buffer which is at most PAGE_SIZE big, so only 1 page can be read() at a
time. In this case, if bs is larger than PAGE_SIZE, dd will silently
truncate blocks to PAGE_SIZE. With the current scripts using bs=<size>
count=1, the data is truncated to PAGE_SIZE as soon as the requested
<size> exceeds this value.

This commit works around this problem by using `cat` in the caldata
routines that can read from a file (routines that read from mtd devices
are untouched). cat correctly handles partial read requests. The output
is then piped to dd with the same parameters as before, to ensure that
the resulting file remains exactly the same.

This is a simple workaround, the downside is that it uses a pipe and one
more executable, and therefore has a larger memory footprint and is
slower. This is deemed acceptable considering these routines are only
used at boot time.

Tested-by: Robert Marko <robimarko@gmail.com>
Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
package/base-files/Makefile
package/base-files/files/lib/functions/caldata.sh

index 5fb275533d0c2659b23cb3cf28600e33ae90703e..5bf783d0e8604d05b59413420e268397fd86f315 100644 (file)
@@ -12,7 +12,7 @@ include $(INCLUDE_DIR)/version.mk
 include $(INCLUDE_DIR)/feeds.mk
 
 PKG_NAME:=base-files
-PKG_RELEASE:=221
+PKG_RELEASE:=222
 PKG_FLAGS:=nonshared
 
 PKG_FILE_DEPENDS:=$(PLATFORM_DIR)/ $(GENERIC_PLATFORM_DIR)/base-files/
index 6862da716436c64f9ff6177dbcb01a8f9959edd7..e22c7d27e69f469bc87021b515d3a20ae749be67 100644 (file)
@@ -64,7 +64,8 @@ caldata_from_file() {
 
        [ -n "$target" ] || target=/lib/firmware/$FIRMWARE
 
-       dd if=$source of=$target iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null || \
+       # dd doesn't handle partial reads from special files: use cat
+       cat $source | dd of=$target iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null || \
                caldata_die "failed to extract calibration data from $source"
 }
 
@@ -73,13 +74,14 @@ caldata_sysfsload_from_file() {
        local offset=$(($2))
        local count=$(($3))
 
+       # dd doesn't handle partial reads from special files: use cat
        # test extract to /dev/null first
-       dd if=$source of=/dev/null iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null || \
+       cat $source | dd of=/dev/null iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null || \
                caldata_die "failed to extract calibration data from $source"
 
        # can't fail now
        echo 1 > /sys/$DEVPATH/loading
-       dd if=$source of=/sys/$DEVPATH/data iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null
+       cat $source | dd of=/sys/$DEVPATH/data iflag=skip_bytes bs=$count skip=$offset count=1 2>/dev/null
        echo 0 > /sys/$DEVPATH/loading
 }