1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2018 FUJITSU LIMITED. All rights reserved.
4 * Author: Xiao Yang <[email protected]>
5 * Copyright (c) Linux Test Project, 2019-2023
6 */
7
8 /*\
9 * [Description]
10 *
11 * Check the basic functionality of the pwritev(2) for the file
12 * opened with O_DIRECT in all filesystem.
13 *
14 * pwritev(2) should succeed to write the expected content of data
15 * and after writing the file, the file offset is not changed.
16 */
17
18 #define _GNU_SOURCE
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/uio.h>
22 #include <sys/ioctl.h>
23 #include <sys/mount.h>
24 #include "tst_test.h"
25 #include "pwritev.h"
26 #include "tst_safe_prw.h"
27
28 #define MNTPOINT "mntpoint"
29 #define FNAME MNTPOINT"/file"
30
31 static char *initbuf, *preadbuf;
32 static int fd;
33 static off_t blk_off, zero_off;
34 static ssize_t blksz;
35
36 static struct iovec wr_iovec[] = {
37 {NULL, 0},
38 {NULL, 0},
39 };
40
41 static struct tcase {
42 int count;
43 off_t *offset;
44 ssize_t *size;
45 } tcases[] = {
46 {1, &zero_off, &blksz},
47 {2, &zero_off, &blksz},
48 {1, &blk_off, &blksz},
49 };
50
verify_direct_pwritev(unsigned int n)51 static void verify_direct_pwritev(unsigned int n)
52 {
53 int i;
54 struct tcase *tc = &tcases[n];
55
56 SAFE_PWRITE(1, fd, initbuf, blksz * 2, 0);
57
58 TEST(pwritev(fd, wr_iovec, tc->count, *tc->offset));
59 if (TST_RET < 0) {
60 tst_res(TFAIL | TTERRNO, "pwritev(O_DIRECT) fails");
61 return;
62 }
63
64 if (TST_RET != *tc->size) {
65 tst_res(TFAIL, "pwritev(O_DIRECT) wrote %li bytes, expected %zi",
66 TST_RET, *tc->size);
67 return;
68 }
69
70 if (SAFE_LSEEK(fd, 0, SEEK_CUR) != 0) {
71 tst_res(TFAIL, "pwritev(O_DIRECT) had changed file offset");
72 return;
73 }
74
75 memset(preadbuf, 0x00, blksz);
76 SAFE_PREAD(1, fd, preadbuf, *tc->size, *tc->offset);
77
78 for (i = 0; i < *tc->size; i++) {
79 if (preadbuf[i] != 0x61)
80 break;
81 }
82
83 if (i != *tc->size) {
84 tst_res(TFAIL, "Buffer wrong at %i have %02x expected 0x61",
85 i, preadbuf[i]);
86 return;
87 }
88
89 tst_res(TPASS, "pwritev(O_DIRECT) wrote %zi bytes successfully "
90 "with content 'a' expectedly ", *tc->size);
91 }
92
setup(void)93 static void setup(void)
94 {
95 int dev_fd, ret;
96
97 dev_fd = SAFE_OPEN(tst_device->dev, O_RDWR);
98 SAFE_IOCTL(dev_fd, BLKSSZGET, &ret);
99 SAFE_CLOSE(dev_fd);
100
101 if (ret <= 0)
102 tst_brk(TBROK, "BLKSSZGET returned invalid block size %i", ret);
103
104 tst_res(TINFO, "Using block size %i", ret);
105
106 blksz = ret;
107 blk_off = ret;
108
109 fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT | O_DIRECT, 0644);
110
111 initbuf = SAFE_MEMALIGN(blksz, blksz * 2);
112 memset(initbuf, 0x00, blksz * 2);
113
114 preadbuf = SAFE_MEMALIGN(blksz, blksz);
115
116 wr_iovec[0].iov_base = SAFE_MEMALIGN(blksz, blksz);
117 wr_iovec[0].iov_len = blksz;
118 memset(wr_iovec[0].iov_base, 0x61, blksz);
119 }
120
cleanup(void)121 static void cleanup(void)
122 {
123 free(initbuf);
124 free(preadbuf);
125 free(wr_iovec[0].iov_base);
126
127 if (fd > 0)
128 SAFE_CLOSE(fd);
129 }
130
131 static struct tst_test test = {
132 .tcnt = ARRAY_SIZE(tcases),
133 .setup = setup,
134 .cleanup = cleanup,
135 .test = verify_direct_pwritev,
136 .mntpoint = MNTPOINT,
137 .mount_device = 1,
138 .all_filesystems = 1,
139 .skip_filesystems = (const char *[]){
140 "tmpfs",
141 NULL
142 }
143 };
144