tree-wide: use pwrite_full() for positioned writes

Several places write a buffer at an offset with a single pwrite() or
pwritev(), and fail if the call writes less than the whole buffer. The
lock file of a dynamic user is then truncated to the bytes written, so
it keeps a cut-off user name. memfd_new_and_seal() fails for data larger
than one write() call accepts, which is about 2 GiB on Linux. The other
callers return -EIO, and lose the error that another write would report,
such as ENOSPC.

Use pwrite_full(), which keeps writing until the whole buffer is written
or a write fails. The lock file of a dynamic user is written from two
buffers and uses pwritev_full() instead.

repart writes the verity signature with lseek() and loop_write(). It
uses pwrite_full() as well, which does not move the file offset.

(cherry picked from commit 2fd17cb9eaca00a4dcb9af85a1f485e6d04bda04)
This commit is contained in:
Daan De Meyer
2026-10-16 17:24:51 +01:00
committed by Luca Boccassi
parent 8b9fb1bbec
commit ef565d78af
5 changed files with 44 additions and 61 deletions
+4 -5
View File
@@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "io-util.h"
#include "memfd-util.h"
#include "process-util.h"
#include "string-util.h"
@@ -132,11 +133,9 @@ int memfd_new_and_seal(const char *name, const void *data, size_t sz) {
return fd;
if (sz > 0) {
ssize_t n = pwrite(fd, data, sz, 0);
if (n < 0)
return -errno;
if ((size_t) n != sz)
return -EIO;
r = pwrite_full(fd, data, sz, 0);
if (r < 0)
return r;
}
r = memfd_set_sealed(fd);
+9 -9
View File
@@ -14,6 +14,7 @@
#include "fileio.h"
#include "format-util.h"
#include "hashmap.h"
#include "io-util.h"
#include "iovec-util.h"
#include "lock-util.h"
#include "manager.h"
@@ -186,7 +187,7 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
char lock_path[STRLEN("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
_cleanup_close_ int lock_fd = -EBADF;
uid_t candidate;
ssize_t l;
uint64_t written;
if (--n_tries <= 0) /* Give up retrying eventually */
return -EBUSY;
@@ -268,18 +269,17 @@ static int pick_uid(char **suggested_paths, const char *name, uid_t *ret_uid) {
}
/* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
l = pwritev(lock_fd,
(struct iovec[2]) {
IOVEC_MAKE_STRING(name),
IOVEC_MAKE((char[1]) { '\n' }, 1),
}, 2, 0);
if (l < 0) {
r = -errno;
r = pwritev_full(lock_fd,
(struct iovec[2]) {
IOVEC_MAKE_STRING(name),
IOVEC_MAKE((char[1]) { '\n' }, 1),
}, 2, 0, &written);
if (r < 0) {
(void) unlink(lock_path);
return r;
}
(void) ftruncate(lock_fd, l);
(void) ftruncate(lock_fd, written);
*ret_uid = candidate;
return TAKE_FD(lock_fd);
+7 -10
View File
@@ -5,6 +5,7 @@
#include "alloc-util.h"
#include "compress.h"
#include "copy.h"
#include "io-util.h"
#include "qcow2-util.h"
#include "sparse-endian.h"
@@ -80,11 +81,9 @@ static int copy_cluster(
if ((uint64_t) l != cluster_size)
return -EIO;
l = pwrite(dfd, buffer, cluster_size, doffset);
if (l < 0)
return -errno;
if ((uint64_t) l != cluster_size)
return -EIO;
r = pwrite_full(dfd, buffer, cluster_size, doffset);
if (r < 0)
return r;
return 0;
}
@@ -122,11 +121,9 @@ static int decompress_cluster(
if (r < 0)
return r;
l = pwrite(dfd, buffer2, cluster_size, doffset);
if (l < 0)
return -errno;
if ((uint64_t) l != cluster_size)
return -EIO;
r = pwrite_full(dfd, buffer2, cluster_size, doffset);
if (r < 0)
return r;
return 0;
}
+2 -8
View File
@@ -21,6 +21,7 @@
#include "fs-util.h"
#include "hashmap.h"
#include "id128-util.h"
#include "io-util.h"
#include "journal-authenticate-internal.h"
#include "journal-def.h"
#include "journal-file.h"
@@ -392,7 +393,6 @@ static int journal_file_init_header(
JournalFileFlags file_flags,
JournalFile *template) {
ssize_t k;
int r;
assert(f);
@@ -429,13 +429,7 @@ static int journal_file_init_header(
} else
h.seqnum_id = h.file_id;
k = pwrite(f->fd, &h, sizeof(h), 0);
if (k < 0)
return -errno;
if (k != sizeof(h))
return -EIO;
return 0;
return pwrite_full(f->fd, &h, sizeof(h), 0);
}
static int journal_file_refresh_header(JournalFile *f) {
+22 -29
View File
@@ -6311,10 +6311,7 @@ static int partition_format_verity_sig(Context *context, Partition *p) {
if (r < 0)
return log_error_errno(r, "Failed to pad string to %s", FORMAT_BYTES(p->new_size));
if (lseek(whole_fd, p->offset, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to partition %s offset: %m", strna(hint));
r = loop_write(whole_fd, text, p->new_size);
r = pwrite_full(whole_fd, text, p->new_size, p->offset);
if (r < 0)
return log_error_errno(r, "Failed to write verity signature to partition %s: %m", strna(hint));
@@ -8383,16 +8380,16 @@ static int write_primary_descriptor(
iso9660_datetime_zero(&desc.volume_expiration_date);
iso9660_datetime_zero(&desc.volume_effective_date);
ssize_t s = pwrite(fd, &desc, sizeof(desc), ISO9660_PRIMARY_DESCRIPTOR*ISO9660_BLOCK_SIZE);
if (s < 0)
return log_error_errno(errno, "Failed to write ISO9660 primary descriptor: %m");
if (s != sizeof(desc))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to fully write ISO9660 primary descriptor");
r = pwrite_full(fd, &desc, sizeof(desc), ISO9660_PRIMARY_DESCRIPTOR*ISO9660_BLOCK_SIZE);
if (r < 0)
return log_error_errno(r, "Failed to write ISO9660 primary descriptor: %m");
return 0;
}
static int write_eltorito_descriptor(int fd, uint32_t catalog_sector) {
int r;
assert(fd >= 0);
struct iso9660_eltorito_descriptor desc = {
@@ -8407,16 +8404,16 @@ static int write_eltorito_descriptor(int fd, uint32_t catalog_sector) {
strncpy(desc.boot_system_identifier, "EL TORITO SPECIFICATION", sizeof(desc.boot_system_identifier));
ssize_t s = pwrite(fd, &desc, sizeof(desc), ISO9660_ELTORITO_DESCRIPTOR*ISO9660_BLOCK_SIZE);
if (s < 0)
return log_error_errno(errno, "Failed to write ISO9660 El-Torito descriptor: %m");
if (s != sizeof(desc))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to fully write ISO9660 El-Torito descriptor");
r = pwrite_full(fd, &desc, sizeof(desc), ISO9660_ELTORITO_DESCRIPTOR*ISO9660_BLOCK_SIZE);
if (r < 0)
return log_error_errno(r, "Failed to write ISO9660 El-Torito descriptor: %m");
return 0;
}
static int write_terminal_descriptor(int fd) {
int r;
assert(fd >= 0);
struct iso9660_terminal_descriptor desc = {
@@ -8428,11 +8425,9 @@ static int write_terminal_descriptor(int fd) {
iso9660_set_const_string(desc.header.identifier, sizeof(desc.header.identifier), "CD001", /* allow_a_chars= */ true);
ssize_t s = pwrite(fd, &desc, sizeof(desc), ISO9660_TERMINAL_DESCRIPTOR*ISO9660_BLOCK_SIZE);
if (s < 0)
return log_error_errno(errno, "Failed to write ISO9660 terminal descriptor: %m");
if (s != sizeof(desc))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to fully write ISO9660 terminal descriptor");
r = pwrite_full(fd, &desc, sizeof(desc), ISO9660_TERMINAL_DESCRIPTOR*ISO9660_BLOCK_SIZE);
if (r < 0)
return log_error_errno(r, "Failed to write ISO9660 terminal descriptor: %m");
return 0;
}
@@ -8450,6 +8445,8 @@ static uint16_t calculate_validation_entry_checksum(const void *p, size_t size)
}
static int write_boot_catalog(int fd, uint32_t load_block) {
int r;
assert(fd >= 0);
struct el_torito_validation_entry ve = {
@@ -8485,11 +8482,9 @@ static int write_boot_catalog(int fd, uint32_t load_block) {
p = mempcpy(p, &sh, sizeof(sh));
assert((size_t) (p - sector) <= sizeof(sector));
ssize_t s = pwrite(fd, &sector, sizeof(sector), ISO9660_BOOT_CATALOG*ISO9660_BLOCK_SIZE);
if (s < 0)
return log_error_errno(errno, "Failed to write El-Torito boot catalog: %m");
if (s != sizeof(sector))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to fully write El-Torito boot catalog");
r = pwrite_full(fd, &sector, sizeof(sector), ISO9660_BOOT_CATALOG*ISO9660_BLOCK_SIZE);
if (r < 0)
return log_error_errno(r, "Failed to write El-Torito boot catalog: %m");
return 0;
}
@@ -8548,11 +8543,9 @@ static int write_directories(
p = mempcpy(p, &parent, sizeof(parent));
assert((size_t) (p - sector) <= sizeof(sector));
ssize_t s = pwrite(fd, &sector, sizeof(sector), ISO9660_ROOT_DIRECTORY*ISO9660_BLOCK_SIZE);
if (s < 0)
return log_error_errno(errno, "Failed to write ISO9660 root directory: %m");
if (s != sizeof(sector))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to fully write ISO9660 root directory");
r = pwrite_full(fd, &sector, sizeof(sector), ISO9660_ROOT_DIRECTORY*ISO9660_BLOCK_SIZE);
if (r < 0)
return log_error_errno(r, "Failed to write ISO9660 root directory: %m");
return 0;
}