/
platform_posix.c
486 lines (380 loc) · 11.8 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
} /* __PHYSFS_platformGetUserDir */
116
117
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
118
119
PHYSFS_EnumFilesCallback callback,
const char *origdir,
120
void *callbackdata)
121
122
123
124
125
126
127
{
DIR *dir;
struct dirent *ent;
int bufsize = 0;
char *buf = NULL;
int dlen = 0;
128
if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
129
130
131
{
dlen = strlen(dirname);
bufsize = dlen + 256;
132
buf = (char *) allocator.Malloc(bufsize);
133
134
if (buf == NULL)
return;
135
136
137
138
139
140
141
142
143
144
145
146
strcpy(buf, dirname);
if (buf[dlen - 1] != '/')
{
buf[dlen++] = '/';
buf[dlen] = '\0';
} /* if */
} /* if */
errno = 0;
dir = opendir(dirname);
if (dir == NULL)
{
147
allocator.Free(buf);
148
return;
149
150
} /* if */
151
while ((ent = readdir(dir)) != NULL)
152
153
154
155
156
157
158
159
160
{
if (strcmp(ent->d_name, ".") == 0)
continue;
if (strcmp(ent->d_name, "..") == 0)
continue;
if (omitSymLinks)
{
161
162
PHYSFS_Stat statbuf;
int exists = 0;
163
164
165
166
char *p;
int len = strlen(ent->d_name) + dlen + 1;
if (len > bufsize)
{
167
p = (char *) allocator.Realloc(buf, len);
168
169
170
171
172
173
174
if (p == NULL)
continue;
buf = p;
bufsize = len;
} /* if */
strcpy(buf + dlen, ent->d_name);
175
176
177
178
179
180
181
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;
182
183
} /* if */
184
callback(callbackdata, origdir, ent->d_name);
185
186
} /* while */
187
allocator.Free(buf);
188
189
190
191
192
193
194
195
196
197
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);
198
return 1;
199
200
201
} /* __PHYSFS_platformMkDir */
202
static void *doOpen(const char *filename, int mode)
203
{
204
const int appending = (mode & O_APPEND);
205
206
int fd;
int *retval;
207
208
errno = 0;
209
210
211
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
212
fd = open(filename, mode, S_IRUSR | S_IWUSR);
213
BAIL_IF_MACRO(fd < 0, strerror(errno), NULL);
214
215
216
217
218
219
220
221
222
223
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
close(fd);
BAIL_MACRO(strerror(errno), NULL);
} /* if */
} /* if */
224
retval = (int *) allocator.Malloc(sizeof (int));
225
226
227
228
229
230
231
if (retval == NULL)
{
close(fd);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
*retval = fd;
232
return ((void *) retval);
233
234
235
236
237
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
238
return doOpen(filename, O_RDONLY);
239
240
241
242
243
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
244
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
245
246
247
248
249
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
250
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
251
252
253
254
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
255
PHYSFS_uint64 len)
256
{
257
const int fd = *((int *) opaque);
258
ssize_t rc = 0;
259
260
BAIL_IF_MACRO(!__PHYSFS_ui64FitsAddressSpace(len),ERR_INVALID_ARGUMENT,-1);
261
262
263
264
265
266
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;
267
268
269
270
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
271
PHYSFS_uint64 len)
272
{
273
const int fd = *((int *) opaque);
274
ssize_t rc = 0;
275
276
BAIL_IF_MACRO(!__PHYSFS_ui64FitsAddressSpace(len),ERR_INVALID_ARGUMENT,-1);
277
278
279
280
281
282
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;
283
284
285
286
287
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
288
const int fd = *((int *) opaque);
289
290
291
292
293
294
295
296
297
298
#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
299
300
return 1;
301
302
303
304
305
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
306
const int fd = *((int *) opaque);
307
308
309
310
311
312
313
314
315
316
317
318
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
319
return retval;
320
321
322
323
324
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
325
const int fd = *((int *) opaque);
326
struct stat statbuf;
327
BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1);
328
return ((PHYSFS_sint64) statbuf.st_size);
329
330
331
332
333
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
334
const int fd = *((int *) opaque);
335
BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0);
336
return 1;
337
338
339
} /* __PHYSFS_platformFlush */
340
void __PHYSFS_platformClose(void *opaque)
341
{
342
343
const int fd = *((int *) opaque);
(void) close(fd); /* we don't check this. You should have used flush! */
344
allocator.Free(opaque);
345
346
347
348
349
350
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0);
351
return 1;
352
353
} /* __PHYSFS_platformDelete */
354
355
356
357
358
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st)
{
struct stat statbuf;
359
if (lstat(filename, &statbuf) == -1)
360
{
361
*exists = (errno == ENOENT);
362
BAIL_MACRO(strerror(errno), 0);
363
364
} /* if */
365
366
*exists = 1;
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
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);
391
return 1;
392
393
} /* __PHYSFS_platformStat */
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
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
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
#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 */
483
#endif /* PHYSFS_PLATFORM_POSIX */
484
485
/* end of posix.c ... */