7768
+ − 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
+ − 25
#if ((!defined PHYSFS_NO_THREAD_SUPPORT) && (!defined PHYSFS_PLATFORM_BEOS))
+ − 26
#include <pthread.h>
+ − 27
#endif
+ − 28
+ − 29
#include "physfs_internal.h"
+ − 30
+ − 31
+ − 32
static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
+ − 33
{
+ − 34
switch (err)
+ − 35
{
+ − 36
case 0: return PHYSFS_ERR_OK;
+ − 37
case EACCES: return PHYSFS_ERR_PERMISSION;
+ − 38
case EPERM: return PHYSFS_ERR_PERMISSION;
+ − 39
case EDQUOT: return PHYSFS_ERR_NO_SPACE;
+ − 40
case EIO: return PHYSFS_ERR_IO;
+ − 41
case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
+ − 42
case EMLINK: return PHYSFS_ERR_NO_SPACE;
+ − 43
case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
+ − 44
case ENOENT: return PHYSFS_ERR_NO_SUCH_PATH;
+ − 45
case ENOSPC: return PHYSFS_ERR_NO_SPACE;
+ − 46
case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH;
+ − 47
case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
+ − 48
case EROFS: return PHYSFS_ERR_READ_ONLY;
+ − 49
case ETXTBSY: return PHYSFS_ERR_BUSY;
+ − 50
case EBUSY: return PHYSFS_ERR_BUSY;
+ − 51
case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
+ − 52
case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
+ − 53
default: return PHYSFS_ERR_OS_ERROR;
+ − 54
} /* switch */
+ − 55
} /* errcodeFromErrnoError */
+ − 56
+ − 57
+ − 58
static inline PHYSFS_ErrorCode errcodeFromErrno(void)
+ − 59
{
+ − 60
return errcodeFromErrnoError(errno);
+ − 61
} /* errcodeFromErrno */
+ − 62
+ − 63
+ − 64
static char *getUserDirByUID(void)
+ − 65
{
+ − 66
uid_t uid = getuid();
+ − 67
struct passwd *pw;
+ − 68
char *retval = NULL;
+ − 69
+ − 70
pw = getpwuid(uid);
+ − 71
if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0'))
+ − 72
{
+ − 73
const size_t dlen = strlen(pw->pw_dir);
+ − 74
const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0;
+ − 75
retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep);
+ − 76
if (retval != NULL)
+ − 77
{
+ − 78
strcpy(retval, pw->pw_dir);
+ − 79
if (add_dirsep)
+ − 80
{
+ − 81
retval[dlen] = '/';
+ − 82
retval[dlen+1] = '\0';
+ − 83
} /* if */
+ − 84
} /* if */
+ − 85
} /* if */
+ − 86
+ − 87
return retval;
+ − 88
} /* getUserDirByUID */
+ − 89
+ − 90
+ − 91
char *__PHYSFS_platformCalcUserDir(void)
+ − 92
{
+ − 93
char *retval = NULL;
+ − 94
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
const size_t envrlen = strlen(envr);
+ − 103
const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0;
+ − 104
retval = allocator.Malloc(envrlen + 1 + add_dirsep);
+ − 105
if (retval)
+ − 106
{
+ − 107
strcpy(retval, envr);
+ − 108
if (add_dirsep)
+ − 109
{
+ − 110
retval[envrlen] = '/';
+ − 111
retval[envrlen+1] = '\0';
+ − 112
} /* if */
+ − 113
} /* if */
+ − 114
} /* if */
+ − 115
} /* if */
+ − 116
+ − 117
if (retval == NULL)
+ − 118
retval = getUserDirByUID();
+ − 119
+ − 120
return retval;
+ − 121
} /* __PHYSFS_platformCalcUserDir */
+ − 122
+ − 123
+ − 124
void __PHYSFS_platformEnumerateFiles(const char *dirname,
+ − 125
int omitSymLinks,
+ − 126
PHYSFS_EnumFilesCallback callback,
+ − 127
const char *origdir,
+ − 128
void *callbackdata)
+ − 129
{
+ − 130
DIR *dir;
+ − 131
struct dirent *ent;
+ − 132
int bufsize = 0;
+ − 133
char *buf = NULL;
+ − 134
int dlen = 0;
+ − 135
+ − 136
if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
+ − 137
{
+ − 138
dlen = strlen(dirname);
+ − 139
bufsize = dlen + 256;
+ − 140
buf = (char *) allocator.Malloc(bufsize);
+ − 141
if (buf == NULL)
+ − 142
return;
+ − 143
strcpy(buf, dirname);
+ − 144
if (buf[dlen - 1] != '/')
+ − 145
{
+ − 146
buf[dlen++] = '/';
+ − 147
buf[dlen] = '\0';
+ − 148
} /* if */
+ − 149
} /* if */
+ − 150
+ − 151
errno = 0;
+ − 152
dir = opendir(dirname);
+ − 153
if (dir == NULL)
+ − 154
{
+ − 155
allocator.Free(buf);
+ − 156
return;
+ − 157
} /* if */
+ − 158
+ − 159
while ((ent = readdir(dir)) != NULL)
+ − 160
{
+ − 161
if (strcmp(ent->d_name, ".") == 0)
+ − 162
continue;
+ − 163
+ − 164
if (strcmp(ent->d_name, "..") == 0)
+ − 165
continue;
+ − 166
+ − 167
if (omitSymLinks)
+ − 168
{
+ − 169
PHYSFS_Stat statbuf;
+ − 170
int exists = 0;
+ − 171
char *p;
+ − 172
int len = strlen(ent->d_name) + dlen + 1;
+ − 173
if (len > bufsize)
+ − 174
{
+ − 175
p = (char *) allocator.Realloc(buf, len);
+ − 176
if (p == NULL)
+ − 177
continue;
+ − 178
buf = p;
+ − 179
bufsize = len;
+ − 180
} /* if */
+ − 181
+ − 182
strcpy(buf + dlen, ent->d_name);
+ − 183
+ − 184
if (!__PHYSFS_platformStat(buf, &exists, &statbuf))
+ − 185
continue;
+ − 186
else if (!exists)
+ − 187
continue; /* probably can't happen, but just in case. */
+ − 188
else if (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK)
+ − 189
continue;
+ − 190
} /* if */
+ − 191
+ − 192
callback(callbackdata, origdir, ent->d_name);
+ − 193
} /* while */
+ − 194
+ − 195
allocator.Free(buf);
+ − 196
closedir(dir);
+ − 197
} /* __PHYSFS_platformEnumerateFiles */
+ − 198
+ − 199
+ − 200
int __PHYSFS_platformMkDir(const char *path)
+ − 201
{
+ − 202
const int rc = mkdir(path, S_IRWXU);
+ − 203
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
+ − 204
return 1;
+ − 205
} /* __PHYSFS_platformMkDir */
+ − 206
+ − 207
+ − 208
static void *doOpen(const char *filename, int mode)
+ − 209
{
+ − 210
const int appending = (mode & O_APPEND);
+ − 211
int fd;
+ − 212
int *retval;
+ − 213
errno = 0;
+ − 214
+ − 215
/* O_APPEND doesn't actually behave as we'd like. */
+ − 216
mode &= ~O_APPEND;
+ − 217
+ − 218
fd = open(filename, mode, S_IRUSR | S_IWUSR);
+ − 219
BAIL_IF_MACRO(fd < 0, errcodeFromErrno(), NULL);
+ − 220
+ − 221
if (appending)
+ − 222
{
+ − 223
if (lseek(fd, 0, SEEK_END) < 0)
+ − 224
{
+ − 225
const int err = errno;
+ − 226
close(fd);
+ − 227
BAIL_MACRO(errcodeFromErrnoError(err), NULL);
+ − 228
} /* if */
+ − 229
} /* if */
+ − 230
+ − 231
retval = (int *) allocator.Malloc(sizeof (int));
+ − 232
if (!retval)
+ − 233
{
+ − 234
close(fd);
+ − 235
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
+ − 236
} /* if */
+ − 237
+ − 238
*retval = fd;
+ − 239
return ((void *) retval);
+ − 240
} /* doOpen */
+ − 241
+ − 242
+ − 243
void *__PHYSFS_platformOpenRead(const char *filename)
+ − 244
{
+ − 245
return doOpen(filename, O_RDONLY);
+ − 246
} /* __PHYSFS_platformOpenRead */
+ − 247
+ − 248
+ − 249
void *__PHYSFS_platformOpenWrite(const char *filename)
+ − 250
{
+ − 251
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
+ − 252
} /* __PHYSFS_platformOpenWrite */
+ − 253
+ − 254
+ − 255
void *__PHYSFS_platformOpenAppend(const char *filename)
+ − 256
{
+ − 257
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
+ − 258
} /* __PHYSFS_platformOpenAppend */
+ − 259
+ − 260
+ − 261
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
if (!__PHYSFS_ui64FitsAddressSpace(len))
+ − 268
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
assert(rc >= 0);
+ − 273
assert(rc <= len);
+ − 274
return (PHYSFS_sint64) rc;
+ − 275
} /* __PHYSFS_platformRead */
+ − 276
+ − 277
+ − 278
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
if (!__PHYSFS_ui64FitsAddressSpace(len))
+ − 285
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
assert(rc >= 0);
+ − 290
assert(rc <= len);
+ − 291
return (PHYSFS_sint64) rc;
+ − 292
} /* __PHYSFS_platformWrite */
+ − 293
+ − 294
+ − 295
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
+ − 296
{
+ − 297
const int fd = *((int *) opaque);
+ − 298
const int rc = lseek(fd, (off_t) pos, SEEK_SET);
+ − 299
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
+ − 300
return 1;
+ − 301
} /* __PHYSFS_platformSeek */
+ − 302
+ − 303
+ − 304
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
+ − 305
{
+ − 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
} /* __PHYSFS_platformTell */
+ − 312
+ − 313
+ − 314
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
+ − 315
{
+ − 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
} /* __PHYSFS_platformFileLength */
+ − 321
+ − 322
+ − 323
int __PHYSFS_platformFlush(void *opaque)
+ − 324
{
+ − 325
const int fd = *((int *) opaque);
+ − 326
BAIL_IF_MACRO(fsync(fd) == -1, errcodeFromErrno(), 0);
+ − 327
return 1;
+ − 328
} /* __PHYSFS_platformFlush */
+ − 329
+ − 330
+ − 331
void __PHYSFS_platformClose(void *opaque)
+ − 332
{
+ − 333
const int fd = *((int *) opaque);
+ − 334
(void) close(fd); /* we don't check this. You should have used flush! */
+ − 335
allocator.Free(opaque);
+ − 336
} /* __PHYSFS_platformClose */
+ − 337
+ − 338
+ − 339
int __PHYSFS_platformDelete(const char *path)
+ − 340
{
+ − 341
BAIL_IF_MACRO(remove(path) == -1, errcodeFromErrno(), 0);
+ − 342
return 1;
+ − 343
} /* __PHYSFS_platformDelete */
+ − 344
+ − 345
+ − 346
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st)
+ − 347
{
+ − 348
struct stat statbuf;
+ − 349
+ − 350
if (lstat(filename, &statbuf) == -1)
+ − 351
{
+ − 352
*exists = (errno != ENOENT);
+ − 353
BAIL_MACRO(errcodeFromErrno(), 0);
+ − 354
} /* if */
+ − 355
+ − 356
*exists = 1;
+ − 357
+ − 358
if (S_ISREG(statbuf.st_mode))
+ − 359
{
+ − 360
st->filetype = PHYSFS_FILETYPE_REGULAR;
+ − 361
st->filesize = statbuf.st_size;
+ − 362
} /* if */
+ − 363
+ − 364
else if(S_ISDIR(statbuf.st_mode))
+ − 365
{
+ − 366
st->filetype = PHYSFS_FILETYPE_DIRECTORY;
+ − 367
st->filesize = 0;
+ − 368
} /* else if */
+ − 369
+ − 370
else
+ − 371
{
+ − 372
st->filetype = PHYSFS_FILETYPE_OTHER;
+ − 373
st->filesize = statbuf.st_size;
+ − 374
} /* else */
+ − 375
+ − 376
st->modtime = statbuf.st_mtime;
+ − 377
st->createtime = statbuf.st_ctime;
+ − 378
st->accesstime = statbuf.st_atime;
+ − 379
+ − 380
/* !!! FIXME: maybe we should just report full permissions? */
+ − 381
st->readonly = access(filename, W_OK);
+ − 382
return 1;
+ − 383
} /* __PHYSFS_platformStat */
+ − 384
+ − 385
+ − 386
#ifndef PHYSFS_PLATFORM_BEOS /* BeOS has its own code in platform_beos.cpp */
+ − 387
#if (defined PHYSFS_NO_THREAD_SUPPORT)
+ − 388
+ − 389
void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); }
+ − 390
void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); }
+ − 391
void __PHYSFS_platformDestroyMutex(void *mutex) {}
+ − 392
int __PHYSFS_platformGrabMutex(void *mutex) { return 1; }
+ − 393
void __PHYSFS_platformReleaseMutex(void *mutex) {}
+ − 394
+ − 395
#else
+ − 396
+ − 397
typedef struct
+ − 398
{
+ − 399
pthread_mutex_t mutex;
+ − 400
pthread_t owner;
+ − 401
PHYSFS_uint32 count;
+ − 402
} PthreadMutex;
+ − 403
+ − 404
+ − 405
void *__PHYSFS_platformGetThreadID(void)
+ − 406
{
+ − 407
return ( (void *) ((size_t) pthread_self()) );
+ − 408
} /* __PHYSFS_platformGetThreadID */
+ − 409
+ − 410
+ − 411
void *__PHYSFS_platformCreateMutex(void)
+ − 412
{
+ − 413
int rc;
+ − 414
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
+ − 415
BAIL_IF_MACRO(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
+ − 416
rc = pthread_mutex_init(&m->mutex, NULL);
+ − 417
if (rc != 0)
+ − 418
{
+ − 419
allocator.Free(m);
+ − 420
BAIL_MACRO(PHYSFS_ERR_OS_ERROR, NULL);
+ − 421
} /* if */
+ − 422
+ − 423
m->count = 0;
+ − 424
m->owner = (pthread_t) 0xDEADBEEF;
+ − 425
return ((void *) m);
+ − 426
} /* __PHYSFS_platformCreateMutex */
+ − 427
+ − 428
+ − 429
void __PHYSFS_platformDestroyMutex(void *mutex)
+ − 430
{
+ − 431
PthreadMutex *m = (PthreadMutex *) mutex;
+ − 432
+ − 433
/* Destroying a locked mutex is a bug, but we'll try to be helpful. */
+ − 434
if ((m->owner == pthread_self()) && (m->count > 0))
+ − 435
pthread_mutex_unlock(&m->mutex);
+ − 436
+ − 437
pthread_mutex_destroy(&m->mutex);
+ − 438
allocator.Free(m);
+ − 439
} /* __PHYSFS_platformDestroyMutex */
+ − 440
+ − 441
+ − 442
int __PHYSFS_platformGrabMutex(void *mutex)
+ − 443
{
+ − 444
PthreadMutex *m = (PthreadMutex *) mutex;
+ − 445
pthread_t tid = pthread_self();
+ − 446
if (m->owner != tid)
+ − 447
{
+ − 448
if (pthread_mutex_lock(&m->mutex) != 0)
+ − 449
return 0;
+ − 450
m->owner = tid;
+ − 451
} /* if */
+ − 452
+ − 453
m->count++;
+ − 454
return 1;
+ − 455
} /* __PHYSFS_platformGrabMutex */
+ − 456
+ − 457
+ − 458
void __PHYSFS_platformReleaseMutex(void *mutex)
+ − 459
{
+ − 460
PthreadMutex *m = (PthreadMutex *) mutex;
+ − 461
assert(m->owner == pthread_self()); /* catch programming errors. */
+ − 462
assert(m->count > 0); /* catch programming errors. */
+ − 463
if (m->owner == pthread_self())
+ − 464
{
+ − 465
if (--m->count == 0)
+ − 466
{
+ − 467
m->owner = (pthread_t) 0xDEADBEEF;
+ − 468
pthread_mutex_unlock(&m->mutex);
+ − 469
} /* if */
+ − 470
} /* if */
+ − 471
} /* __PHYSFS_platformReleaseMutex */
+ − 472
+ − 473
#endif /* !PHYSFS_NO_THREAD_SUPPORT */
+ − 474
#endif /* !PHYSFS_PLATFORM_BEOS */
+ − 475
+ − 476
#endif /* PHYSFS_PLATFORM_POSIX */
+ − 477
+ − 478
/* end of posix.c ... */
+ − 479