1 /*
2 * HLLib
3 * Copyright (C) 2006-2010 Ryan Gregg
4
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later
9 * version.
10 */
11
12 #include "HLLib.h"
13 #include "FileMapping.h"
14
15 using namespace HLLib;
16 using namespace HLLib::Mapping;
17
18 #ifdef _WIN32
CFileMapping(const hlChar * lpFileName)19 CFileMapping::CFileMapping(const hlChar *lpFileName) : hFile(0), hFileMapping(0), uiMode(HL_MODE_INVALID), lpView(0), uiViewSize(0)
20 #else
21 CFileMapping::CFileMapping(const hlChar *lpFileName) : iFile(-1), uiMode(HL_MODE_INVALID), lpView(0), uiViewSize(0)
22 #endif
23 {
24 this->lpFileName = new hlChar[strlen(lpFileName) + 1];
25 strcpy(this->lpFileName, lpFileName);
26
27 #ifdef _WIN32
28 SYSTEM_INFO SystemInfo;
29 GetSystemInfo(&SystemInfo);
30
31 this->uiAllocationGranularity = SystemInfo.dwAllocationGranularity;
32 #else
33 this->uiAllocationGranularity = static_cast<hlUInt>(getpagesize());
34 #endif
35 }
36
~CFileMapping()37 CFileMapping::~CFileMapping()
38 {
39 this->Close();
40
41 delete []this->lpFileName;
42 }
43
GetType() const44 HLMappingType CFileMapping::GetType() const
45 {
46 return HL_MAPPING_FILE;
47 }
48
GetFileName() const49 const hlChar *CFileMapping::GetFileName() const
50 {
51 return this->lpFileName;
52 }
53
GetOpened() const54 hlBool CFileMapping::GetOpened() const
55 {
56 #ifdef _WIN32
57 return this->hFile != 0;
58 #else
59 return this->iFile >= 0;
60 #endif
61 }
62
GetMode() const63 hlUInt CFileMapping::GetMode() const
64 {
65 return this->uiMode;
66 }
67
OpenInternal(hlUInt uiMode)68 hlBool CFileMapping::OpenInternal(hlUInt uiMode)
69 {
70 assert(!this->GetOpened());
71
72 #ifdef _WIN32
73 DWORD dwDesiredAccess = ((uiMode & HL_MODE_READ) ? GENERIC_READ : 0) | ((uiMode & HL_MODE_WRITE) ? GENERIC_WRITE : 0);
74 DWORD dwShareMode = (uiMode & HL_MODE_VOLATILE) ? FILE_SHARE_READ | FILE_SHARE_WRITE : ((uiMode & HL_MODE_READ) && !(uiMode & HL_MODE_WRITE) ? FILE_SHARE_READ : 0);
75 DWORD dwCreationDisposition = (uiMode & HL_MODE_WRITE) && (uiMode & HL_MODE_CREATE) ? (bOverwriteFiles ? CREATE_ALWAYS : CREATE_NEW) : ((uiMode & HL_MODE_READ) || (uiMode & HL_MODE_WRITE) ? OPEN_EXISTING : 0);
76
77 if(dwDesiredAccess == 0 || dwCreationDisposition == 0)
78 {
79 LastError.SetErrorMessageFormated("Invalid open mode (%#.8x).", uiMode);
80
81 return hlFalse;
82 }
83
84 this->hFile = CreateFile(this->lpFileName, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
85
86 if(this->hFile == INVALID_HANDLE_VALUE)
87 {
88 LastError.SetSystemErrorMessage("Error opening file.");
89
90 this->hFile = 0;
91 return hlFalse;
92 }
93
94 DWORD dwProtectionMode = (uiMode & HL_MODE_WRITE) ? PAGE_READWRITE : ((uiMode & HL_MODE_READ) ? PAGE_READONLY : 0);
95
96 this->hFileMapping = CreateFileMapping(this->hFile, NULL, dwProtectionMode, 0, 0, NULL);
97
98 if(this->hFileMapping == 0)
99 {
100 LastError.SetSystemErrorMessage("Failed to create file mapping object for file.");
101
102 return hlFalse;
103 }
104
105 // Map the whole file to memory then pass pointers to the
106 // master view back instead of mapping smaller views.
107 if(uiMode & HL_MODE_QUICK_FILEMAPPING)
108 {
109 LARGE_INTEGER liFileSize;
110 #ifdef _WIN64
111 if(GetFileSizeEx(this->hFile, &liFileSize))
112 #else
113 if(GetFileSizeEx(this->hFile, &liFileSize) && liFileSize.HighPart == 0)
114 #endif
115 {
116 DWORD dwDesiredAccess = ((uiMode & HL_MODE_READ) ? FILE_MAP_READ : 0) | ((uiMode & HL_MODE_WRITE) ? FILE_MAP_WRITE : 0);
117
118 this->uiViewSize = static_cast<hlULongLong>(liFileSize.QuadPart);
119 this->lpView = MapViewOfFile(this->hFileMapping, dwDesiredAccess, 0, 0, static_cast<SIZE_T>(this->uiViewSize));
120
121 if(this->lpView == 0)
122 {
123 LastError.SetSystemErrorMessage("Failed to map view of file. Try disabling quick file mapping.");
124
125 return hlFalse;
126 }
127 }
128 else
129 {
130 // Cannot map more than 4 GB on x86.
131 uiMode &= ~HL_MODE_QUICK_FILEMAPPING;
132 }
133 }
134 #else
135 hlInt iMode;
136
137 if((uiMode & HL_MODE_READ) && (uiMode & HL_MODE_WRITE))
138 {
139 iMode = O_RDWR;
140 }
141 else if(uiMode & HL_MODE_READ)
142 {
143 iMode = O_RDONLY;
144 }
145 else if(uiMode & HL_MODE_WRITE)
146 {
147 iMode = O_WRONLY;
148 }
149
150 if((uiMode & HL_MODE_WRITE) && (uiMode & HL_MODE_CREATE))
151 {
152 iMode |= bOverwriteFiles ? O_CREAT | O_TRUNC : O_CREAT | O_EXCL;
153 }
154
155 if((uiMode & (HL_MODE_READ | HL_MODE_WRITE)) == 0)
156 {
157 LastError.SetErrorMessageFormated("Invalid open mode (%#.8x).", uiMode);
158
159 return hlFalse;
160 }
161
162 this->iFile = open(this->lpFileName, iMode | O_BINARY | O_RANDOM, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
163
164 if(this->iFile < 0)
165 {
166 LastError.SetSystemErrorMessage("Error opening file.");
167
168 this->iFile = -1;
169 return hlFalse;
170 }
171
172 // Map the whole file to memory then pass pointers to the
173 // master view back instead of mapping smaller views.
174 if(uiMode & HL_MODE_QUICK_FILEMAPPING)
175 {
176 hlInt iProtection = ((uiMode & HL_MODE_READ) ? PROT_READ : 0) | ((uiMode & HL_MODE_WRITE) ? PROT_WRITE : 0);
177
178 struct stat Stat;
179
180 if(fstat(this->iFile, &Stat) < 0)
181 {
182 LastError.SetSystemErrorMessage("Error retrieving file size.");
183
184 return hlFalse;
185 }
186
187 this->uiViewSize = (hlUInt)Stat.st_size;
188
189 this->lpView = mmap(0, this->uiViewSize, iProtection, MAP_SHARED, this->iFile, 0);
190
191 if(this->lpView == MAP_FAILED)
192 {
193 LastError.SetSystemErrorMessage("Failed to map view of file. Try disabling quick file mapping.");
194
195 this->lpView = 0;
196 return hlFalse;
197 }
198 }
199 #endif
200
201 this->uiMode = uiMode;
202
203 return hlTrue;
204 }
205
CloseInternal()206 hlVoid CFileMapping::CloseInternal()
207 {
208 #ifdef _WIN32
209 if(this->lpView != 0)
210 {
211 UnmapViewOfFile(this->lpView);
212 this->lpView = 0;
213 }
214
215 this->uiViewSize = 0;
216
217 if(this->hFileMapping != 0)
218 {
219 CloseHandle(this->hFileMapping);
220 this->hFileMapping = 0;
221 }
222
223 if(this->hFile != 0)
224 {
225 CloseHandle(this->hFile);
226 this->hFile = 0;
227 }
228 #else
229 if(this->lpView != 0)
230 {
231 munmap(this->lpView, this->uiViewSize);
232 this->lpView = 0;
233 }
234
235 this->uiViewSize = 0;
236
237 if(this->iFile >= 0)
238 {
239 close(this->iFile);
240 this->iFile = -1;
241 }
242 #endif
243 }
244
GetMappingSize() const245 hlULongLong CFileMapping::GetMappingSize() const
246 {
247 if(!this->GetOpened())
248 {
249 return 0;
250 }
251
252 #ifdef _WIN32
253 LARGE_INTEGER liFileSize;
254 return GetFileSizeEx(this->hFile, &liFileSize) ? static_cast<hlULongLong>(liFileSize.QuadPart) : 0;
255 #else
256 struct stat Stat;
257
258 return fstat(this->iFile, &Stat) < 0 ? 0 : Stat.st_size;
259 #endif
260 }
261
MapInternal(CView * & pView,hlULongLong uiOffset,hlULongLong uiLength)262 hlBool CFileMapping::MapInternal(CView *&pView, hlULongLong uiOffset, hlULongLong uiLength)
263 {
264 assert(this->GetOpened());
265
266 if(this->lpView != 0)
267 {
268 if(uiOffset + uiLength > this->uiViewSize)
269 {
270 #ifdef _WIN32
271 LastError.SetErrorMessageFormated("Requested view (%I64u, %I64u) does not fit inside mapping, (%I64u, %I64u).", uiOffset, uiLength, 0ULL, this->uiViewSize);
272 #else
273 LastError.SetErrorMessageFormated("Requested view (%llu, %llu) does not fit inside mapping, (%llu, %llu).", uiOffset, uiLength, 0ULL, this->uiViewSize);
274 #endif
275 return hlFalse;
276 }
277
278 pView = new CView(this, (hlByte *)this->lpView, 0, this->uiViewSize, uiOffset, uiLength);
279 }
280 else
281 {
282 hlULongLong uiMappingSize = this->GetMappingSize();
283
284 if(uiOffset + uiLength > uiMappingSize)
285 {
286 #ifdef _WIN32
287 LastError.SetErrorMessageFormated("Requested view (%I64u, %I64u) does not fit inside mapping, (%I64u, %I64u).", uiOffset, uiLength, 0ULL, this->uiViewSize);
288 #else
289 LastError.SetErrorMessageFormated("Requested view (%llu, %llu) does not fit inside mapping, (%llu, %llu).", uiOffset, uiLength, 0ULL, this->uiViewSize);
290 #endif
291 return hlFalse;
292 }
293
294 // Map multiples of the allocation granularity from the nearest allocation granularity
295 // (for performance).
296 hlULongLong uiGrainOffset = uiOffset % static_cast<hlULongLong>(this->uiAllocationGranularity);
297 hlULongLong uiFileOffset = uiOffset - uiGrainOffset;
298 hlULongLong uiFileLength = (((uiLength + uiGrainOffset + static_cast<hlULongLong>(this->uiAllocationGranularity) - 1) / static_cast<hlULongLong>(this->uiAllocationGranularity)) * static_cast<hlULongLong>(this->uiAllocationGranularity));
299
300 if(uiFileOffset + uiFileLength > uiMappingSize)
301 {
302 uiFileLength = uiMappingSize - uiFileOffset;
303 }
304
305 #ifdef _WIN32
306 DWORD dwDesiredAccess = ((uiMode & HL_MODE_READ) ? FILE_MAP_READ : 0) | ((uiMode & HL_MODE_WRITE) ? FILE_MAP_WRITE : 0);
307
308 hlVoid *lpView = MapViewOfFile(this->hFileMapping, dwDesiredAccess, static_cast<DWORD>(uiFileOffset >> 32), static_cast<DWORD>(uiFileOffset), static_cast<SIZE_T>(uiFileLength));
309
310 if(lpView == 0)
311 {
312 LastError.SetSystemErrorMessage("Failed to map view of file. Try disabling file mapping.");
313 return hlFalse;
314 }
315 #else
316 hlInt iProtection = ((uiMode & HL_MODE_READ) ? PROT_READ : 0) | ((uiMode & HL_MODE_WRITE) ? PROT_WRITE : 0);
317
318 hlVoid *lpView = mmap(0, uiFileLength, iProtection, MAP_SHARED, this->iFile, uiFileOffset);
319
320 if(this->lpView == MAP_FAILED)
321 {
322 LastError.SetSystemErrorMessage("Failed to map view of file. Try disabling file mapping.");
323 return hlFalse;
324 }
325 #endif
326
327 pView = new CView(this, lpView, uiFileOffset, uiFileLength, uiGrainOffset, uiLength);
328 }
329
330 return hlTrue;
331 }
332
UnmapInternal(CView & View)333 hlVoid CFileMapping::UnmapInternal(CView &View)
334 {
335 assert(this->GetOpened());
336 assert(View.GetMapping() == this);
337
338 if(this->lpView == 0)
339 {
340 #ifdef _WIN32
341 UnmapViewOfFile((hlVoid *)View.GetAllocationView());
342 #else
343 munmap((hlVoid *)View.GetAllocationView(), View.GetAllocationLength());
344 #endif
345 }
346 }
347