1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-or-later
2*49cdfc7eSAndroid Build Coastguard Worker /*
3*49cdfc7eSAndroid Build Coastguard Worker *
4*49cdfc7eSAndroid Build Coastguard Worker * Copyright (c) Linux Test Project, 2016
5*49cdfc7eSAndroid Build Coastguard Worker */
6*49cdfc7eSAndroid Build Coastguard Worker
7*49cdfc7eSAndroid Build Coastguard Worker /*\
8*49cdfc7eSAndroid Build Coastguard Worker * [Description]
9*49cdfc7eSAndroid Build Coastguard Worker *
10*49cdfc7eSAndroid Build Coastguard Worker * Verify writev() behaviour with partially valid iovec list.
11*49cdfc7eSAndroid Build Coastguard Worker * Kernel <4.8 used to shorten write up to first bad invalid
12*49cdfc7eSAndroid Build Coastguard Worker * iovec. Starting with 4.8, a writev with short data (under
13*49cdfc7eSAndroid Build Coastguard Worker * page size) is likely to get shorten to 0 bytes and return
14*49cdfc7eSAndroid Build Coastguard Worker * EFAULT.
15*49cdfc7eSAndroid Build Coastguard Worker *
16*49cdfc7eSAndroid Build Coastguard Worker * This test doesn't make assumptions how much will write get
17*49cdfc7eSAndroid Build Coastguard Worker * shortened. It only tests that file content/offset after
18*49cdfc7eSAndroid Build Coastguard Worker * syscall corresponds to return value of writev().
19*49cdfc7eSAndroid Build Coastguard Worker *
20*49cdfc7eSAndroid Build Coastguard Worker * See: [RFC] writev() semantics with invalid iovec in the middle
21*49cdfc7eSAndroid Build Coastguard Worker * https://marc.info/?l=linux-kernel&m=147388891614289&w=2.
22*49cdfc7eSAndroid Build Coastguard Worker *
23*49cdfc7eSAndroid Build Coastguard Worker * This is also regression test for kernel commits:
24*49cdfc7eSAndroid Build Coastguard Worker *
25*49cdfc7eSAndroid Build Coastguard Worker * * 20c64ec83a9f ("iomap: fix a regression for partial write errors")
26*49cdfc7eSAndroid Build Coastguard Worker * * 3ac974796e5d ("iomap: fix short copy in iomap_write_iter()")
27*49cdfc7eSAndroid Build Coastguard Worker */
28*49cdfc7eSAndroid Build Coastguard Worker
29*49cdfc7eSAndroid Build Coastguard Worker #include <errno.h>
30*49cdfc7eSAndroid Build Coastguard Worker #include <fcntl.h>
31*49cdfc7eSAndroid Build Coastguard Worker #include <stdio.h>
32*49cdfc7eSAndroid Build Coastguard Worker #include <sys/mman.h>
33*49cdfc7eSAndroid Build Coastguard Worker #include <sys/stat.h>
34*49cdfc7eSAndroid Build Coastguard Worker #include <sys/types.h>
35*49cdfc7eSAndroid Build Coastguard Worker #include <sys/uio.h>
36*49cdfc7eSAndroid Build Coastguard Worker #include "tst_test.h"
37*49cdfc7eSAndroid Build Coastguard Worker
38*49cdfc7eSAndroid Build Coastguard Worker #define TESTFILE "testfile"
39*49cdfc7eSAndroid Build Coastguard Worker #define CHUNK 64
40*49cdfc7eSAndroid Build Coastguard Worker #define BUFSIZE (CHUNK * 4)
41*49cdfc7eSAndroid Build Coastguard Worker
42*49cdfc7eSAndroid Build Coastguard Worker static void *bad_addr;
43*49cdfc7eSAndroid Build Coastguard Worker
test_partially_valid_iovec(int initial_file_offset)44*49cdfc7eSAndroid Build Coastguard Worker static void test_partially_valid_iovec(int initial_file_offset)
45*49cdfc7eSAndroid Build Coastguard Worker {
46*49cdfc7eSAndroid Build Coastguard Worker int i, fd;
47*49cdfc7eSAndroid Build Coastguard Worker unsigned char buffer[BUFSIZE], fpattern[BUFSIZE], tmp[BUFSIZE];
48*49cdfc7eSAndroid Build Coastguard Worker long off_after;
49*49cdfc7eSAndroid Build Coastguard Worker struct iovec wr_iovec[] = {
50*49cdfc7eSAndroid Build Coastguard Worker { buffer, CHUNK },
51*49cdfc7eSAndroid Build Coastguard Worker { bad_addr, CHUNK },
52*49cdfc7eSAndroid Build Coastguard Worker { buffer + CHUNK, CHUNK },
53*49cdfc7eSAndroid Build Coastguard Worker { buffer + CHUNK * 2, CHUNK },
54*49cdfc7eSAndroid Build Coastguard Worker };
55*49cdfc7eSAndroid Build Coastguard Worker
56*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "starting test with initial file offset: %d ",
57*49cdfc7eSAndroid Build Coastguard Worker initial_file_offset);
58*49cdfc7eSAndroid Build Coastguard Worker
59*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < BUFSIZE; i++)
60*49cdfc7eSAndroid Build Coastguard Worker buffer[i] = i % (CHUNK - 1);
61*49cdfc7eSAndroid Build Coastguard Worker
62*49cdfc7eSAndroid Build Coastguard Worker memset(fpattern, 0xff, BUFSIZE);
63*49cdfc7eSAndroid Build Coastguard Worker tst_fill_file(TESTFILE, 0xff, CHUNK, BUFSIZE / CHUNK);
64*49cdfc7eSAndroid Build Coastguard Worker
65*49cdfc7eSAndroid Build Coastguard Worker fd = SAFE_OPEN(TESTFILE, O_RDWR, 0644);
66*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(fd, initial_file_offset, SEEK_SET);
67*49cdfc7eSAndroid Build Coastguard Worker TEST(writev(fd, wr_iovec, ARRAY_SIZE(wr_iovec)));
68*49cdfc7eSAndroid Build Coastguard Worker off_after = (long) SAFE_LSEEK(fd, 0, SEEK_CUR);
69*49cdfc7eSAndroid Build Coastguard Worker
70*49cdfc7eSAndroid Build Coastguard Worker /* bad errno */
71*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1 && TST_ERR != EFAULT) {
72*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL | TTERRNO, "unexpected errno");
73*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
74*49cdfc7eSAndroid Build Coastguard Worker return;
75*49cdfc7eSAndroid Build Coastguard Worker }
76*49cdfc7eSAndroid Build Coastguard Worker
77*49cdfc7eSAndroid Build Coastguard Worker /* nothing has been written */
78*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET == -1 && TST_ERR == EFAULT) {
79*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "got EFAULT");
80*49cdfc7eSAndroid Build Coastguard Worker /* initial file content remains untouched */
81*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(fd, 0, SEEK_SET);
82*49cdfc7eSAndroid Build Coastguard Worker SAFE_READ(1, fd, tmp, BUFSIZE);
83*49cdfc7eSAndroid Build Coastguard Worker if (memcmp(tmp, fpattern, BUFSIZE))
84*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "file was written to");
85*49cdfc7eSAndroid Build Coastguard Worker else
86*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "file stayed untouched");
87*49cdfc7eSAndroid Build Coastguard Worker
88*49cdfc7eSAndroid Build Coastguard Worker /* offset hasn't changed */
89*49cdfc7eSAndroid Build Coastguard Worker if (off_after == initial_file_offset)
90*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "offset stayed unchanged");
91*49cdfc7eSAndroid Build Coastguard Worker else
92*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "offset changed to %ld",
93*49cdfc7eSAndroid Build Coastguard Worker off_after);
94*49cdfc7eSAndroid Build Coastguard Worker
95*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
96*49cdfc7eSAndroid Build Coastguard Worker return;
97*49cdfc7eSAndroid Build Coastguard Worker }
98*49cdfc7eSAndroid Build Coastguard Worker
99*49cdfc7eSAndroid Build Coastguard Worker /* writev() wrote more bytes than bytes preceding invalid iovec */
100*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "writev() has written %ld bytes", TST_RET);
101*49cdfc7eSAndroid Build Coastguard Worker if (TST_RET > (long) wr_iovec[0].iov_len) {
102*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "writev wrote more than expected");
103*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
104*49cdfc7eSAndroid Build Coastguard Worker return;
105*49cdfc7eSAndroid Build Coastguard Worker }
106*49cdfc7eSAndroid Build Coastguard Worker
107*49cdfc7eSAndroid Build Coastguard Worker /* file content matches written bytes */
108*49cdfc7eSAndroid Build Coastguard Worker SAFE_LSEEK(fd, initial_file_offset, SEEK_SET);
109*49cdfc7eSAndroid Build Coastguard Worker SAFE_READ(1, fd, tmp, TST_RET);
110*49cdfc7eSAndroid Build Coastguard Worker if (memcmp(tmp, wr_iovec[0].iov_base, TST_RET) == 0) {
111*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "file has expected content");
112*49cdfc7eSAndroid Build Coastguard Worker } else {
113*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "file has unexpected content");
114*49cdfc7eSAndroid Build Coastguard Worker tst_res_hexd(TFAIL, wr_iovec[0].iov_base, TST_RET,
115*49cdfc7eSAndroid Build Coastguard Worker "expected:");
116*49cdfc7eSAndroid Build Coastguard Worker tst_res_hexd(TFAIL, tmp, TST_RET,
117*49cdfc7eSAndroid Build Coastguard Worker "actual file content:");
118*49cdfc7eSAndroid Build Coastguard Worker }
119*49cdfc7eSAndroid Build Coastguard Worker
120*49cdfc7eSAndroid Build Coastguard Worker /* file offset has been updated according to written bytes */
121*49cdfc7eSAndroid Build Coastguard Worker if (off_after == initial_file_offset + TST_RET)
122*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "offset at %ld as expected", off_after);
123*49cdfc7eSAndroid Build Coastguard Worker else
124*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL, "offset unexpected %ld", off_after);
125*49cdfc7eSAndroid Build Coastguard Worker
126*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
127*49cdfc7eSAndroid Build Coastguard Worker }
128*49cdfc7eSAndroid Build Coastguard Worker
test_writev(void)129*49cdfc7eSAndroid Build Coastguard Worker static void test_writev(void)
130*49cdfc7eSAndroid Build Coastguard Worker {
131*49cdfc7eSAndroid Build Coastguard Worker test_partially_valid_iovec(0);
132*49cdfc7eSAndroid Build Coastguard Worker test_partially_valid_iovec(CHUNK + 1);
133*49cdfc7eSAndroid Build Coastguard Worker test_partially_valid_iovec(getpagesize());
134*49cdfc7eSAndroid Build Coastguard Worker test_partially_valid_iovec(getpagesize() + 1);
135*49cdfc7eSAndroid Build Coastguard Worker }
136*49cdfc7eSAndroid Build Coastguard Worker
setup(void)137*49cdfc7eSAndroid Build Coastguard Worker static void setup(void)
138*49cdfc7eSAndroid Build Coastguard Worker {
139*49cdfc7eSAndroid Build Coastguard Worker bad_addr = SAFE_MMAP(0, 1, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS,
140*49cdfc7eSAndroid Build Coastguard Worker 0, 0);
141*49cdfc7eSAndroid Build Coastguard Worker }
142*49cdfc7eSAndroid Build Coastguard Worker
143*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
144*49cdfc7eSAndroid Build Coastguard Worker .needs_tmpdir = 1,
145*49cdfc7eSAndroid Build Coastguard Worker .setup = setup,
146*49cdfc7eSAndroid Build Coastguard Worker .test_all = test_writev,
147*49cdfc7eSAndroid Build Coastguard Worker .tags = (const struct tst_tag[]) {
148*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "20c64ec83a9f"},
149*49cdfc7eSAndroid Build Coastguard Worker {"linux-git", "3ac974796e5d"},
150*49cdfc7eSAndroid Build Coastguard Worker {},
151*49cdfc7eSAndroid Build Coastguard Worker }
152*49cdfc7eSAndroid Build Coastguard Worker };
153