/
platform_posix.c
503 lines (395 loc) · 12.6 KB
1
2
3
/*
* Posix-esque support routines for PhysicsFS.
*
4
* Please see the file LICENSE.txt in the source's root directory.
5
6
7
8
*
* This file written by Ryan C. Gordon.
*/
9
10
/* !!! FIXME: check for EINTR? */
11
12
13
14
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_POSIX
15
16
#include <unistd.h>
17
#include <ctype.h>
18
19
#include <sys/types.h>
#include <sys/stat.h>
20
#include <pwd.h>
21
22
#include <dirent.h>
#include <errno.h>
23
#include <fcntl.h>
24
25
26
27
28
#if ((!defined PHYSFS_NO_THREAD_SUPPORT) && (!defined PHYSFS_PLATFORM_BEOS))
#include <pthread.h>
#endif
29
30
#include "physfs_internal.h"
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
static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
{
switch (err)
{
case 0: return PHYSFS_ERR_OK;
case EACCES: return PHYSFS_ERR_PERMISSION;
case EPERM: return PHYSFS_ERR_PERMISSION;
case EDQUOT: return PHYSFS_ERR_NO_SPACE;
case EIO: return PHYSFS_ERR_IO;
case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
case EMLINK: return PHYSFS_ERR_NO_SPACE;
case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
case ENOENT: return PHYSFS_ERR_NO_SUCH_PATH;
case ENOSPC: return PHYSFS_ERR_NO_SPACE;
case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH;
case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
case EROFS: return PHYSFS_ERR_READ_ONLY;
case ETXTBSY: return PHYSFS_ERR_BUSY;
case EBUSY: return PHYSFS_ERR_BUSY;
case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
default: return PHYSFS_ERR_OS_ERROR;
} /* switch */
} /* errcodeFromErrnoError */
static inline PHYSFS_ErrorCode errcodeFromErrno(void)
{
return errcodeFromErrnoError(errno);
} /* errcodeFromErrno */
65
66
67
68
69
70
71
char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname)
{
const char *envr = getenv(varname);
char *retval = NULL;
if (envr != NULL)
{
72
retval = (char *) allocator.Malloc(strlen(envr) + 1);
73
74
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, envr);
75
76
} /* if */
77
return retval;
78
79
80
81
82
83
84
85
86
87
88
89
} /* __PHYSFS_platformCopyEnvironmentVariable */
static char *getUserNameByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
if ((pw != NULL) && (pw->pw_name != NULL))
{
90
retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1);
91
92
93
94
if (retval != NULL)
strcpy(retval, pw->pw_name);
} /* if */
95
return retval;
96
97
98
99
100
101
102
103
104
105
106
107
} /* getUserNameByUID */
static char *getUserDirByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
if ((pw != NULL) && (pw->pw_dir != NULL))
{
108
retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1);
109
110
111
112
if (retval != NULL)
strcpy(retval, pw->pw_dir);
} /* if */
113
return retval;
114
115
116
117
118
119
120
121
} /* getUserDirByUID */
char *__PHYSFS_platformGetUserName(void)
{
char *retval = getUserNameByUID();
if (retval == NULL)
retval = __PHYSFS_platformCopyEnvironmentVariable("USER");
122
return retval;
123
124
125
} /* __PHYSFS_platformGetUserName */
126
char *__PHYSFS_platformCalcUserDir(void)
127
128
{
char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME");
129
130
131
132
133
134
135
136
137
138
139
140
/* if the environment variable was set, make sure it's really a dir. */
if (retval != NULL)
{
struct stat statbuf;
if ((stat(retval, &statbuf) == -1) || (S_ISDIR(statbuf.st_mode) == 0))
{
allocator.Free(retval);
retval = NULL;
} /* if */
} /* if */
141
142
if (retval == NULL)
retval = getUserDirByUID();
143
144
return retval;
145
} /* __PHYSFS_platformCalcUserDir */
146
147
148
149
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
150
151
PHYSFS_EnumFilesCallback callback,
const char *origdir,
152
void *callbackdata)
153
154
155
156
157
158
159
{
DIR *dir;
struct dirent *ent;
int bufsize = 0;
char *buf = NULL;
int dlen = 0;
160
if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
161
162
163
{
dlen = strlen(dirname);
bufsize = dlen + 256;
164
buf = (char *) allocator.Malloc(bufsize);
165
166
if (buf == NULL)
return;
167
168
169
170
171
172
173
174
175
176
177
178
strcpy(buf, dirname);
if (buf[dlen - 1] != '/')
{
buf[dlen++] = '/';
buf[dlen] = '\0';
} /* if */
} /* if */
errno = 0;
dir = opendir(dirname);
if (dir == NULL)
{
179
allocator.Free(buf);
180
return;
181
182
} /* if */
183
while ((ent = readdir(dir)) != NULL)
184
185
186
187
188
189
190
191
192
{
if (strcmp(ent->d_name, ".") == 0)
continue;
if (strcmp(ent->d_name, "..") == 0)
continue;
if (omitSymLinks)
{
193
194
PHYSFS_Stat statbuf;
int exists = 0;
195
196
197
198
char *p;
int len = strlen(ent->d_name) + dlen + 1;
if (len > bufsize)
{
199
p = (char *) allocator.Realloc(buf, len);
200
201
202
203
204
205
206
if (p == NULL)
continue;
buf = p;
bufsize = len;
} /* if */
strcpy(buf + dlen, ent->d_name);
207
208
209
210
211
212
213
if (!__PHYSFS_platformStat(buf, &exists, &statbuf))
continue;
else if (!exists)
continue; /* probably can't happen, but just in case. */
else if (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK)
continue;
214
215
} /* if */
216
callback(callbackdata, origdir, ent->d_name);
217
218
} /* while */
219
allocator.Free(buf);
220
221
222
223
224
225
closedir(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
226
227
const int rc = mkdir(path, S_IRWXU);
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
228
return 1;
229
230
231
} /* __PHYSFS_platformMkDir */
232
static void *doOpen(const char *filename, int mode)
233
{
234
const int appending = (mode & O_APPEND);
235
236
int fd;
int *retval;
237
238
errno = 0;
239
240
241
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
242
fd = open(filename, mode, S_IRUSR | S_IWUSR);
243
BAIL_IF_MACRO(fd < 0, errcodeFromErrno(), NULL);
244
245
246
247
248
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
249
const int err = errno;
250
close(fd);
251
BAIL_MACRO(errcodeFromErrnoError(err), NULL);
252
253
254
} /* if */
} /* if */
255
retval = (int *) allocator.Malloc(sizeof (int));
256
if (!retval)
257
258
{
close(fd);
259
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
260
261
262
} /* if */
*retval = fd;
263
return ((void *) retval);
264
265
266
267
268
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
269
return doOpen(filename, O_RDONLY);
270
271
272
273
274
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
275
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
276
277
278
279
280
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
281
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
282
283
284
285
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
286
PHYSFS_uint64 len)
287
{
288
const int fd = *((int *) opaque);
289
ssize_t rc = 0;
290
291
292
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
293
294
rc = read(fd, buffer, (size_t) len);
295
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), -1);
296
297
298
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
299
300
301
302
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
303
PHYSFS_uint64 len)
304
{
305
const int fd = *((int *) opaque);
306
ssize_t rc = 0;
307
308
309
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
310
311
rc = write(fd, (void *) buffer, (size_t) len);
312
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), rc);
313
314
315
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
316
317
318
319
320
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
321
const int fd = *((int *) opaque);
322
323
const int rc = lseek(fd, (off_t) pos, SEEK_SET);
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
324
return 1;
325
326
327
328
329
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
330
const int fd = *((int *) opaque);
331
PHYSFS_sint64 retval;
332
retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
333
BAIL_IF_MACRO(retval == -1, errcodeFromErrno(), -1);
334
return retval;
335
336
337
338
339
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
340
const int fd = *((int *) opaque);
341
struct stat statbuf;
342
BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
343
return ((PHYSFS_sint64) statbuf.st_size);
344
345
346
347
348
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
349
const int fd = *((int *) opaque);
350
BAIL_IF_MACRO(fsync(fd) == -1, errcodeFromErrno(), 0);
351
return 1;
352
353
354
} /* __PHYSFS_platformFlush */
355
void __PHYSFS_platformClose(void *opaque)
356
{
357
358
const int fd = *((int *) opaque);
(void) close(fd); /* we don't check this. You should have used flush! */
359
allocator.Free(opaque);
360
361
362
363
364
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
365
BAIL_IF_MACRO(remove(path) == -1, errcodeFromErrno(), 0);
366
return 1;
367
368
} /* __PHYSFS_platformDelete */
369
370
371
372
373
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st)
{
struct stat statbuf;
374
if (lstat(filename, &statbuf) == -1)
375
{
376
*exists = (errno == ENOENT);
377
BAIL_MACRO(errcodeFromErrno(), 0);
378
379
} /* if */
380
381
*exists = 1;
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
if (S_ISREG(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_REGULAR;
st->filesize = statbuf.st_size;
} /* if */
else if(S_ISDIR(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_DIRECTORY;
st->filesize = 0;
} /* else if */
else
{
st->filetype = PHYSFS_FILETYPE_OTHER;
st->filesize = statbuf.st_size;
} /* else */
st->modtime = statbuf.st_mtime;
st->createtime = statbuf.st_ctime;
st->accesstime = statbuf.st_atime;
/* !!! FIXME: maybe we should just report full permissions? */
st->readonly = access(filename, W_OK);
406
return 1;
407
408
} /* __PHYSFS_platformStat */
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
#ifndef PHYSFS_PLATFORM_BEOS /* BeOS has its own code in platform_beos.cpp */
#if (defined PHYSFS_NO_THREAD_SUPPORT)
void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); }
void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); }
void __PHYSFS_platformDestroyMutex(void *mutex) {}
int __PHYSFS_platformGrabMutex(void *mutex) { return 1; }
void __PHYSFS_platformReleaseMutex(void *mutex) {}
#else
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
void *__PHYSFS_platformGetThreadID(void)
{
return ( (void *) ((size_t) pthread_self()) );
} /* __PHYSFS_platformGetThreadID */
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
439
BAIL_IF_MACRO(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
440
441
442
443
rc = pthread_mutex_init(&m->mutex, NULL);
if (rc != 0)
{
allocator.Free(m);
444
BAIL_MACRO(PHYSFS_ERR_OS_ERROR, NULL);
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
} /* if */
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
return ((void *) m);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
/* Destroying a locked mutex is a bug, but we'll try to be helpful. */
if ((m->owner == pthread_self()) && (m->count > 0))
pthread_mutex_unlock(&m->mutex);
pthread_mutex_destroy(&m->mutex);
allocator.Free(m);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
pthread_t tid = pthread_self();
if (m->owner != tid)
{
if (pthread_mutex_lock(&m->mutex) != 0)
return 0;
m->owner = tid;
} /* if */
m->count++;
return 1;
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
485
486
assert(m->owner == pthread_self()); /* catch programming errors. */
assert(m->count > 0); /* catch programming errors. */
487
488
489
490
491
492
493
494
495
496
497
498
499
if (m->owner == pthread_self())
{
if (--m->count == 0)
{
m->owner = (pthread_t) 0xDEADBEEF;
pthread_mutex_unlock(&m->mutex);
} /* if */
} /* if */
} /* __PHYSFS_platformReleaseMutex */
#endif /* !PHYSFS_NO_THREAD_SUPPORT */
#endif /* !PHYSFS_PLATFORM_BEOS */
500
#endif /* PHYSFS_PLATFORM_POSIX */
501
502
/* end of posix.c ... */