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  * Copy a file from src to dest, using mmap to copy the data,
  19  * with either contiguous or discontiguous mappings.
  20  * (Jilin calls discontiguous "discrete" below.)
  21  */
  22 
  23 #include <sys/mman.h>
  24 #include <sys/types.h>
  25 #include <sys/stat.h>
  26 #include <limits.h>
  27 #include <fcntl.h>
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #include <string.h>
  32 #include <errno.h>
  33 
  34 void
  35 usage(void)
  36 {
  37         fprintf(stderr,
  38             "usage: cp_mmap -t {d|c} -f <srcfile> <desfile>\n");
  39         exit(1);
  40 }
  41 
  42 int
  43 main(int argc, char **argv)
  44 {
  45         struct stat sb;
  46         char *src_addr, *des_addr;
  47         char *src_file = NULL, *des_file = NULL;
  48         off_t offset;
  49         size_t filesize;
  50         size_t blksize;
  51         size_t pagesize;
  52         size_t len;
  53         size_t numblks;
  54         int src_fid, des_fid;
  55         int mret = 0;
  56         size_t i;
  57         size_t stride;
  58         boolean_t discrete = B_FALSE; /* discontiguous mappings */
  59 
  60         /*
  61          * parse arguments
  62          * Not getopt because -f has two optargs
  63          */
  64         if (argc != 6)
  65                 usage();
  66 
  67         for (i = 1; i < argc; ) {
  68                 switch (argv[i][1]) {
  69                 case 't': /* copy type */
  70                         i++;
  71                         discrete = (argv[i][0] == 'd');
  72                         i++;
  73                         break;
  74                 case 'f': /* src file and des file */
  75                         i++;
  76                         src_file = argv[i];
  77                         i++;
  78                         des_file = argv[i];
  79                         i++;
  80                         break;
  81                 default:
  82                         usage();
  83                         break;
  84                 }
  85         }
  86 
  87         pagesize = sysconf(_SC_PAGESIZE); /* mmap one page each time */
  88         if (pagesize < 4096) {
  89                 fprintf(stderr, "sysconf error=%d\n", errno);
  90                 return (1);
  91         }
  92         if (discrete) {
  93                 /*
  94                  * Use discontiguous mappings, and only mmap
  95                  * one page each time
  96                  */
  97                 blksize = pagesize;
  98                 stride = 3;
  99         } else {
 100                 /* will do contiguous mmap */
 101                 blksize = 64 * 1024 * 1024; /* mmap a block each time */
 102                 stride = 1;
 103         }
 104 
 105         /* source file */
 106         src_fid = open(src_file, O_RDONLY);
 107         if (src_fid == -1) {
 108                 fprintf(stderr, "open %s error=%d\n", src_file, errno);
 109                 return (1);
 110         }
 111         /* destination file */
 112         des_fid = open(des_file, O_RDWR | O_CREAT | O_TRUNC,
 113             S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
 114         if (des_fid == -1) {
 115                 fprintf(stderr, "open %s error=%d\n", des_file, errno);
 116                 mret = 1;
 117                 goto exit3;
 118         }
 119 
 120         /* get src file size */
 121         if (fstat(src_fid, &sb) == -1) {
 122                 fprintf(stderr, "fstat %s error=%d\n", src_file, errno);
 123                 mret = 1;
 124                 goto exit2;
 125         }
 126         filesize = sb.st_size;
 127         if (filesize < pagesize) {
 128                 fprintf(stderr, "src file size < %d\n", (int)pagesize);
 129                 mret = 1;
 130                 goto exit2;
 131         }
 132 
 133         /* extend des file */
 134         if (ftruncate(des_fid, filesize) == -1) {
 135                 fprintf(stderr, "ftrunc %s error=%d\n", des_file, errno);
 136                 mret = 1;
 137                 goto exit2;
 138         }
 139 
 140         /* copy data */
 141         numblks = (filesize + blksize - 1) / blksize;
 142         for (i = 0; i < stride * numblks && mret == 0; i += stride) {
 143 
 144                 offset = (i % numblks) * blksize;
 145                 if (offset + blksize > filesize)
 146                         len = filesize - offset;
 147                 else
 148                         len = blksize;
 149 
 150                 /* map file */
 151                 src_addr = mmap(NULL, len, PROT_READ, MAP_SHARED,
 152                     src_fid, offset);
 153                 if (src_addr == MAP_FAILED) {
 154                         fprintf(stderr, "mmap %s error=%d\n", src_file, errno);
 155                         mret = 1;
 156                         break;
 157                 }
 158                 des_addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED,
 159                     des_fid, offset);
 160                 if (des_addr == MAP_FAILED) {
 161                         fprintf(stderr, "mmap %s error=%d\n", des_file, errno);
 162                         mret = 1;
 163                         goto exit1;
 164                 }
 165 
 166                 /* cp data from src addr to des addr */
 167                 memcpy(des_addr, src_addr, len);
 168                 /* sync mapped pages to file */
 169                 if (msync(des_addr, len, MS_SYNC) == -1) {
 170                         fprintf(stderr, "msync %s error=%d\n", des_file, errno);
 171                         mret = 1;
 172                 }
 173 
 174                 /* unmap file */
 175                 if (munmap(des_addr, len) == -1) {
 176                         fprintf(stderr, "munmap %s error=%d\n",
 177                             des_file, errno);
 178                         mret = 1;
 179                 }
 180         exit1:
 181                 if (munmap(src_addr, len) == -1) {
 182                         fprintf(stderr, "munmap %s error=%d\n",
 183                             src_file, errno);
 184                         mret = 1;
 185                 }
 186         }
 187 
 188 exit2:
 189         close(des_fid);
 190 exit3:
 191         close(src_fid);
 192 
 193         return (mret);
 194 }