File: mmap.cpp

package info (click to toggle)
mrtrix3 3.0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,712 kB
  • sloc: cpp: 129,776; python: 9,494; sh: 593; makefile: 234; xml: 47
file content (246 lines) | stat: -rw-r--r-- 8,109 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/* Copyright (c) 2008-2022 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include <fcntl.h>
#include <unistd.h>
#include <zlib.h>

#ifdef MRTRIX_MACOSX
# include <sys/param.h>
# include <sys/mount.h>
#elif !defined(MRTRIX_WINDOWS)
# include <sys/vfs.h> 
#endif

#ifdef MRTRIX_WINDOWS
#include <windows.h>
#else
#include <sys/mman.h>
#endif

#include "app.h"
#include "file/ofstream.h"
#include "file/path.h"
#include "file/mmap.h"
#include "file/config.h"

#include "debug.h"

namespace MR
{
  namespace File
  {

    MMap::MMap (const Entry& entry, bool readwrite, bool preload, int64_t mapped_size) :
      Entry (entry), addr (NULL), first (NULL), msize (mapped_size), readwrite (readwrite)
    {
      DEBUG ("memory-mapping file \"" + Entry::name + "\"...");

      struct stat sbuf;
      if (stat (Entry::name.c_str(), &sbuf))
        throw Exception ("cannot stat file \"" + Entry::name + "\": " + strerror (errno));

      mtime = sbuf.st_mtime;


      if (msize < 0) 
        msize = sbuf.st_size - start;
      else if (start + msize > sbuf.st_size) 
        throw Exception ("file \"" + Entry::name + "\" is smaller than expected");

      bool delayed_writeback = false;
      if (readwrite) {

#ifdef MRTRIX_WINDOWS
        const unsigned int length = 255;
        char root_path[length];
        if (GetVolumePathName (Entry::name.c_str(), root_path, length)) { // Returns non-zero on success

          const unsigned int code = GetDriveType (root_path);
          switch (code) {
            case 0: // DRIVE_UNKNOWN
              DEBUG ("cannot get filesystem information on file \"" + Entry::name + "\": " + strerror (errno));
              DEBUG ("  defaulting to delayed write-back");
              delayed_writeback = true;
              break;
            case 1: // DRIVE_NO_ROOT_DIR:
              DEBUG ("erroneous root path derived for file \"" + Entry::name + "\": " + strerror (errno));
              DEBUG ("  defaulting to delayed write-back");
              delayed_writeback = true;
              break;
            case 2: // DRIVE_REMOVABLE
              DEBUG ("Drive for file \"" + Entry::name + "\" detected as removable; using memory-mapping");
              break;
            case 3: // DRIVE_FIXED
              DEBUG ("Drive for file \"" + Entry::name + "\" detected as fixed; using memory-mapping");
              break;
            case 4: // DRIVE_REMOTE
              DEBUG ("Drive for file \"" + Entry::name + "\" detected as network - using delayed write-back");
              delayed_writeback = true;
              break;
            case 5: // DRIVE_CDROM
              DEBUG ("Drive for file \"" + Entry::name + "\" detected as CD-ROM - using delayed write-back");
              delayed_writeback = true;
              break;
            case 6: // DRIVE_RAMDISK
              DEBUG ("Drive for file \"" + Entry::name + "\" detected as RAM - using memory-mapping");
              break;
          }

        } else {
          DEBUG ("unable to query root drive path for file \"" + Entry::name + "\"; using delayed write-back");
          delayed_writeback = true;
        }
#else
        struct statfs fsbuf;
        if (statfs (Entry::name.c_str(), &fsbuf)) {
          DEBUG ("cannot get filesystem information on file \"" + Entry::name + "\": " + strerror (errno));
          DEBUG ("  defaulting to delayed write-back");
          delayed_writeback = true;
        }

        if (fsbuf.f_type == 0xff534d42 /* CIFS */|| fsbuf.f_type == 0x6969 /* NFS */ || 
            fsbuf.f_type == 0x65735546 /* FUSE */ || fsbuf.f_type == 0x517b /* SMB */ || 
            fsbuf.f_type == 0x47504653 /* GPFS */ || fsbuf.f_type == 0xbd00bd0 /* LUSTRE */

#ifdef MRTRIX_MACOSX
            || fsbuf.f_type == 0x0017 /* OSXFUSE */
#endif 
        ) {
          DEBUG ("\"" + Entry::name + "\" appears to reside on a networked filesystem - using delayed write-back");
          delayed_writeback = true;
        }
#endif

        if (delayed_writeback) {
          try {
            first = new uint8_t [msize];
            if (!first) throw 1;
          }
          catch (...) {
            throw Exception ("error allocating memory to hold mmap buffer contents");
          }

          if (preload) {
            CONSOLE ("preloading contents of mapped file \"" + Entry::name + "\"...");
            std::ifstream in (Entry::name.c_str(), std::ios::in | std::ios::binary);
            if (!in) 
              throw Exception ("failed to open file \"" + Entry::name + "\": " + strerror (errno));
            in.seekg (start, in.beg);
            in.read ((char*) first, msize);
            if (!in.good())
              throw Exception ("error preloading contents of file \"" + Entry::name + "\": " + strerror(errno));
          }
          else 
            memset (first, 0, msize);
          DEBUG ("file \"" + Entry::name + "\" held in RAM at " + str ( (void*) first) + ", size " + str (msize));

          return;
        }
      }

      // use regular memory-mapping:

      if ( (fd = open (Entry::name.c_str(), ( readwrite ? O_RDWR : O_RDONLY ), 0666)) < 0)
        throw Exception ("error opening file \"" + Entry::name + "\": " + strerror (errno));

      try {
#ifdef MRTRIX_WINDOWS
        HANDLE handle = CreateFileMapping ( (HANDLE) _get_osfhandle (fd), NULL,
            ( readwrite ? PAGE_READWRITE : PAGE_READONLY ), 0, start + msize, NULL);
        if (!handle) throw 0;
        addr = static_cast<uint8_t*> (MapViewOfFile (handle, ( readwrite ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ ), 0, 0, start + msize));
        if (!addr) throw 0;
        CloseHandle (handle);
#else
        addr = static_cast<uint8_t*> (mmap ( (char*) 0, start + msize,
              ( readwrite ? PROT_WRITE | PROT_READ : PROT_READ ), MAP_SHARED, fd, 0));
        if (addr == MAP_FAILED) throw 0;
#endif
      }
      catch (...) {
        close (fd);
        addr = NULL;
        throw Exception ("memory-mapping failed for file \"" + Entry::name + "\": " + strerror (errno));
      }
      first = addr + start;

      DEBUG ("file \"" + Entry::name + "\" mapped at " + str ( (void*) addr) + ", size " + str (msize)
          + " (read-" + (readwrite ? "write" : "only") + ")");
    }





    MMap::~MMap()
    {
      if (!first) return;
      if (addr) {
        DEBUG ("unmapping file \"" + Entry::name + "\"");
#ifdef MRTRIX_WINDOWS
        if (!UnmapViewOfFile ( (LPVOID) addr))
#else
          if (munmap (addr, msize))
#endif
            WARN ("error unmapping file \"" + Entry::name + "\": " + strerror (errno));
        close (fd);
      }
      else {
        if (readwrite) {
          INFO ("writing back contents of mapped file \"" + Entry::name + "\"...");
          try {
            File::OFStream out (Entry::name, std::ios::in | std::ios::out | std::ios::binary);
            out.seekp (start, out.beg);
            out.write ((char*) first, msize);
            if (!out.good())
              throw 1;
          }
          catch (...) {
            FAIL ("error writing back contents of file \"" + Entry::name + "\": " + strerror(errno));
            App::exit_error_code = 1;
          }

        }
        delete [] first;
      }
    }






    bool MMap::changed () const
    {
      assert (fd >= 0);
      struct stat sbuf;
      if (fstat (fd, &sbuf)) 
        return false;
      if (int64_t (msize) != sbuf.st_size) 
        return true;
      if (mtime != sbuf.st_mtime) 
        return true;
      return false;
    }




  }
}