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