generic: test reflinked file corruption after short COW
[xfstests-dev.git] / src / mmap-write-concurrent.c
1 // SPDX-License-Identifier: GPL-2.0-or-newer
2 /*
3  * Copyright (c) 2019 Oracle.
4  * All Rights Reserved.
5  *
6  * Create writable mappings to multiple files and write them all to test
7  * concurrent mmap writes to the same shared blocks.
8  */
9 #include <sys/mman.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 struct file_info {
20         char                    *mapping;
21         off_t                   file_offset;
22         off_t                   file_length;
23         int                     fd;
24 };
25
26 int
27 main(
28         int                     argc,
29         char                    *argv[])
30 {
31         struct file_info        *fi;
32         size_t                  length;
33         char                    *endptr;
34         unsigned int            nr_files;
35         unsigned int            i;
36         char                    *buf;
37         int                     ret;
38
39         if (argc < 4) {
40                 printf("Usage: %s len offset file [offset file]...\n", argv[0]);
41                 return 1;
42         }
43
44         /* Parse mwrite length. */
45         errno = 0;
46         length = strtoul(argv[1], &endptr, 0);
47         if (errno) {
48                 perror(argv[1]);
49                 return 1;
50         }
51         if (*endptr != '\0') {
52                 fprintf(stderr, "%s: not a proper file length?\n", argv[2]);
53                 return 1;
54         }
55
56         /* Allocate file info */
57         nr_files = (argc - 2) / 2;
58
59         fi = calloc(nr_files, sizeof(struct file_info));
60         if (!fi) {
61                 perror("calloc file info");
62                 return 1;
63         }
64
65         buf = malloc(length);
66         if (!buf) {
67                 perror("malloc buf");
68                 return 1;
69         }
70
71         for (i = 0; i < nr_files; i++) {
72                 struct stat     statbuf;
73                 char            *offset = argv[((i + 1) * 2)];
74                 char            *fname = argv[((i + 1) * 2) + 1];
75
76                 /* Open file, create mapping for the range we want. */
77                 fi[i].fd = open(fname, O_RDWR);
78                 if (fi[i].fd < 0) {
79                         perror(fname);
80                         return 1;
81                 }
82
83                 /* Parse mwrite offset */
84                 errno = 0;
85                 fi[i].file_offset = strtoul(offset, &endptr, 0);
86                 if (errno) {
87                         perror(argv[1]);
88                         return 1;
89                 }
90
91                 /* Remember file size */
92                 ret = fstat(fi[i].fd, &statbuf);
93                 if (ret) {
94                         perror(fname);
95                         return 1;
96                 }
97                 fi[i].file_length = statbuf.st_size;
98
99                 if (fi[i].file_offset + length > fi[i].file_length) {
100                         fprintf(stderr, "%s: file must be %llu bytes\n",
101                                 fname,
102                                 (unsigned long long)fi[i].file_offset + length);
103                         return 1;
104                 }
105
106                 /* Create the mapping */
107                 fi[i].mapping = mmap(NULL, fi[i].file_length,
108                                 PROT_READ | PROT_WRITE, MAP_SHARED,
109                                 fi[i].fd, 0);
110                 if (fi[i].mapping == MAP_FAILED) {
111                         perror(fname);
112                         return 1;
113                 }
114
115                 /*
116                  * Make sure the mapping for region we're going to write is
117                  * already populated in the page cache.
118                  */
119                 memcpy(buf, fi[i].mapping + fi[i].file_offset, length);
120         }
121
122         /* Dirty the same region in each file to test COW. */
123         for (i = 0; i < nr_files; i++) {
124                 memset(buf, 0x62 + i, length);
125                 memcpy(fi[i].mapping + fi[i].file_offset, buf, length);
126         }
127         for (i = 0; i < nr_files; i++) {
128                 ret = msync(fi[i].mapping, fi[i].file_offset + length, MS_SYNC);
129                 if (ret) {
130                         perror("msync");
131                         return 1;
132                 }
133         }
134
135         /* Close everything. */
136         for (i = 0; i < nr_files; i++) {
137                 ret = munmap(fi[i].mapping, fi[i].file_length);
138                 if (ret) {
139                         perror("munmap");
140                         return 1;
141                 }
142
143                 ret = close(fi[i].fd);
144                 if (ret) {
145                         perror("close");
146                         return 1;
147                 }
148         }
149
150         /* Free everything. */
151         free(buf);
152         free(fi);
153
154         return 0;
155 }