From: Yu Watanabe Date: Sat, 25 Apr 2026 00:57:41 +0000 (+0900) Subject: iovec-wrapper: introduce iovw_put_full() and friends to make them accept zero length... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fe6fed5d9730de40bd328ee11678ab235d067b94;p=thirdparty%2Fsystemd.git iovec-wrapper: introduce iovw_put_full() and friends to make them accept zero length entry These will be used later. Preparation for later commits. --- diff --git a/src/basic/iovec-wrapper.c b/src/basic/iovec-wrapper.c index c5d1a878ff3..375ecbdaaa2 100644 --- a/src/basic/iovec-wrapper.c +++ b/src/basic/iovec-wrapper.c @@ -45,14 +45,13 @@ int iovw_compare(const struct iovec_wrapper *a, const struct iovec_wrapper *b) { return CMP(a->count, b->count); } -int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) { +int iovw_put_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len) { assert(iovw); + assert(data || len == 0); - if (len == 0) + if (len == 0 && !accept_zero) return 0; - assert(data); - if (iovw->count >= IOV_MAX) return -E2BIG; @@ -63,16 +62,18 @@ int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) { return 1; } -int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov) { +int iovw_put_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov) { assert(iovw); if (!iov) return 0; - return iovw_put(iovw, iov->iov_base, iov->iov_len); + return iovw_put_full(iovw, accept_zero, iov->iov_base, iov->iov_len); } -int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) { +int iovw_put_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source) { + int r; + assert(iovw); if (iovw_isempty(source)) @@ -87,24 +88,41 @@ int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source if (iovw->count + source->count > IOV_MAX) return -E2BIG; - if (!GREEDY_REALLOC_APPEND(iovw->iovec, iovw->count, source->iovec, source->count)) - return -ENOMEM; + if (accept_zero) { + if (!GREEDY_REALLOC_APPEND(iovw->iovec, iovw->count, source->iovec, source->count)) + return -ENOMEM; + + return 0; + } + + /* When accept_zero is false, we need to filter zero length iovec in source. */ + size_t original_count = iovw->count; + + FOREACH_ARRAY(iovec, source->iovec, source->count) { + r = iovw_put_iov_full(iovw, accept_zero, iovec); + if (r < 0) + goto rollback; + } return 0; + +rollback: + iovw->count = original_count; + return r; } -int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) { +int iovw_consume_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len) { /* Move data into iovw or free on error */ int r; - r = iovw_put(iovw, data, len); + r = iovw_put_full(iovw, accept_zero, data, len); if (r <= 0) free(data); return r; } -int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) { +int iovw_consume_iov_full(struct iovec_wrapper *iovw, bool accept_zero, struct iovec *iov) { int r; assert(iovw); @@ -112,7 +130,7 @@ int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) { if (!iov) return 0; - r = iovw_put_iov(iovw, iov); + r = iovw_put_iov_full(iovw, accept_zero, iov); if (r <= 0) iovec_done(iov); else @@ -123,27 +141,30 @@ int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) { return r; } -int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len) { +int iovw_extend_full(struct iovec_wrapper *iovw, bool accept_zero, const void *data, size_t len) { + assert(iovw); + assert(data || len == 0); + if (len == 0) - return 0; + return iovw_put_full(iovw, accept_zero, /* data= */ NULL, /* len= */ 0); void *c = memdup(data, len); if (!c) return -ENOMEM; - return iovw_consume(iovw, c, len); + return iovw_consume_full(iovw, accept_zero, c, len); } -int iovw_extend_iov(struct iovec_wrapper *iovw, const struct iovec *iov) { +int iovw_extend_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov) { assert(iovw); - if (!iovec_is_set(iov)) + if (!iov) return 0; - return iovw_extend(iovw, iov->iov_base, iov->iov_len); + return iovw_extend_full(iovw, accept_zero, iov->iov_base, iov->iov_len); } -int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) { +int iovw_extend_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source) { int r; assert(iovw); @@ -165,7 +186,7 @@ int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *sou size_t original_count = iovw->count; FOREACH_ARRAY(iovec, source->iovec, source->count) { - r = iovw_extend_iov(iovw, iovec); + r = iovw_extend_iov_full(iovw, accept_zero, iovec); if (r < 0) goto rollback; } @@ -260,7 +281,7 @@ int iovw_concat(const struct iovec_wrapper *iovw, struct iovec *ret) { uint8_t *p = buf; FOREACH_ARRAY(i, iovw->iovec, iovw->count) - p = mempcpy(p, i->iov_base, i->iov_len); + p = mempcpy_safe(p, i->iov_base, i->iov_len); *p = 0; diff --git a/src/basic/iovec-wrapper.h b/src/basic/iovec-wrapper.h index 26b7f5ad4be..c860eaf3339 100644 --- a/src/basic/iovec-wrapper.h +++ b/src/basic/iovec-wrapper.h @@ -16,14 +16,38 @@ static inline bool iovw_equal(const struct iovec_wrapper *a, const struct iovec_ return iovw_compare(a, b) == 0; } -int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len); -int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov); -int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source); -int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len); -int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov); -int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len); -int iovw_extend_iov(struct iovec_wrapper *iovw, const struct iovec *iov); -int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source); +int iovw_put_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len); +static inline int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) { + return iovw_put_full(iovw, false, data, len); +} +int iovw_put_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov); +static inline int iovw_put_iov(struct iovec_wrapper *iovw, const struct iovec *iov) { + return iovw_put_iov_full(iovw, false, iov); +} +int iovw_put_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source); +static inline int iovw_put_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) { + return iovw_put_iovw_full(iovw, false, source); +} +int iovw_consume_full(struct iovec_wrapper *iovw, bool accept_zero, void *data, size_t len); +static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) { + return iovw_consume_full(iovw, false, data, len); +} +int iovw_consume_iov_full(struct iovec_wrapper *iovw, bool accept_zero, struct iovec *iov); +static inline int iovw_consume_iov(struct iovec_wrapper *iovw, struct iovec *iov) { + return iovw_consume_iov_full(iovw, false, iov); +} +int iovw_extend_full(struct iovec_wrapper *iovw, bool accept_zero, const void *data, size_t len); +static inline int iovw_extend(struct iovec_wrapper *iovw, const void *data, size_t len) { + return iovw_extend_full(iovw, false, data, len); +} +int iovw_extend_iov_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec *iov); +static inline int iovw_extend_iov(struct iovec_wrapper *iovw, const struct iovec *iov) { + return iovw_extend_iov_full(iovw, false, iov); +} +int iovw_extend_iovw_full(struct iovec_wrapper *iovw, bool accept_zero, const struct iovec_wrapper *source); +static inline int iovw_extend_iovw(struct iovec_wrapper *iovw, const struct iovec_wrapper *source) { + return iovw_extend_iovw_full(iovw, false, source); +} static inline bool iovw_isempty(const struct iovec_wrapper *iovw) { return !iovw || iovw->count == 0; diff --git a/src/test/test-iovec-wrapper.c b/src/test/test-iovec-wrapper.c index d38806e75e5..3bd2123c3c9 100644 --- a/src/test/test-iovec-wrapper.c +++ b/src/test/test-iovec-wrapper.c @@ -82,6 +82,12 @@ TEST(iovw_put) { ASSERT_EQ(memcmp(iovw.iovec[1].iov_base, "barbar", 6), 0); ASSERT_EQ(iovw.iovec[2].iov_len, 1U); ASSERT_EQ(memcmp(iovw.iovec[2].iov_base, "q", 1), 0); + + ASSERT_OK(iovw_put_full(&iovw, /* accept_zero= */ false, NULL, 0)); + ASSERT_EQ(iovw.count, 3U); + ASSERT_OK(iovw_put_full(&iovw, /* accept_zero= */ true, NULL, 0)); + ASSERT_EQ(iovw.count, 4U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[3], &(struct iovec) {})); } TEST(iovw_put_iov) { @@ -100,6 +106,12 @@ TEST(iovw_put_iov) { ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa"))); ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb"))); ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); + + ASSERT_OK(iovw_put_iov_full(&iovw, /* accept_zero= */ false, &(struct iovec) {})); + ASSERT_EQ(iovw.count, 3U); + ASSERT_OK(iovw_put_iov_full(&iovw, /* accept_zero= */ true, &(struct iovec) {})); + ASSERT_EQ(iovw.count, 4U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[3], &(struct iovec) {})); } TEST(iovw_put_iovw) { @@ -113,7 +125,8 @@ TEST(iovw_put_iovw) { ASSERT_OK(iovw_put_iov(&source, &IOVEC_MAKE_STRING("aaa"))); ASSERT_OK(iovw_put_iov(&source, &IOVEC_MAKE_STRING("bbb"))); ASSERT_OK(iovw_put_iov(&source, &IOVEC_MAKE_STRING("ccc"))); - ASSERT_EQ(source.count, 3U); + ASSERT_OK(iovw_put_iov_full(&source, /* accept_zero= */ true, &(struct iovec) {})); + ASSERT_EQ(source.count, 4U); /* Pre-seed target with one entry to check that append adds on top rather than replacing */ ASSERT_OK(iovw_put_iov(&target, &IOVEC_MAKE_STRING("xxx"))); @@ -136,10 +149,24 @@ TEST(iovw_put_iovw) { ASSERT_PTR_EQ(target.iovec[5].iov_base, source.iovec[2].iov_base); /* Source is unchanged */ - ASSERT_EQ(source.count, 3U); + ASSERT_EQ(source.count, 4U); ASSERT_TRUE(iovec_equal(&source.iovec[0], &IOVEC_MAKE_STRING("aaa"))); ASSERT_TRUE(iovec_equal(&source.iovec[1], &IOVEC_MAKE_STRING("bbb"))); ASSERT_TRUE(iovec_equal(&source.iovec[2], &IOVEC_MAKE_STRING("ccc"))); + ASSERT_TRUE(iovec_equal(&source.iovec[3], &(struct iovec) {})); + + ASSERT_OK(iovw_put_iovw_full(&target, /* accept_zero= */ true, &source)); + ASSERT_EQ(target.count, 10U); + ASSERT_TRUE(iovec_equal(&target.iovec[0], &IOVEC_MAKE_STRING("xxx"))); + ASSERT_TRUE(iovec_equal(&target.iovec[1], &IOVEC_MAKE_STRING("yyy"))); + ASSERT_TRUE(iovec_equal(&target.iovec[2], &IOVEC_MAKE_STRING("zzz"))); + ASSERT_TRUE(iovec_equal(&target.iovec[3], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&target.iovec[4], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&target.iovec[5], &IOVEC_MAKE_STRING("ccc"))); + ASSERT_TRUE(iovec_equal(&target.iovec[6], &IOVEC_MAKE_STRING("aaa"))); + ASSERT_TRUE(iovec_equal(&target.iovec[7], &IOVEC_MAKE_STRING("bbb"))); + ASSERT_TRUE(iovec_equal(&target.iovec[8], &IOVEC_MAKE_STRING("ccc"))); + ASSERT_TRUE(iovec_equal(&target.iovec[9], &(struct iovec) {})); /* Cannot pass the same objects */ ASSERT_ERROR(iovw_put_iovw(&target, &target), EINVAL); @@ -169,6 +196,12 @@ TEST(iovw_extend) { /* Mutating the caller's buffer does not affect what's stored */ memset(buf, 'X', sizeof buf); ASSERT_EQ(memcmp(iovw.iovec[0].iov_base, "one", 3), 0); + + ASSERT_OK(iovw_extend_full(&iovw, /* accept_zero= */ false, NULL, 0)); + ASSERT_EQ(iovw.count, 2U); + ASSERT_OK(iovw_extend_full(&iovw, /* accept_zero= */ true, NULL, 0)); + ASSERT_EQ(iovw.count, 3U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &(struct iovec) {})); } TEST(iovw_extend_iov) { @@ -186,6 +219,12 @@ TEST(iovw_extend_iov) { ASSERT_TRUE(iovec_equal(&iovw.iovec[0], &IOVEC_MAKE_STRING("aaa"))); ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &IOVEC_MAKE_STRING("bbb"))); ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &IOVEC_MAKE_STRING("ccc"))); + + ASSERT_OK(iovw_extend_iov_full(&iovw, /* accept_zero= */ false, &(struct iovec) {})); + ASSERT_EQ(iovw.count, 3U); + ASSERT_OK(iovw_extend_iov_full(&iovw, /* accept_zero= */ true, &(struct iovec) {})); + ASSERT_EQ(iovw.count, 4U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[3], &(struct iovec) {})); } TEST(iovw_extend_iovw) { @@ -199,7 +238,8 @@ TEST(iovw_extend_iovw) { ASSERT_OK(iovw_put(&source, (char*) "one", 3)); ASSERT_OK(iovw_put(&source, (char*) "twotwo", 6)); - ASSERT_EQ(source.count, 2U); + ASSERT_OK(iovw_put_full(&source, /* accept_zero= */ true, NULL, 0)); + ASSERT_EQ(source.count, 3U); /* Pre-seed target with one entry to check that append adds on top rather than replacing */ char *seed = strdup("zero"); @@ -218,8 +258,17 @@ TEST(iovw_extend_iovw) { ASSERT_EQ(target.iovec[2].iov_len, 6U); ASSERT_EQ(memcmp(target.iovec[2].iov_base, "twotwo", 6), 0); + ASSERT_OK(iovw_extend_iovw_full(&target, /* accept_zero= */ true, &source)); + ASSERT_EQ(target.count, 6U); + ASSERT_TRUE(iovec_equal(&target.iovec[0], &IOVEC_MAKE_STRING("zero"))); + ASSERT_TRUE(iovec_equal(&target.iovec[1], &IOVEC_MAKE_STRING("one"))); + ASSERT_TRUE(iovec_equal(&target.iovec[2], &IOVEC_MAKE_STRING("twotwo"))); + ASSERT_TRUE(iovec_equal(&target.iovec[3], &IOVEC_MAKE_STRING("one"))); + ASSERT_TRUE(iovec_equal(&target.iovec[4], &IOVEC_MAKE_STRING("twotwo"))); + ASSERT_TRUE(iovec_equal(&target.iovec[5], &(struct iovec) {})); + /* Source is unchanged */ - ASSERT_EQ(source.count, 2U); + ASSERT_EQ(source.count, 3U); /* Cannot pass the same objects */ ASSERT_ERROR(iovw_extend_iovw(&target, &target), EINVAL); @@ -240,6 +289,16 @@ TEST(iovw_consume) { char *q = ASSERT_NOT_NULL(strdup("")); ASSERT_OK_ZERO(iovw_consume(&iovw, q, 0)); ASSERT_EQ(iovw.count, 1U); + + ASSERT_OK(iovw_consume_full(&iovw, /* accept_zero= */ false, NULL, 0)); + ASSERT_EQ(iovw.count, 1U); + ASSERT_OK(iovw_consume_full(&iovw, /* accept_zero= */ true, NULL, 0)); + ASSERT_EQ(iovw.count, 2U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &(struct iovec) {})); + q = ASSERT_NOT_NULL(strdup("")); + ASSERT_OK(iovw_consume_full(&iovw, /* accept_zero= */ true, q, 0)); + ASSERT_EQ(iovw.count, 3U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &(struct iovec) {})); } TEST(iovw_consume_iov) { @@ -270,6 +329,19 @@ TEST(iovw_consume_iov) { /* zero length iovec is also freed */ ASSERT_NULL(iov.iov_base); ASSERT_EQ(iov.iov_len, 0U); + + ASSERT_OK(iovw_consume_iov_full(&iovw, /* accept_zero= */ false, &(struct iovec) {})); + ASSERT_EQ(iovw.count, 1U); + ASSERT_OK(iovw_consume_iov_full(&iovw, /* accept_zero= */ true, &(struct iovec) {})); + ASSERT_EQ(iovw.count, 2U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[1], &(struct iovec) {})); + iov = (struct iovec) { + .iov_base = ASSERT_NOT_NULL(strdup("")), + .iov_len = 0, + }; + ASSERT_OK(iovw_consume_iov_full(&iovw, /* accept_zero= */ true, &iov)); + ASSERT_EQ(iovw.count, 3U); + ASSERT_TRUE(iovec_equal(&iovw.iovec[2], &(struct iovec) {})); } TEST(iovw_isempty) {