This series stops the EFI file protocol Write() from cutting a FAT file
at the end of the written range. It is an RFC, mainly for the naming and
for the shape of the new interface.

The problem
-----------

fs_write() with an offset trims the file at offset + len. This was chosen
on purpose in commit cb8af8af5ba0 ("fs: fat: support write with non-zero
offset") to stay compatible with the fatwrite command, which replaces a
whole file. efi_file_write_int() uses fs_write(), so a write in the middle
of a file through EFI_FILE_PROTOCOL.Write() loses the data after it.

I found this with an EFI application that keeps a 256 MiB file on the
ESP and updates it in small pieces. With U-Boot 2025.01 on qemu_arm64 the
file went from 268435456 to 602112 bytes after one run. The code is the
same in current master (used for this submission).

What the specification and others do
------------------------------------

- UEFI 2.10, section 13.5.6, EFI_FILE_PROTOCOL.Write(): the data is
  written at the current position, the file grows if required, and
  partial writes only happen on a data error. Nothing allows a successful
  Write() to drop data after the written range.
- The UEFI SCT checks it: BBTestWriteBasicTestCheckpoint1 in edk2-test
  writes 50 bytes at position 0 of a 200 byte file and expects the size to
  stay 200. I did not run SCT on U-Boot. By reading it, this case fails
  today (and its set-up needs SetInfo() to change the file size, which
  U-Boot refuses).
- EDK2's FAT driver (FatIFileAccess) only grows the file when a write goes
  past the end.

So U-Boot's behaviour is surprising for EFI applications, and I believe
it is not conforming. I may be missing a reason for it, hence the RFC.

The series
----------

1. fs: fat: add a write that keeps the end of the file. The body of
   file_fat_write_at() moves to a static function with a keep_tail flag.
   file_fat_write_at() keeps its behaviour as a wrapper.
   file_fat_overwrite() is the new entry point.
2. fs: add fs_overwrite(). It uses an optional overwrite() operation in
   struct fstype_info. A filesystem without it falls back to write().
3. efi_loader: use fs_overwrite() in Write().

fs_write() and the fatwrite command do not change.

Questions
---------

- Naming: fs_overwrite() is what I chose. fs_write_inplace(),
  fs_write_keep_tail() and fs_update() were also considered. Please
  suggest a better one.
- exFAT has the same trim (exfat_fs_write() calls exfat_truncate() with
  offset + size). ext4 refuses a non-zero offset and replaces the file at
  offset 0. I did not touch them. Should exFAT get the same operation in
  this series? A test for efi_selftest is not included yet.

Cedric Hombourger (3):
  fs: fat: add a write that keeps the end of the file
  fs: add fs_overwrite() to write without trimming the file
  efi_loader: do not trim files in Write()

fs/fat/fat_write.c        | 32 ++++++++++++++++++++++++--------
 fs/fs.c                   | 25 ++++++++++++++++++++++---
 include/fat.h             |  2 ++
 include/fs.h              | 16 ++++++++++++++++
 lib/efi_loader/efi_file.c |  4 ++--
 5 files changed, 66 insertions(+), 13 deletions(-)

To: [email protected]
Cc: Tom Rini <[email protected]>
Cc: Cedric Hombourger <[email protected]>
Cc: Simon Glass <[email protected]>
Cc: Heinrich Schuchardt <[email protected]>
Cc: Qu Wenruo <[email protected]>
Cc: Alexey Charkov <[email protected]>
Cc: Kuan-Wei Chiu <[email protected]>
Cc: Vincent Jardin <[email protected]>
Cc: Ilias Apalodimas <[email protected]>
Cc: Neil Armstrong <[email protected]>
Cc: Jorge Ramirez-Ortiz <[email protected]>
-- 
2.47.3

Signed-off-by: Cedric Hombourger <[email protected]>
---
Changes in v2:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v1: 
https://lore.kernel.org/r/[email protected]

---
Cedric Hombourger (3):
      fs: fat: add a write that keeps the end of the file
      fs: add fs_overwrite() to write without trimming the file
      efi_loader: do not trim files in Write()

 fs/fat/fat_write.c        | 32 ++++++++++++++++++++++++--------
 fs/fs.c                   | 25 ++++++++++++++++++++++---
 include/fat.h             |  2 ++
 include/fs.h              | 16 ++++++++++++++++
 lib/efi_loader/efi_file.c |  4 ++--
 5 files changed, 66 insertions(+), 13 deletions(-)
---
base-commit: 2203aabfa59826d04d9e2094c6b32c1029cd01f8
change-id: 20261026-fat-fs-fixes-c5eb444f1e63
prerequisite-change-id: 20261026-fat-fixes:v1

Best regards,
-- 
Cedric Hombourger <[email protected]>

Reply via email to