1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2012 Jilin Xpd <jilinxpd@gmail.com>
  14  * Copyright 2018 Nexenta Systems, Inc.
  15  */
  16 
  17 /*
  18  * Test if file read/write is coherent with mmap, perform 2 tests:
  19  * modify file through mmap, and check the result through file read.
  20  * modify file through file write, and check the result through mmap.
  21  */
  22 
  23 #include <sys/mman.h>
  24 #include <sys/types.h>
  25 #include <sys/stat.h>
  26 #include <fcntl.h>
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <unistd.h>
  30 #include <string.h>
  31 #include <errno.h>
  32 
  33 void
  34 usage(void)
  35 {
  36         fprintf(stderr,
  37             "usage: rw_mmap -n <size>[b|k|m|g] -f <filename>\n");
  38         exit(1);
  39 }
  40 
  41 int
  42 main(int argc, char **argv)
  43 {
  44         char *suffix;
  45         char *filename = NULL;
  46         char *file_addr;
  47         char *p;
  48         size_t filesize;
  49         ssize_t blksize;
  50         size_t cnt = 1;
  51         size_t mul = 1;
  52         int c, fid;
  53         char *buf;
  54 
  55         /*
  56          * parse arguments
  57          */
  58         while ((c = getopt(argc, argv, "n:f:")) != -1) {
  59                 switch (c) {
  60                 case 'n':
  61                         cnt = (size_t)strtoul(optarg, &suffix, 0);
  62                         if (cnt == 0)
  63                                 goto bad_n_arg;
  64                         switch (*suffix) {
  65                         case '\0':
  66                         case 'b':
  67                                 mul = 1;
  68                                 break;
  69                         case 'k':
  70                                 mul = 1024;
  71                                 break;
  72                         case 'm':
  73                                 mul = (1024 * 1024);
  74                                 break;
  75                         case 'g':
  76                                 mul = (1024 * 1024 * 1024);
  77                                 break;
  78                         default:
  79                         bad_n_arg:
  80                                 fprintf(stderr, "-n %s: invalid size\n",
  81                                     optarg);
  82                                 return (1);
  83                         }
  84                         cnt = cnt * mul;
  85                         break;
  86 
  87                 case 'f': /* target file */
  88                         filename = optarg;
  89                         break;
  90 
  91                 case ':':   /* missing optarg */
  92                         fprintf(stderr,
  93                             "Option -%c requires an arg\n", optopt);
  94                         usage();
  95                         break;
  96                 case '?':
  97                         fprintf(stderr,
  98                             "Unrecognized option: -%c\n", optopt);
  99                         usage();
 100                         break;
 101                 }
 102         }
 103 
 104         /* open test file */
 105         fid = open(filename, O_RDWR | O_CREAT | O_TRUNC,
 106             S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
 107         if (fid == -1) {
 108                 fprintf(stderr, "open %s error=%d\n", filename, errno);
 109                 return (1);
 110         }
 111 
 112         /* extend file */
 113         filesize = cnt;
 114         if (ftruncate(fid, filesize) == -1) {
 115                 fprintf(stderr, "ftrunc %s error=%d\n", filename, errno);
 116                 return (1);
 117         }
 118 
 119         /* map file */
 120         file_addr = mmap(NULL, filesize,
 121             PROT_READ | PROT_WRITE, MAP_SHARED, fid, 0);
 122         if (file_addr == MAP_FAILED) {
 123                 fprintf(stderr, "mmap %s error=%d\n", filename, errno);
 124                 return (1);
 125         }
 126 
 127         blksize = 4096;
 128         buf = malloc(blksize);
 129         if (buf == NULL) {
 130                 fprintf(stderr, "malloc failed\n");
 131                 return (1);
 132         }
 133 
 134         /* verify fread and mmap see the same data */
 135         p = file_addr + 2013; /* not aligned to 4KB, on purpose */
 136         lseek(fid, 2013, SEEK_SET);
 137         while (p < file_addr + filesize) {
 138                 blksize = read(fid, buf, blksize);
 139                 if (blksize < 0) {
 140                         perror(filename);
 141                         return (1);
 142                 }
 143                 if (blksize == 0)
 144                         break;
 145                 if (memcmp(buf, p, blksize) != 0) {
 146                         fprintf(stderr, "memcmp failed 1\n");
 147                         return (1);
 148                 }
 149                 p += blksize;
 150         }
 151 
 152         /* modify file through mmap, verify fread can see the change */
 153         blksize = 4096;
 154         p = file_addr + 2013; /* not aligned to 4KB */
 155         lseek(fid, 2013, SEEK_SET);
 156         c = 'a';
 157         while (p < file_addr + filesize) {
 158                 if (p + blksize > file_addr + filesize)
 159                         blksize = file_addr + filesize - p;
 160                 memset(p, c++, blksize);
 161                 blksize = read(fid, buf, blksize);
 162                 if (blksize < 0) {
 163                         perror(filename);
 164                         return (1);
 165                 }
 166                 if (blksize == 0)
 167                         break;
 168                 if (memcmp(buf, p, blksize) != 0) {
 169                         fprintf(stderr, "memcmp failed 2\n");
 170                         return (1);
 171                 }
 172                 p += blksize;
 173         }
 174 
 175         /* modify file through fwrite, verify mmap can see the change */
 176         blksize = 4096;
 177         p = file_addr + 2013; /* not aligned to 4KB */
 178         lseek(fid, 2013, SEEK_SET);
 179         c = 'Z';
 180         while (p < file_addr + filesize) {
 181                 if (p + blksize > file_addr + filesize)
 182                         blksize = file_addr + filesize - p;
 183                 memset(buf, c--, blksize);
 184                 blksize = write(fid, buf, blksize);
 185                 if (blksize < 0) {
 186                         perror(filename);
 187                         return (1);
 188                 }
 189                 if (blksize == 0)
 190                         break;
 191                 if (memcmp(buf, p, blksize) != 0) {
 192                         fprintf(stderr, "memcmp failed 3\n");
 193                         return (1);
 194                 }
 195                 p += blksize;
 196         }
 197 
 198         /* sync pages to file */
 199         if (msync(file_addr, filesize, MS_SYNC) == -1) {
 200                 fprintf(stderr, "msync %s error=%d\n", filename, errno);
 201                 return (1);
 202         }
 203 
 204         /* unmap file */
 205         if (munmap(file_addr, filesize) == -1) {
 206                 fprintf(stderr, "munmap %s error=%d\n", filename, errno);
 207                 return (1);
 208         }
 209 
 210         /* close file */
 211         if (close(fid) == -1) {
 212                 fprintf(stderr, "close %s error=%d\n", filename, errno);
 213                 return (1);
 214         }
 215 
 216         return (0);
 217 }