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