generic: mmap and copy file data with page overlapping
[xfstests-dev.git] / src / t_mmap_writev_overlap.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2021 RedHat Inc.  All Rights Reserved.
4  *
5  * mmap a file, alloc blocks, reading&writing blocks with overlapping. For example:
6  *
7  * |<--- block --->|<--- block --->|
8  *  len             len
9  * +---------------+---------------+
10  * |AAAA| ........ |AAAA| ........ |
11  * +---------------+---------------+
12  *    |               |
13  *    |               `------------+
14  *    `-----------------------+    |
15  *                            |    |
16  *                            V    V
17  * +---------------+---------------+----+
18  * |AAAA| ........ |AAAA| ... |AAAA|AAAA|
19  * +---------------+---------------+----+
20  */
21 #include <sys/types.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <sys/mman.h>
27 #include <string.h>
28 #include <sys/uio.h>
29 #include <unistd.h>
30
31 void usage(char *progname)
32 {
33         fprintf(stderr, "usage: %s [-b blocksize ] [-c count] [-l length] filename\n"
34                 "\tmmap $count * $blocksize bytes memory, pwritev $length bytes in each block. blocksize=4096, count=2, length=12 by default.\n"
35                 "e.g: %s -b 4096 -c 2 -l 12 filename\n",
36                 progname, progname);
37         exit(1);
38 }
39
40 int main(int argc, char **argv)
41 {
42         char *filename = NULL;
43         size_t bsize = 4096;
44         size_t count = 2;
45         size_t length = 12;
46         int fd, i, c;
47         void *base;
48         char *buf, *cmp_buf;
49         struct iovec *iov;
50         int ret = 0;
51
52         while ((c = getopt(argc, argv, "b:l:c:")) != -1) {
53                 char *endp;
54
55                 switch (c) {
56                 case 'b':
57                         bsize = strtoul(optarg, &endp, 0);
58                         break;
59                 case 'c':
60                         count = strtoul(optarg, &endp, 0);
61                         break;
62                 case 'l':
63                         length = strtoul(optarg, &endp, 0);
64                         break;
65                 default:
66                         usage(argv[0]);
67                 }
68         }
69
70         if (optind == argc - 1)
71                 filename = argv[optind];
72         else
73                 usage(argv[0]);
74
75         if (length >= bsize) {
76                 printf("-l length must be less than -b blocksize\n");
77                 usage(argv[0]);
78         }
79
80         fd = open(filename, O_RDWR);
81         if (fd == -1) {
82                 fprintf(stderr, "open %s failed:%s\n", filename, strerror(errno));
83                 exit(1);
84         }
85         base = mmap(NULL, bsize * count, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
86         if (base == MAP_FAILED) {
87                 fprintf(stderr, "mmap failed %s\n", strerror(errno));
88                 exit(1);
89         }
90
91         /* Write each of blocks */
92         buf = malloc(length);
93         memset(buf, 0xAA, length);
94         for (i=0; i<count; i++) {
95                 ret = pwrite(fd, buf, length, i * bsize);
96                 if (ret == -1) {
97                         fprintf(stderr, "pwrite failed %s\n", strerror(errno));
98                         exit(1);
99                 }
100         }
101
102         /* Copy from the beginning of each blocks ... */
103         iov = malloc(sizeof(struct iovec) * count);
104         for (i=0; i<count; i++) {
105                 iov[i].iov_base = base + i * bsize;
106                 iov[i].iov_len  = length;
107         }
108         /* ... Write to the last block with offset ($bsize - $length) */
109         ret = pwritev(fd, iov, count, bsize * count - length);
110         if (ret == -1) {
111                 fprintf(stderr, "pwritev failed %s\n", strerror(errno));
112                 exit(1);
113         }
114
115         /* Verify data */
116         cmp_buf = malloc(length);
117         for (i=0; i<count; i++) {
118                 ret = pread(fd, cmp_buf, length, bsize * count + (i - 1) * length);
119                 if (ret == -1) {
120                         fprintf(stderr, "pread failed %s\n", strerror(errno));
121                         exit(1);
122                 }
123                 if (memcmp(buf, cmp_buf, length))
124                         printf("Find corruption\n");
125         }
126
127         munmap(base, bsize * count);
128         free(buf);
129         free(cmp_buf);
130         free(iov);
131         close(fd);
132
133         return 0;
134 }