1 /*-------------------------------------------------------------------------
2  * drawElements Utility Library
3  * ----------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief File abstraction.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "deFile.h"
25 #include "deMemory.h"
26 
27 #if (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS) || (DE_OS == DE_OS_ANDROID) || (DE_OS == DE_OS_SYMBIAN)
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <errno.h>
34 
35 struct deFile_s
36 {
37 	int fd;
38 };
39 
deFileExists(const char * filename)40 deBool deFileExists (const char* filename)
41 {
42 	struct stat st;
43 	int result = stat(filename, &st);
44 	return result == 0;
45 }
46 
deDeleteFile(const char * filename)47 deBool deDeleteFile (const char* filename)
48 {
49 	return unlink(filename) == 0;
50 }
51 
deFile_createFromHandle(deUintptr handle)52 deFile* deFile_createFromHandle (deUintptr handle)
53 {
54 	int		fd		= (int)handle;
55 	deFile* file	= (deFile*)deCalloc(sizeof(deFile));
56 	if (!file)
57 	{
58 		close(fd);
59 		return file;
60 	}
61 
62 	file->fd = fd;
63 	return file;
64 }
65 
mapOpenMode(deFileMode mode)66 static int mapOpenMode (deFileMode mode)
67 {
68 	int flag = 0;
69 
70 	/* Read, write or read and write access is required. */
71 	DE_ASSERT((mode & DE_FILEMODE_READ) != 0 || ((mode & DE_FILEMODE_WRITE) != 0));
72 
73 	/* Create, open or create and open mode is required. */
74 	DE_ASSERT((mode & DE_FILEMODE_OPEN) != 0 || ((mode & DE_FILEMODE_CREATE) != 0));
75 
76 	/* Require write when using create. */
77 	DE_ASSERT(!(mode & DE_FILEMODE_CREATE) || (mode & DE_FILEMODE_WRITE));
78 
79 	/* Require write and open when using truncate */
80 	DE_ASSERT(!(mode & DE_FILEMODE_TRUNCATE) || ((mode & DE_FILEMODE_WRITE) && (mode & DE_FILEMODE_OPEN)));
81 
82 	if (mode & DE_FILEMODE_READ)
83 		flag |= O_RDONLY;
84 
85 	if (mode & DE_FILEMODE_WRITE)
86 		flag |= O_WRONLY;
87 
88 	if (mode & DE_FILEMODE_TRUNCATE)
89 		flag |= O_TRUNC;
90 
91 	if (mode & DE_FILEMODE_CREATE)
92 		flag |= O_CREAT;
93 
94 	if (!(mode & DE_FILEMODE_OPEN))
95 		flag |= O_EXCL;
96 
97 	return flag;
98 }
99 
deFile_create(const char * filename,deUint32 mode)100 deFile* deFile_create (const char* filename, deUint32 mode)
101 {
102 	int fd = open(filename, mapOpenMode(mode), 0777);
103 	if (fd >= 0)
104 		return deFile_createFromHandle((deUintptr)fd);
105 	else
106 		return DE_NULL;
107 }
108 
deFile_destroy(deFile * file)109 void deFile_destroy (deFile* file)
110 {
111 	close(file->fd);
112 	deFree(file);
113 }
114 
deFile_setFlags(deFile * file,deUint32 flags)115 deBool deFile_setFlags (deFile* file, deUint32 flags)
116 {
117 	/* Non-blocking. */
118 	{
119 		int oldFlags = fcntl(file->fd, F_GETFL, 0);
120 		int newFlags = (flags & DE_FILE_NONBLOCKING) ? (oldFlags | O_NONBLOCK) : (oldFlags & ~O_NONBLOCK);
121 		if (fcntl(file->fd, F_SETFL, newFlags) != 0)
122 			return DE_FALSE;
123 	}
124 
125 	/* Close on exec. */
126 	{
127 		int oldFlags = fcntl(file->fd, F_GETFD, 0);
128 		int newFlags = (flags & DE_FILE_CLOSE_ON_EXEC) ? (oldFlags | FD_CLOEXEC) : (oldFlags & ~FD_CLOEXEC);
129 		if (fcntl(file->fd, F_SETFD, newFlags) != 0)
130 			return DE_FALSE;
131 	}
132 
133 	return DE_TRUE;
134 }
135 
mapSeekPosition(deFilePosition position)136 static int mapSeekPosition (deFilePosition position)
137 {
138 	switch (position)
139 	{
140 		case DE_FILEPOSITION_BEGIN:		return SEEK_SET;
141 		case DE_FILEPOSITION_END:		return SEEK_END;
142 		case DE_FILEPOSITION_CURRENT:	return SEEK_CUR;
143 		default:
144 			DE_ASSERT(DE_FALSE);
145 			return 0;
146 	}
147 }
148 
deFile_seek(deFile * file,deFilePosition base,deInt64 offset)149 deBool deFile_seek (deFile* file, deFilePosition base, deInt64 offset)
150 {
151 	return lseek(file->fd, (off_t)offset, mapSeekPosition(base)) >= 0;
152 }
153 
deFile_getPosition(const deFile * file)154 deInt64 deFile_getPosition (const deFile* file)
155 {
156 	return lseek(file->fd, 0, SEEK_CUR);
157 }
158 
deFile_getSize(const deFile * file)159 deInt64 deFile_getSize (const deFile* file)
160 {
161 	deInt64 size	= 0;
162 	deInt64 curPos	= lseek(file->fd, 0, SEEK_CUR);
163 
164 	if (curPos < 0)
165 		return -1;
166 
167 	size = lseek(file->fd, 0, SEEK_END);
168 
169 	if (size < 0)
170 		return -1;
171 
172 	lseek(file->fd, (off_t)curPos, SEEK_SET);
173 
174 	return size;
175 }
176 
mapReadWriteResult(deInt64 numBytes)177 static deFileResult mapReadWriteResult (deInt64 numBytes)
178 {
179 	if (numBytes > 0)
180 		return DE_FILERESULT_SUCCESS;
181 	else if (numBytes == 0)
182 		return DE_FILERESULT_END_OF_FILE;
183 	else
184 		return errno == EAGAIN ? DE_FILERESULT_WOULD_BLOCK : DE_FILERESULT_ERROR;
185 }
186 
deFile_read(deFile * file,void * buf,deInt64 bufSize,deInt64 * numReadPtr)187 deFileResult deFile_read (deFile* file, void* buf, deInt64 bufSize, deInt64* numReadPtr)
188 {
189 	deInt64 numRead = read(file->fd, buf, (size_t)bufSize);
190 
191 	if (numReadPtr)
192 		*numReadPtr = numRead;
193 
194 	return mapReadWriteResult(numRead);
195 }
196 
deFile_write(deFile * file,const void * buf,deInt64 bufSize,deInt64 * numWrittenPtr)197 deFileResult deFile_write (deFile* file, const void* buf, deInt64 bufSize, deInt64* numWrittenPtr)
198 {
199 	deInt64 numWritten = write(file->fd, buf, (size_t)bufSize);
200 
201 	if (numWrittenPtr)
202 		*numWrittenPtr = numWritten;
203 
204 	return mapReadWriteResult(numWritten);
205 }
206 
207 #elif (DE_OS == DE_OS_WIN32)
208 
209 #define VC_EXTRALEAN
210 #define WIN32_LEAN_AND_MEAN
211 #include <windows.h>
212 
213 struct deFile_s
214 {
215 	HANDLE handle;
216 };
217 
deFileExists(const char * filename)218 deBool deFileExists (const char* filename)
219 {
220 	return GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES;
221 }
222 
deDeleteFile(const char * filename)223 deBool deDeleteFile (const char* filename)
224 {
225 	return DeleteFile(filename) == TRUE;
226 }
227 
deFile_createFromHandle(deUintptr handle)228 deFile* deFile_createFromHandle (deUintptr handle)
229 {
230 	deFile* file = (deFile*)deCalloc(sizeof(deFile));
231 	if (!file)
232 	{
233 		CloseHandle((HANDLE)handle);
234 		return file;
235 	}
236 
237 	file->handle = (HANDLE)handle;
238 	return file;
239 }
240 
deFile_create(const char * filename,deUint32 mode)241 deFile* deFile_create (const char* filename, deUint32 mode)
242 {
243 	DWORD	access		= 0;
244 	DWORD	create		= OPEN_EXISTING;
245 	HANDLE	handle		= DE_NULL;
246 
247 	/* Read, write or read and write access is required. */
248 	DE_ASSERT((mode & DE_FILEMODE_READ) != 0 || ((mode & DE_FILEMODE_WRITE) != 0));
249 
250 	/* Create, open or create and open mode is required. */
251 	DE_ASSERT((mode & DE_FILEMODE_OPEN) != 0 || ((mode & DE_FILEMODE_CREATE) != 0));
252 
253 	/* Require write when using create. */
254 	DE_ASSERT(!(mode & DE_FILEMODE_CREATE) || (mode & DE_FILEMODE_WRITE));
255 
256 	/* Require write and open when using truncate */
257 	DE_ASSERT(!(mode & DE_FILEMODE_TRUNCATE) || ((mode & DE_FILEMODE_WRITE) && (mode & DE_FILEMODE_OPEN)));
258 
259 
260 	if (mode & DE_FILEMODE_READ)
261 		access |= GENERIC_READ;
262 
263 	if (mode & DE_FILEMODE_WRITE)
264 		access |= GENERIC_WRITE;
265 
266 	if ((mode & DE_FILEMODE_TRUNCATE))
267 	{
268 		if ((mode & DE_FILEMODE_CREATE) && (mode & DE_FILEMODE_OPEN))
269 			create = CREATE_ALWAYS;
270 		else if (mode & DE_FILEMODE_OPEN)
271 			create = TRUNCATE_EXISTING;
272 		else
273 			DE_ASSERT(DE_FALSE);
274 	}
275 	else
276 	{
277 		if ((mode & DE_FILEMODE_CREATE) && (mode & DE_FILEMODE_OPEN))
278 			create = OPEN_ALWAYS;
279 		else if (mode & DE_FILEMODE_CREATE)
280 			create = CREATE_NEW;
281 		else if (mode & DE_FILEMODE_OPEN)
282 			create = OPEN_EXISTING;
283 		else
284 			DE_ASSERT(DE_FALSE);
285 	}
286 
287 	handle = CreateFile(filename, access, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, DE_NULL, create, FILE_ATTRIBUTE_NORMAL, DE_NULL);
288 	if (handle == INVALID_HANDLE_VALUE)
289 		return DE_NULL;
290 
291 	return deFile_createFromHandle((deUintptr)handle);
292 }
293 
deFile_destroy(deFile * file)294 void deFile_destroy (deFile* file)
295 {
296 	CloseHandle(file->handle);
297 	deFree(file);
298 }
299 
deFile_setFlags(deFile * file,deUint32 flags)300 deBool deFile_setFlags (deFile* file, deUint32 flags)
301 {
302 	/* Non-blocking. */
303 	if (flags & DE_FILE_NONBLOCKING)
304 		return DE_FALSE; /* Not supported. */
305 
306 	/* Close on exec. */
307 	if (!SetHandleInformation(file->handle, HANDLE_FLAG_INHERIT, (flags & DE_FILE_CLOSE_ON_EXEC) ? HANDLE_FLAG_INHERIT : 0))
308 		return DE_FALSE;
309 
310 	return DE_TRUE;
311 }
312 
deFile_seek(deFile * file,deFilePosition base,deInt64 offset)313 deBool deFile_seek (deFile* file, deFilePosition base, deInt64 offset)
314 {
315 	DWORD	method		= 0;
316 	LONG	lowBits		= (LONG)(offset & 0xFFFFFFFFll);
317 	LONG	highBits	= (LONG)((offset >> 32) & 0xFFFFFFFFll);
318 
319 	switch (base)
320 	{
321 		case DE_FILEPOSITION_BEGIN:		method = FILE_BEGIN;	break;
322 		case DE_FILEPOSITION_END:		method = FILE_END;		break;
323 		case DE_FILEPOSITION_CURRENT:	method = FILE_CURRENT;	break;
324 		default:
325 			DE_ASSERT(DE_FALSE);
326 			return DE_FALSE;
327 	}
328 
329 	return SetFilePointer(file->handle, lowBits, &highBits, method) != INVALID_SET_FILE_POINTER;
330 }
331 
deFile_getPosition(const deFile * file)332 deInt64 deFile_getPosition (const deFile* file)
333 {
334 	LONG	highBits	= 0;
335 	LONG	lowBits		= SetFilePointer(file->handle, 0, &highBits, FILE_CURRENT);
336 
337 	return (deInt64)(((deUint64)highBits << 32) | (deUint64)lowBits);
338 }
339 
deFile_getSize(const deFile * file)340 deInt64 deFile_getSize (const deFile* file)
341 {
342 	DWORD	highBits	= 0;
343 	DWORD	lowBits		= GetFileSize(file->handle, &highBits);
344 
345 	return (deInt64)(((deUint64)highBits << 32) | (deUint64)lowBits);
346 }
347 
mapReadWriteResult(BOOL retVal,DWORD numBytes)348 static deFileResult mapReadWriteResult (BOOL retVal, DWORD numBytes)
349 {
350 	if (retVal && numBytes > 0)
351 		return DE_FILERESULT_SUCCESS;
352 	else if (retVal && numBytes == 0)
353 		return DE_FILERESULT_END_OF_FILE;
354 	else
355 	{
356 		DWORD error = GetLastError();
357 
358 		if (error == ERROR_HANDLE_EOF)
359 			return DE_FILERESULT_END_OF_FILE;
360 		else
361 			return DE_FILERESULT_ERROR;
362 	}
363 }
364 
deFile_read(deFile * file,void * buf,deInt64 bufSize,deInt64 * numReadPtr)365 deFileResult deFile_read (deFile* file, void* buf, deInt64 bufSize, deInt64* numReadPtr)
366 {
367 	DWORD	bufSize32	= (DWORD)bufSize;
368 	DWORD	numRead32	= 0;
369 	BOOL	result;
370 
371 	/* \todo [2011-10-03 pyry] 64-bit IO. */
372 	DE_ASSERT((deInt64)bufSize32 == bufSize);
373 
374 	result = ReadFile(file->handle, buf, bufSize32, &numRead32, DE_NULL);
375 
376 	if (numReadPtr)
377 		*numReadPtr = (deInt64)numRead32;
378 
379 	return mapReadWriteResult(result, numRead32);
380 }
381 
deFile_write(deFile * file,const void * buf,deInt64 bufSize,deInt64 * numWrittenPtr)382 deFileResult deFile_write (deFile* file, const void* buf, deInt64 bufSize, deInt64* numWrittenPtr)
383 {
384 	DWORD	bufSize32		= (DWORD)bufSize;
385 	DWORD	numWritten32	= 0;
386 	BOOL	result;
387 
388 	/* \todo [2011-10-03 pyry] 64-bit IO. */
389 	DE_ASSERT((deInt64)bufSize32 == bufSize);
390 
391 	result = WriteFile(file->handle, buf, bufSize32, &numWritten32, DE_NULL);
392 
393 	if (numWrittenPtr)
394 		*numWrittenPtr = (deInt64)numWritten32;
395 
396 	return mapReadWriteResult(result, numWritten32);
397 }
398 
399 #else
400 #	error Implement deFile for your OS.
401 #endif
402