]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
iovec-wrapper: introduce iovw_put_full() and friends to make them accept zero length...
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 25 Apr 2026 00:57:41 +0000 (09:57 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 12 May 2026 06:33:21 +0000 (15:33 +0900)
These will be used later. Preparation for later commits.

src/basic/iovec-wrapper.c
src/basic/iovec-wrapper.h
src/test/test-iovec-wrapper.c

index c5d1a878ff3cb4aeb8532f693654f59e6b860f52..375ecbdaaa23dc2f5a4c996dcf85b9d98f997acd 100644 (file)
@@ -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;
 
index 26b7f5ad4be30ccd3d1faa317e713f29e37c926f..c860eaf3339e772d35070e06bf32991ca6b18453 100644 (file)
@@ -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;
index d38806e75e543b3b3bece7ae6624c85097d0fd60..3bd2123c3c95a842ea035e478d1bf96e5f19afc6 100644 (file)
@@ -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) {