/
platform_posix.c
479 lines (382 loc) · 12.3 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
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 */
64
65
66
67
68
69
70
static char *getUserDirByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
71
if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0'))
72
{
73
74
75
const size_t dlen = strlen(pw->pw_dir);
const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0;
retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep);
76
if (retval != NULL)
77
{
78
strcpy(retval, pw->pw_dir);
79
80
81
82
83
84
if (add_dirsep)
{
retval[dlen] = '/';
retval[dlen+1] = '\0';
} /* if */
} /* if */
85
86
} /* if */
87
return retval;
88
89
90
} /* getUserDirByUID */
91
char *__PHYSFS_platformCalcUserDir(void)
92
{
93
94
char *retval = NULL;
char *envr = getenv("HOME");
95
96
/* if the environment variable was set, make sure it's really a dir. */
97
if (envr != NULL)
98
99
{
struct stat statbuf;
100
if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode)))
101
{
102
103
104
105
106
107
108
109
110
111
112
113
const size_t envrlen = strlen(envr);
const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0;
retval = allocator.Malloc(envrlen + 1 + add_dirsep);
if (retval)
{
strcpy(retval, envr);
if (add_dirsep)
{
retval[envrlen] = '/';
retval[envrlen+1] = '\0';
} /* if */
} /* if */
114
115
116
} /* if */
} /* if */
117
118
if (retval == NULL)
retval = getUserDirByUID();
119
120
return retval;
121
} /* __PHYSFS_platformCalcUserDir */
122
123
124
125
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
126
127
PHYSFS_EnumFilesCallback callback,
const char *origdir,
128
void *callbackdata)
129
130
131
132
133
134
135
{
DIR *dir;
struct dirent *ent;
int bufsize = 0;
char *buf = NULL;
int dlen = 0;
136
if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
137
138
139
{
dlen = strlen(dirname);
bufsize = dlen + 256;
140
buf = (char *) allocator.Malloc(bufsize);
141
142
if (buf == NULL)
return;
143
144
145
146
147
148
149
150
151
152
153
154
strcpy(buf, dirname);
if (buf[dlen - 1] != '/')
{
buf[dlen++] = '/';
buf[dlen] = '\0';
} /* if */
} /* if */
errno = 0;
dir = opendir(dirname);
if (dir == NULL)
{
155
allocator.Free(buf);
156
return;
157
158
} /* if */
159
while ((ent = readdir(dir)) != NULL)
160
161
162
163
164
165
166
167
168
{
if (strcmp(ent->d_name, ".") == 0)
continue;
if (strcmp(ent->d_name, "..") == 0)
continue;
if (omitSymLinks)
{
169
170
PHYSFS_Stat statbuf;
int exists = 0;
171
172
173
174
char *p;
int len = strlen(ent->d_name) + dlen + 1;
if (len > bufsize)
{
175
p = (char *) allocator.Realloc(buf, len);
176
177
178
179
180
181
182
if (p == NULL)
continue;
buf = p;
bufsize = len;
} /* if */
strcpy(buf + dlen, ent->d_name);
183
184
185
186
187
188
189
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;
190
191
} /* if */
192
callback(callbackdata, origdir, ent->d_name);
193
194
} /* while */
195
allocator.Free(buf);
196
197
198
199
200
201
closedir(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
202
203
const int rc = mkdir(path, S_IRWXU);
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
204
return 1;
205
206
207
} /* __PHYSFS_platformMkDir */
208
static void *doOpen(const char *filename, int mode)
209
{
210
const int appending = (mode & O_APPEND);
211
212
int fd;
int *retval;
213
214
errno = 0;
215
216
217
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
218
fd = open(filename, mode, S_IRUSR | S_IWUSR);
219
BAIL_IF_MACRO(fd < 0, errcodeFromErrno(), NULL);
220
221
222
223
224
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
225
const int err = errno;
226
close(fd);
227
BAIL_MACRO(errcodeFromErrnoError(err), NULL);
228
229
230
} /* if */
} /* if */
231
retval = (int *) allocator.Malloc(sizeof (int));
232
if (!retval)
233
234
{
close(fd);
235
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
236
237
238
} /* if */
*retval = fd;
239
return ((void *) retval);
240
241
242
243
244
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
245
return doOpen(filename, O_RDONLY);
246
247
248
249
250
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
251
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
252
253
254
255
256
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
257
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
258
259
260
261
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
262
PHYSFS_uint64 len)
263
{
264
const int fd = *((int *) opaque);
265
ssize_t rc = 0;
266
267
268
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
269
270
rc = read(fd, buffer, (size_t) len);
271
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), -1);
272
273
274
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
275
276
277
278
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
279
PHYSFS_uint64 len)
280
{
281
const int fd = *((int *) opaque);
282
ssize_t rc = 0;
283
284
285
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
286
287
rc = write(fd, (void *) buffer, (size_t) len);
288
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), rc);
289
290
291
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
292
293
294
295
296
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
297
const int fd = *((int *) opaque);
298
299
const int rc = lseek(fd, (off_t) pos, SEEK_SET);
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
300
return 1;
301
302
303
304
305
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
306
const int fd = *((int *) opaque);
307
PHYSFS_sint64 retval;
308
retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
309
BAIL_IF_MACRO(retval == -1, errcodeFromErrno(), -1);
310
return retval;
311
312
313
314
315
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
316
const int fd = *((int *) opaque);
317
struct stat statbuf;
318
BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
319
return ((PHYSFS_sint64) statbuf.st_size);
320
321
322
323
324
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
325
const int fd = *((int *) opaque);
326
BAIL_IF_MACRO(fsync(fd) == -1, errcodeFromErrno(), 0);
327
return 1;
328
329
330
} /* __PHYSFS_platformFlush */
331
void __PHYSFS_platformClose(void *opaque)
332
{
333
334
const int fd = *((int *) opaque);
(void) close(fd); /* we don't check this. You should have used flush! */
335
allocator.Free(opaque);
336
337
338
339
340
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
341
BAIL_IF_MACRO(remove(path) == -1, errcodeFromErrno(), 0);
342
return 1;
343
344
} /* __PHYSFS_platformDelete */
345
346
347
348
349
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st)
{
struct stat statbuf;
350
if (lstat(filename, &statbuf) == -1)
351
{
352
*exists = (errno == ENOENT);
353
BAIL_MACRO(errcodeFromErrno(), 0);
354
355
} /* if */
356
357
*exists = 1;
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
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);
382
return 1;
383
384
} /* __PHYSFS_platformStat */
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#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));
415
BAIL_IF_MACRO(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
416
417
418
419
rc = pthread_mutex_init(&m->mutex, NULL);
if (rc != 0)
{
allocator.Free(m);
420
BAIL_MACRO(PHYSFS_ERR_OS_ERROR, NULL);
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
} /* 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;
461
462
assert(m->owner == pthread_self()); /* catch programming errors. */
assert(m->count > 0); /* catch programming errors. */
463
464
465
466
467
468
469
470
471
472
473
474
475
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 */
476
#endif /* PHYSFS_PLATFORM_POSIX */
477
478
/* end of posix.c ... */