Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Posix-esque support routines for PhysicsFS.
3
 *
4
 * Please see the file LICENSE.txt in the source's root directory.
5
 *
6
 *  This file written by Ryan C. Gordon.
7
 */
8
 
9
/* !!! FIXME: check for EINTR? */
10
 
11
#define __PHYSICSFS_INTERNAL__
12
#include "physfs_platforms.h"
13
 
14
#ifdef PHYSFS_PLATFORM_POSIX
15
 
16
#include <unistd.h>
17
#include <ctype.h>
18
#include <sys/types.h>
19
#include <sys/stat.h>
20
#include <pwd.h>
21
#include <dirent.h>
22
#include <errno.h>
23
#include <fcntl.h>
24
#include <pthread.h>
25
 
26
#include "physfs_internal.h"
27
 
28
 
29
static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
30
{
31
    switch (err)
32
    {
33
        case 0: return PHYSFS_ERR_OK;
34
        case EACCES: return PHYSFS_ERR_PERMISSION;
35
        case EPERM: return PHYSFS_ERR_PERMISSION;
36
        case EDQUOT: return PHYSFS_ERR_NO_SPACE;
37
        case EIO: return PHYSFS_ERR_IO;
38
        case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
39
        case EMLINK: return PHYSFS_ERR_NO_SPACE;
40
        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
        case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
45
        case EROFS: return PHYSFS_ERR_READ_ONLY;
46
        case ETXTBSY: return PHYSFS_ERR_BUSY;
47
        case EBUSY: return PHYSFS_ERR_BUSY;
48
        case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
49
        case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
50
        default: return PHYSFS_ERR_OS_ERROR;
51
    } /* switch */
52
} /* errcodeFromErrnoError */
53
 
54
 
55
static inline PHYSFS_ErrorCode errcodeFromErrno(void)
56
{
57
    return errcodeFromErrnoError(errno);
58
} /* errcodeFromErrno */
59
 
60
 
61
static char *getUserDirByUID(void)
62
{
63
    uid_t uid = getuid();
64
    struct passwd *pw;
65
    char *retval = NULL;
66
 
67
    pw = getpwuid(uid);
68
    if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0'))
69
    {
70
        const size_t dlen = strlen(pw->pw_dir);
71
        const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0;
72
        retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep);
73
        if (retval != NULL)
74
        {
75
            strcpy(retval, pw->pw_dir);
76
            if (add_dirsep)
77
            {
78
                retval[dlen] = '/';
79
                retval[dlen+1] = '\0';
80
            } /* if */
81
        } /* if */
82
    } /* if */
83
 
84
    return retval;
85
} /* getUserDirByUID */
86
 
87
 
88
char *__PHYSFS_platformCalcUserDir(void)
89
{
90
    char *retval = NULL;
91
    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
            const size_t envrlen = strlen(envr);
100
            const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0;
101
            retval = allocator.Malloc(envrlen + 1 + add_dirsep);
102
            if (retval)
103
            {
104
                strcpy(retval, envr);
105
                if (add_dirsep)
106
                {
107
                    retval[envrlen] = '/';
108
                    retval[envrlen+1] = '\0';
109
                } /* if */
110
            } /* if */
111
        } /* if */
112
    } /* if */
113
 
114
    if (retval == NULL)
115
        retval = getUserDirByUID();
116
 
117
    return retval;
118
} /* __PHYSFS_platformCalcUserDir */
119
 
120
 
121
PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
122
                               PHYSFS_EnumerateCallback callback,
123
                               const char *origdir, void *callbackdata)
124
{
125
    DIR *dir;
126
    struct dirent *ent;
127
    PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
128
 
129
    dir = opendir(dirname);
130
    BAIL_IF(dir == NULL, errcodeFromErrno(), PHYSFS_ENUM_ERROR);
131
 
132
    while ((retval == PHYSFS_ENUM_OK) && ((ent = readdir(dir)) != NULL))
133
    {
134
        const char *name = ent->d_name;
135
        if (name[0] == '.')  /* ignore "." and ".." */
136
        {
137
            if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0')))
138
                continue;
139
        } /* if */
140
 
141
        retval = callback(callbackdata, origdir, name);
142
        if (retval == PHYSFS_ENUM_ERROR)
143
            PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
144
    } /* while */
145
 
146
    closedir(dir);
147
 
148
    return retval;
149
} /* __PHYSFS_platformEnumerate */
150
 
151
 
152
int __PHYSFS_platformMkDir(const char *path)
153
{
154
    const int rc = mkdir(path, S_IRWXU);
155
    BAIL_IF(rc == -1, errcodeFromErrno(), 0);
156
    return 1;
157
} /* __PHYSFS_platformMkDir */
158
 
159
 
160
static void *doOpen(const char *filename, int mode)
161
{
162
    const int appending = (mode & O_APPEND);
163
    int fd;
164
    int *retval;
165
    errno = 0;
166
 
167
    /* O_APPEND doesn't actually behave as we'd like. */
168
    mode &= ~O_APPEND;
169
 
170
    fd = open(filename, mode, S_IRUSR | S_IWUSR);
171
    BAIL_IF(fd < 0, errcodeFromErrno(), NULL);
172
 
173
    if (appending)
174
    {
175
        if (lseek(fd, 0, SEEK_END) < 0)
176
        {
177
            const int err = errno;
178
            close(fd);
179
            BAIL(errcodeFromErrnoError(err), NULL);
180
        } /* if */
181
    } /* if */
182
 
183
    retval = (int *) allocator.Malloc(sizeof (int));
184
    if (!retval)
185
    {
186
        close(fd);
187
        BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
188
    } /* if */
189
 
190
    *retval = fd;
191
    return ((void *) retval);
192
} /* doOpen */
193
 
194
 
195
void *__PHYSFS_platformOpenRead(const char *filename)
196
{
197
    return doOpen(filename, O_RDONLY);
198
} /* __PHYSFS_platformOpenRead */
199
 
200
 
201
void *__PHYSFS_platformOpenWrite(const char *filename)
202
{
203
    return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
204
} /* __PHYSFS_platformOpenWrite */
205
 
206
 
207
void *__PHYSFS_platformOpenAppend(const char *filename)
208
{
209
    return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
210
} /* __PHYSFS_platformOpenAppend */
211
 
212
 
213
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
214
                                    PHYSFS_uint64 len)
215
{
216
    const int fd = *((int *) opaque);
217
    ssize_t rc = 0;
218
 
219
    if (!__PHYSFS_ui64FitsAddressSpace(len))
220
        BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
221
 
222
    rc = read(fd, buffer, (size_t) len);
223
    BAIL_IF(rc == -1, errcodeFromErrno(), -1);
224
    assert(rc >= 0);
225
    assert(rc <= len);
226
    return (PHYSFS_sint64) rc;
227
} /* __PHYSFS_platformRead */
228
 
229
 
230
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
231
                                     PHYSFS_uint64 len)
232
{
233
    const int fd = *((int *) opaque);
234
    ssize_t rc = 0;
235
 
236
    if (!__PHYSFS_ui64FitsAddressSpace(len))
237
        BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
238
 
239
    rc = write(fd, (void *) buffer, (size_t) len);
240
    BAIL_IF(rc == -1, errcodeFromErrno(), rc);
241
    assert(rc >= 0);
242
    assert(rc <= len);
243
    return (PHYSFS_sint64) rc;
244
} /* __PHYSFS_platformWrite */
245
 
246
 
247
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
248
{
249
    const int fd = *((int *) opaque);
250
    const off_t rc = lseek(fd, (off_t) pos, SEEK_SET);
251
    BAIL_IF(rc == -1, errcodeFromErrno(), 0);
252
    return 1;
253
} /* __PHYSFS_platformSeek */
254
 
255
 
256
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
257
{
258
    const int fd = *((int *) opaque);
259
    PHYSFS_sint64 retval;
260
    retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
261
    BAIL_IF(retval == -1, errcodeFromErrno(), -1);
262
    return retval;
263
} /* __PHYSFS_platformTell */
264
 
265
 
266
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
267
{
268
    const int fd = *((int *) opaque);
269
    struct stat statbuf;
270
    BAIL_IF(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
271
    return ((PHYSFS_sint64) statbuf.st_size);
272
} /* __PHYSFS_platformFileLength */
273
 
274
 
275
int __PHYSFS_platformFlush(void *opaque)
276
{
277
    const int fd = *((int *) opaque);
278
    if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY)
279
        BAIL_IF(fsync(fd) == -1, errcodeFromErrno(), 0);
280
    return 1;
281
} /* __PHYSFS_platformFlush */
282
 
283
 
284
void __PHYSFS_platformClose(void *opaque)
285
{
286
    const int fd = *((int *) opaque);
287
    (void) close(fd);  /* we don't check this. You should have used flush! */
288
    allocator.Free(opaque);
289
} /* __PHYSFS_platformClose */
290
 
291
 
292
int __PHYSFS_platformDelete(const char *path)
293
{
294
    BAIL_IF(remove(path) == -1, errcodeFromErrno(), 0);
295
    return 1;
296
} /* __PHYSFS_platformDelete */
297
 
298
 
299
int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
300
{
301
    struct stat statbuf;
302
    const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf);
303
    BAIL_IF(rc == -1, errcodeFromErrno(), 0);
304
 
305
    if (S_ISREG(statbuf.st_mode))
306
    {
307
        st->filetype = PHYSFS_FILETYPE_REGULAR;
308
        st->filesize = statbuf.st_size;
309
    } /* if */
310
 
311
    else if(S_ISDIR(statbuf.st_mode))
312
    {
313
        st->filetype = PHYSFS_FILETYPE_DIRECTORY;
314
        st->filesize = 0;
315
    } /* else if */
316
 
317
    else if(S_ISLNK(statbuf.st_mode))
318
    {
319
        st->filetype = PHYSFS_FILETYPE_SYMLINK;
320
        st->filesize = 0;
321
    } /* else if */
322
 
323
    else
324
    {
325
        st->filetype = PHYSFS_FILETYPE_OTHER;
326
        st->filesize = statbuf.st_size;
327
    } /* else */
328
 
329
    st->modtime = statbuf.st_mtime;
330
    st->createtime = statbuf.st_ctime;
331
    st->accesstime = statbuf.st_atime;
332
 
333
    st->readonly = (access(fname, W_OK) == -1);
334
    return 1;
335
} /* __PHYSFS_platformStat */
336
 
337
 
338
typedef struct
339
{
340
    pthread_mutex_t mutex;
341
    pthread_t owner;
342
    PHYSFS_uint32 count;
343
} PthreadMutex;
344
 
345
 
346
void *__PHYSFS_platformGetThreadID(void)
347
{
348
    return ( (void *) ((size_t) pthread_self()) );
349
} /* __PHYSFS_platformGetThreadID */
350
 
351
 
352
void *__PHYSFS_platformCreateMutex(void)
353
{
354
    int rc;
355
    PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
356
    BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
357
    rc = pthread_mutex_init(&m->mutex, NULL);
358
    if (rc != 0)
359
    {
360
        allocator.Free(m);
361
        BAIL(PHYSFS_ERR_OS_ERROR, NULL);
362
    } /* if */
363
 
364
    m->count = 0;
365
    m->owner = (pthread_t) 0xDEADBEEF;
366
    return ((void *) m);
367
} /* __PHYSFS_platformCreateMutex */
368
 
369
 
370
void __PHYSFS_platformDestroyMutex(void *mutex)
371
{
372
    PthreadMutex *m = (PthreadMutex *) mutex;
373
 
374
    /* Destroying a locked mutex is a bug, but we'll try to be helpful. */
375
    if ((m->owner == pthread_self()) && (m->count > 0))
376
        pthread_mutex_unlock(&m->mutex);
377
 
378
    pthread_mutex_destroy(&m->mutex);
379
    allocator.Free(m);
380
} /* __PHYSFS_platformDestroyMutex */
381
 
382
 
383
int __PHYSFS_platformGrabMutex(void *mutex)
384
{
385
    PthreadMutex *m = (PthreadMutex *) mutex;
386
    pthread_t tid = pthread_self();
387
    if (m->owner != tid)
388
    {
389
        if (pthread_mutex_lock(&m->mutex) != 0)
390
            return 0;
391
        m->owner = tid;
392
    } /* if */
393
 
394
    m->count++;
395
    return 1;
396
} /* __PHYSFS_platformGrabMutex */
397
 
398
 
399
void __PHYSFS_platformReleaseMutex(void *mutex)
400
{
401
    PthreadMutex *m = (PthreadMutex *) mutex;
402
    assert(m->owner == pthread_self());  /* catch programming errors. */
403
    assert(m->count > 0);  /* catch programming errors. */
404
    if (m->owner == pthread_self())
405
    {
406
        if (--m->count == 0)
407
        {
408
            m->owner = (pthread_t) 0xDEADBEEF;
409
            pthread_mutex_unlock(&m->mutex);
410
        } /* if */
411
    } /* if */
412
} /* __PHYSFS_platformReleaseMutex */
413
 
414
#endif  /* PHYSFS_PLATFORM_POSIX */
415
 
416
/* end of physfs_platform_posix.c ... */
417