/
platform_posix.c
514 lines (399 loc) · 12.5 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
11
12
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_POSIX
13
14
#include <unistd.h>
15
#include <ctype.h>
16
17
#include <sys/types.h>
#include <sys/stat.h>
18
#include <pwd.h>
19
20
#include <dirent.h>
#include <errno.h>
21
#include <fcntl.h>
22
23
24
25
26
#if ((!defined PHYSFS_NO_THREAD_SUPPORT) && (!defined PHYSFS_PLATFORM_BEOS))
#include <pthread.h>
#endif
27
28
29
30
#ifdef PHYSFS_HAVE_LLSEEK
#include <linux/unistd.h>
#endif
31
32
33
34
35
36
37
38
39
#include "physfs_internal.h"
char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname)
{
const char *envr = getenv(varname);
char *retval = NULL;
if (envr != NULL)
{
40
retval = (char *) allocator.Malloc(strlen(envr) + 1);
41
42
43
44
if (retval != NULL)
strcpy(retval, envr);
} /* if */
45
return retval;
46
47
48
49
50
51
52
53
54
55
56
57
} /* __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))
{
58
retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1);
59
60
61
62
if (retval != NULL)
strcpy(retval, pw->pw_name);
} /* if */
63
return retval;
64
65
66
67
68
69
70
71
72
73
74
75
} /* 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))
{
76
retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1);
77
78
79
80
if (retval != NULL)
strcpy(retval, pw->pw_dir);
} /* if */
81
return retval;
82
83
84
85
86
87
88
89
} /* getUserDirByUID */
char *__PHYSFS_platformGetUserName(void)
{
char *retval = getUserNameByUID();
if (retval == NULL)
retval = __PHYSFS_platformCopyEnvironmentVariable("USER");
90
return retval;
91
92
93
94
95
96
} /* __PHYSFS_platformGetUserName */
char *__PHYSFS_platformGetUserDir(void)
{
char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME");
97
98
99
100
101
102
103
104
105
106
107
108
/* 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 */
109
110
if (retval == NULL)
retval = getUserDirByUID();
111
112
return retval;
113
114
115
116
117
118
119
120
121
122
} /* __PHYSFS_platformGetUserDir */
char *__PHYSFS_platformCvtToDependent(const char *prepend,
const char *dirName,
const char *append)
{
int len = ((prepend) ? strlen(prepend) : 0) +
((append) ? strlen(append) : 0) +
strlen(dirName) + 1;
123
char *retval = (char *) allocator.Malloc(len);
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
/* platform-independent notation is Unix-style already. :) */
if (prepend)
strcpy(retval, prepend);
else
retval[0] = '\0';
strcat(retval, dirName);
if (append)
strcat(retval, append);
139
return retval;
140
141
142
143
} /* __PHYSFS_platformCvtToDependent */
144
145
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
146
147
PHYSFS_EnumFilesCallback callback,
const char *origdir,
148
void *callbackdata)
149
150
151
152
153
154
155
{
DIR *dir;
struct dirent *ent;
int bufsize = 0;
char *buf = NULL;
int dlen = 0;
156
if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
157
158
159
{
dlen = strlen(dirname);
bufsize = dlen + 256;
160
buf = (char *) allocator.Malloc(bufsize);
161
162
if (buf == NULL)
return;
163
164
165
166
167
168
169
170
171
172
173
174
strcpy(buf, dirname);
if (buf[dlen - 1] != '/')
{
buf[dlen++] = '/';
buf[dlen] = '\0';
} /* if */
} /* if */
errno = 0;
dir = opendir(dirname);
if (dir == NULL)
{
175
allocator.Free(buf);
176
return;
177
178
} /* if */
179
while ((ent = readdir(dir)) != NULL)
180
181
182
183
184
185
186
187
188
{
if (strcmp(ent->d_name, ".") == 0)
continue;
if (strcmp(ent->d_name, "..") == 0)
continue;
if (omitSymLinks)
{
189
190
PHYSFS_Stat statbuf;
int exists = 0;
191
192
193
194
char *p;
int len = strlen(ent->d_name) + dlen + 1;
if (len > bufsize)
{
195
p = (char *) allocator.Realloc(buf, len);
196
197
198
199
200
201
202
if (p == NULL)
continue;
buf = p;
bufsize = len;
} /* if */
strcpy(buf + dlen, ent->d_name);
203
204
205
206
207
208
209
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;
210
211
} /* if */
212
callback(callbackdata, origdir, ent->d_name);
213
214
} /* while */
215
allocator.Free(buf);
216
217
218
219
220
221
222
223
224
225
closedir(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
int rc;
errno = 0;
rc = mkdir(path, S_IRWXU);
BAIL_IF_MACRO(rc == -1, strerror(errno), 0);
226
return 1;
227
228
229
} /* __PHYSFS_platformMkDir */
230
static void *doOpen(const char *filename, int mode)
231
{
232
const int appending = (mode & O_APPEND);
233
234
int fd;
int *retval;
235
236
errno = 0;
237
238
239
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
240
fd = open(filename, mode, S_IRUSR | S_IWUSR);
241
BAIL_IF_MACRO(fd < 0, strerror(errno), NULL);
242
243
244
245
246
247
248
249
250
251
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
close(fd);
BAIL_MACRO(strerror(errno), NULL);
} /* if */
} /* if */
252
retval = (int *) allocator.Malloc(sizeof (int));
253
254
255
256
257
258
259
if (retval == NULL)
{
close(fd);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
*retval = fd;
260
return ((void *) retval);
261
262
263
264
265
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
266
return doOpen(filename, O_RDONLY);
267
268
269
270
271
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
272
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
273
274
275
276
277
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
278
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
279
280
281
282
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
283
PHYSFS_uint64 len)
284
{
285
const int fd = *((int *) opaque);
286
ssize_t rc = 0;
287
288
BAIL_IF_MACRO(!__PHYSFS_ui64FitsAddressSpace(len),ERR_INVALID_ARGUMENT,-1);
289
290
291
292
293
294
rc = read(fd, buffer, (size_t) len);
BAIL_IF_MACRO(rc == -1, strerror(errno), (PHYSFS_sint64) rc);
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
295
296
297
298
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
299
PHYSFS_uint64 len)
300
{
301
const int fd = *((int *) opaque);
302
ssize_t rc = 0;
303
304
BAIL_IF_MACRO(!__PHYSFS_ui64FitsAddressSpace(len),ERR_INVALID_ARGUMENT,-1);
305
306
307
308
309
310
rc = write(fd, (void *) buffer, (size_t) len);
BAIL_IF_MACRO(rc == -1, strerror(errno), rc);
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
311
312
313
314
315
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
316
const int fd = *((int *) opaque);
317
318
319
320
321
322
323
324
325
326
#ifdef PHYSFS_HAVE_LLSEEK
unsigned long offset_high = ((pos >> 32) & 0xFFFFFFFF);
unsigned long offset_low = (pos & 0xFFFFFFFF);
loff_t retoffset;
int rc = llseek(fd, offset_high, offset_low, &retoffset, SEEK_SET);
BAIL_IF_MACRO(rc == -1, strerror(errno), 0);
#else
BAIL_IF_MACRO(lseek(fd, (int) pos, SEEK_SET) == -1, strerror(errno), 0);
#endif
327
328
return 1;
329
330
331
332
333
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
334
const int fd = *((int *) opaque);
335
336
337
338
339
340
341
342
343
344
345
346
PHYSFS_sint64 retval;
#ifdef PHYSFS_HAVE_LLSEEK
loff_t retoffset;
int rc = llseek(fd, 0, &retoffset, SEEK_CUR);
BAIL_IF_MACRO(rc == -1, strerror(errno), -1);
retval = (PHYSFS_sint64) retoffset;
#else
retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
#endif
347
return retval;
348
349
350
351
352
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
353
const int fd = *((int *) opaque);
354
struct stat statbuf;
355
BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1);
356
return ((PHYSFS_sint64) statbuf.st_size);
357
358
359
360
361
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
362
const int fd = *((int *) opaque);
363
BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0);
364
return 1;
365
366
367
} /* __PHYSFS_platformFlush */
368
void __PHYSFS_platformClose(void *opaque)
369
{
370
371
const int fd = *((int *) opaque);
(void) close(fd); /* we don't check this. You should have used flush! */
372
allocator.Free(opaque);
373
374
375
376
377
378
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0);
379
return 1;
380
381
} /* __PHYSFS_platformDelete */
382
383
384
385
386
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st)
{
struct stat statbuf;
387
if (lstat(filename, &statbuf) == -1)
388
{
389
*exists = (errno == ENOENT);
390
BAIL_MACRO(strerror(errno), 0);
391
392
} /* if */
393
394
*exists = 1;
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
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);
419
return 1;
420
421
} /* __PHYSFS_platformStat */
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
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
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
#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));
BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(&m->mutex, NULL);
if (rc != 0)
{
allocator.Free(m);
BAIL_MACRO(strerror(rc), NULL);
} /* 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;
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 */
511
#endif /* PHYSFS_PLATFORM_POSIX */
512
513
/* end of posix.c ... */