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  * use mmap to copy data from src file to des file,
  19  * with given flags and modes.
  20  * the src & des file should exist and have the same size.
  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: "
  38             "prot_mmap -o <r|w> <r|w>"
  39             " -m <r|w|s|p> <r|w|s|p>"
  40             " -f <srcfile> <desfile>\n");
  41         exit(1);
  42 }
  43 
  44 int
  45 main(int argc, char **argv)
  46 {
  47         struct stat sb;
  48         char *src_addr, *des_addr;
  49         char *src_file = NULL, *des_file = NULL;
  50         off_t offset;
  51         size_t filesize;
  52         size_t blksize;
  53         size_t numblks;
  54         size_t i, j;
  55         int src_fid, des_fid;
  56         int mret = 0;
  57         int flags0 = 0, mflags0 = 0, prot0 = 0; /* flags for src file */
  58         int flags1 = 0, mflags1 = 0, prot1 = 0; /* flags for des file */
  59 
  60         /*
  61          * parse arguments
  62          * Not getopt because -o -m -f all have 2 optargs each.
  63          */
  64         if (argc != 10) {
  65                 usage();
  66         }
  67         for (i = 1; i < argc; ) {
  68                 switch (argv[i][1]) {
  69                         case 'o': /* options for open() */
  70                                 i++;
  71                                 for (j = 0; argv[i][j]; j++) {
  72                                         if (argv[i][j] == 'r')
  73                                                 flags0 |= O_RDONLY;
  74                                         else if (argv[i][j] == 'w')
  75                                                 flags0 |= O_WRONLY;
  76                                 }
  77                                 if ((flags0 & (O_RDONLY | O_WRONLY)) ==
  78                                     (O_RDONLY | O_WRONLY))
  79                                         flags0 = O_RDWR;
  80                                 i++;
  81                                 for (j = 0; argv[i][j]; j++) {
  82                                         if (argv[i][j] == 'r')
  83                                                 flags1 |= O_RDONLY;
  84                                         else if (argv[i][j] == 'w')
  85                                                 flags1 |= O_WRONLY;
  86                                 }
  87                                 if ((flags1 & (O_RDONLY | O_WRONLY)) ==
  88                                     (O_RDONLY | O_WRONLY))
  89                                         flags1 = O_RDWR;
  90                                 i++;
  91                                 break;
  92                         case 'm': /* options for mmap() */
  93                                 i++;
  94                                 for (j = 0; argv[i][j]; j++) {
  95                                         if (argv[i][j] == 'r')
  96                                                 prot0 |= PROT_READ;
  97                                         else if (argv[i][j] == 'w')
  98                                                 prot0 |= PROT_WRITE;
  99                                         else if (argv[i][j] == 's')
 100                                                 mflags0 |= MAP_SHARED;
 101                                         else if (argv[i][j] == 'p')
 102                                                 mflags0 |= MAP_PRIVATE;
 103                                 }
 104                                 i++;
 105                                 for (j = 0; argv[i][j]; j++) {
 106                                         if (argv[i][j] == 'r')
 107                                                 prot1 |= PROT_READ;
 108                                         else if (argv[i][j] == 'w')
 109                                                 prot1 |= PROT_WRITE;
 110                                         else if (argv[i][j] == 's')
 111                                                 mflags1 |= MAP_SHARED;
 112                                         else if (argv[i][j] == 'p')
 113                                                 mflags1 |= MAP_PRIVATE;
 114                                 }
 115                                 i++;
 116                                 break;
 117                         case 'f': /* src file and des file */
 118                                 i++;
 119                                 src_file = argv[i];
 120                                 i++;
 121                                 des_file = argv[i];
 122                                 i++;
 123                 }
 124         }
 125 
 126         /* source file */
 127         src_fid = open(src_file, flags0);
 128         if (src_fid == -1) {
 129                 fprintf(stderr, "open %s error=%d\n", src_file, errno);
 130                 return (1);
 131         }
 132         /* destination file */
 133         des_fid = open(des_file, flags1);
 134         if (des_fid == -1) {
 135                 fprintf(stderr, "open %s error=%d\n", des_file, errno);
 136                 mret = 1;
 137                 goto exit3;
 138         }
 139 
 140         /* get file size */
 141         if (fstat(src_fid, &sb) == -1) {
 142                 fprintf(stderr, "fstat %s error=%d\n", src_file, errno);
 143                 mret = 1;
 144                 goto exit2;
 145         }
 146         filesize = sb.st_size;
 147         if (filesize < 4096) {
 148                 fprintf(stderr, "file too small\n");
 149                 mret = 1;
 150                 goto exit2;
 151         }
 152 
 153         if (fstat(des_fid, &sb) == -1) {
 154                 fprintf(stderr, "fstat %s error=%d\n", des_file, errno);
 155                 mret = 1;
 156                 goto exit2;
 157         }
 158         if (filesize != sb.st_size) {
 159                 fprintf(stderr, "file sizes differ\n");
 160                 mret = 1;
 161                 goto exit2;
 162         }
 163 
 164         /* copy data */
 165         blksize = 64 * 1024 * 1024;
 166         numblks = (filesize + blksize - 1) / blksize;
 167         for (i = 0; i < numblks && mret == 0; i++) {
 168 
 169                 offset = (i % numblks) * blksize;
 170                 if (offset + blksize > filesize)
 171                         blksize = filesize - offset;
 172 
 173                 /* map file */
 174                 src_addr = mmap(NULL, blksize, prot0, mflags0, src_fid, offset);
 175                 if (src_addr == MAP_FAILED) {
 176                         fprintf(stderr, "mmap %s error=%d\n", src_file, errno);
 177                         mret = 1;
 178                         break;
 179                 }
 180                 des_addr = mmap(NULL, blksize, prot1, mflags1, des_fid, offset);
 181                 if (des_addr == MAP_FAILED) {
 182                         fprintf(stderr, "mmap %s error=%d\n", des_file, errno);
 183                         mret = 1;
 184                         goto exit1;
 185                 }
 186 
 187                 /* cp data from src addr to des addr */
 188                 memcpy(des_addr, src_addr, blksize);
 189                 /* sync mapped pages to file */
 190                 if (msync(des_addr, blksize, MS_SYNC) == -1) {
 191                         fprintf(stderr, "msync %s error=%d\n", des_file, errno);
 192                         mret = 1;
 193                 }
 194 
 195                 /* unmap file */
 196                 if (munmap(des_addr, blksize) == -1) {
 197                         fprintf(stderr, "munmap %s error=%d\n",
 198                             des_file, errno);
 199                         mret = 1;
 200                 }
 201 exit1:
 202                 if (munmap(src_addr, blksize) == -1) {
 203                         fprintf(stderr, "munmap %s error=%d\n",
 204                             src_file, errno);
 205                         mret = 1;
 206                 }
 207         }
 208 
 209         /* close file */
 210 exit2:
 211         close(des_fid);
 212 exit3:
 213         close(src_fid);
 214 
 215         return (mret);
 216 }