/
platform_unix.c
448 lines (351 loc) · 11.2 KB
1
2
3
/*
* Unix 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_UNIX
13
14
15
16
17
18
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
19
#include <sys/param.h>
20
21
22
#include <dirent.h>
#include <time.h>
#include <errno.h>
23
24
#if (!defined PHYSFS_NO_THREAD_SUPPORT)
25
26
27
#include <pthread.h>
#endif
28
29
30
31
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
32
# include <sys/mount.h>
33
# include <sys/ucred.h>
34
#endif
35
36
37
38
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
39
40
41
42
43
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
44
45
#include "physfs_internal.h"
46
47
48
49
/* !!! FIXME: we should probably remove MAXPATHLEN entirely, if we can. */
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
50
51
52
int __PHYSFS_platformInit(void)
{
53
return 1; /* always succeed. */
54
55
56
57
58
} /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void)
{
59
return 1; /* always succeed. */
60
61
62
} /* __PHYSFS_platformDeinit */
63
/* Stub version for platforms without CD-ROM support. */
64
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
65
{
66
67
#if (defined PHYSFS_NO_CDROM_SUPPORT)
/* no-op. */
68
69
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
70
int i;
71
72
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_WAIT);
73
74
75
for (i = 0; i < mounts; i++)
{
76
77
int add_it = 0;
78
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
79
add_it = 1;
80
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
81
add_it = 1;
82
83
/* add other mount types here */
84
85
if (add_it)
86
cb(data, mntbufp[i].f_mntonname);
87
} /* for */
88
89
#elif (defined PHYSFS_HAVE_MNTENT_H)
90
91
92
93
FILE *mounts = NULL;
struct mntent *ent = NULL;
mounts = setmntent("/etc/mtab", "r");
94
BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/);
95
96
97
98
99
100
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
101
102
103
104
105
106
107
108
else if (strcmp(ent->mnt_type, "udf") == 0)
add_it = 1;
/* !!! FIXME: these might pick up floppy drives, right? */
else if (strcmp(ent->mnt_type, "auto") == 0)
add_it = 1;
else if (strcmp(ent->mnt_type, "supermount") == 0)
add_it = 1;
109
110
111
/* !!! FIXME: udf? automount? */
112
/* add other mount types here */
113
114
if (add_it)
115
cb(data, ent->mnt_dir);
116
117
118
} /* while */
endmntent(mounts);
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
FILE *mounts = fopen(MNTTAB, "r");
struct mnttab ent;
BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/);
while (getmntent(mounts, &ent) == 0)
{
int add_it = 0;
if (strcmp(ent.mnt_fstype, "hsfs") == 0)
add_it = 1;
/* add other mount types here */
if (add_it)
cb(data, ent.mnt_mountp);
} /* while */
fclose(mounts);
#else
#error Unknown platform. Should have defined PHYSFS_NO_CDROM_SUPPORT, perhaps.
141
#endif
142
} /* __PHYSFS_platformDetectAvailableCDs */
143
144
145
146
/* this is in posix.c ... */
extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
147
148
149
150
151
152
153
154
/*
* See where program (bin) resides in the $PATH specified by (envr).
* returns a copy of the first element in envr that contains it, or NULL
* if it doesn't exist or there were other problems. PHYSFS_SetError() is
* called if we have a problem.
*
155
* (envr) will be scribbled over, and you are expected to allocator.Free() the
156
157
158
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
159
{
160
161
162
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
163
164
char *ptr;
165
166
BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
167
168
169
do
{
170
171
size_t size;
ptr = strchr(start, ':'); /* find next $PATH separator. */
172
173
174
if (ptr)
*ptr = '\0';
175
176
size = strlen(start) + strlen(bin) + 2;
if (size > alloc_size)
177
{
178
char *x = (char *) allocator.Realloc(exe, size);
179
180
181
if (x == NULL)
{
if (exe != NULL)
182
allocator.Free(exe);
183
184
185
186
187
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
alloc_size = size;
exe = x;
188
} /* if */
189
190
/* build full binary path... */
191
strcpy(exe, start);
192
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
193
strcat(exe, "/");
194
195
196
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
197
{
198
strcpy(exe, start); /* i'm lazy. piss off. */
199
return exe;
200
} /* if */
201
202
start = ptr + 1; /* start points to beginning of next element. */
203
204
} while (ptr != NULL);
205
if (exe != NULL)
206
allocator.Free(exe);
207
208
return NULL; /* doesn't exist in path. */
209
210
211
} /* findBinaryInPath */
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
static char *readSymLink(const char *path)
{
ssize_t len = 64;
ssize_t rc = -1;
char *retval = NULL;
while (1)
{
char *ptr = (char *) allocator.Realloc(retval, (size_t) len);
if (ptr == NULL)
break; /* out of memory. */
retval = ptr;
rc = readlink(path, retval, len);
if (rc == -1)
break; /* not a symlink, i/o error, etc. */
else if (rc < len)
{
retval[rc] = '\0'; /* readlink doesn't null-terminate. */
return retval; /* we're good to go. */
} /* else if */
len *= 2; /* grow buffer, try again. */
} /* while */
if (retval != NULL)
allocator.Free(retval);
return NULL;
} /* readSymLink */
244
245
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
246
247
248
249
250
char *retval = NULL;
char *envr = NULL;
/* fast path: default behaviour can handle this. */
if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
251
return NULL; /* higher level will parse out real path from argv0. */
252
253
254
255
256
257
/*
* Try to avoid using argv0 unless forced to. If there's a Linux-like
* /proc filesystem, you can get the full path to the current process from
* the /proc/self/exe symlink.
*/
258
retval = readSymLink("/proc/self/exe");
259
260
261
262
263
264
265
266
267
268
if (retval == NULL)
{
/* older kernels don't have /proc/self ... try PID version... */
const unsigned long long pid = (unsigned long long) getpid();
char path[64];
const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid);
if ( (rc > 0) && (rc < sizeof(path)) )
retval = readSymLink(path);
} /* if */
269
if (retval != NULL) /* chop off filename. */
270
{
271
272
273
char *ptr = strrchr(retval, '/');
if (ptr != NULL)
*ptr = '\0';
274
} /* if */
275
276
277
278
279
280
281
282
283
if ((retval == NULL) && (argv0 != NULL))
{
/* If there's no dirsep on argv0, then look through $PATH for it. */
envr = __PHYSFS_platformCopyEnvironmentVariable("PATH");
BAIL_IF_MACRO(!envr, NULL, NULL);
retval = findBinaryInPath(argv0, envr);
allocator.Free(envr);
} /* if */
284
285
286
287
288
289
290
291
292
if (retval != NULL)
{
/* try to shrink buffer... */
char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1);
if (ptr != NULL)
retval = ptr; /* oh well if it failed. */
} /* if */
293
return retval;
294
295
296
} /* __PHYSFS_platformCalcBaseDir */
297
298
299
300
301
302
303
char *__PHYSFS_platformRealPath(const char *path)
{
char resolved_path[MAXPATHLEN];
char *retval = NULL;
errno = 0;
BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL);
304
retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
305
306
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, resolved_path);
307
308
return retval;
309
310
} /* __PHYSFS_platformRealPath */
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
char *__PHYSFS_platformCurrentDir(void)
{
/*
* This can't just do platformRealPath("."), since that would eventually
* just end up calling back into here.
*/
int allocSize = 0;
char *retval = NULL;
char *ptr;
do
{
allocSize += 100;
ptr = (char *) allocator.Realloc(retval, allocSize);
if (ptr == NULL)
{
if (retval != NULL)
allocator.Free(retval);
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
retval = ptr;
ptr = getcwd(retval, allocSize);
} while (ptr == NULL && errno == ERANGE);
if (ptr == NULL && errno)
{
/*
* getcwd() failed for some reason, for example current
* directory not existing.
*/
if (retval != NULL)
allocator.Free(retval);
BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
} /* if */
349
return retval;
350
351
352
} /* __PHYSFS_platformCurrentDir */
353
354
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
{
355
return 0; /* just use malloc() and friends. */
356
357
358
} /* __PHYSFS_platformSetDefaultAllocator */
359
#if (defined PHYSFS_NO_THREAD_SUPPORT)
360
361
362
void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); }
void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); }
363
void __PHYSFS_platformDestroyMutex(void *mutex) {}
364
int __PHYSFS_platformGrabMutex(void *mutex) { return 1; }
365
366
367
368
void __PHYSFS_platformReleaseMutex(void *mutex) {}
#else
369
370
371
372
373
374
375
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
376
377
void *__PHYSFS_platformGetThreadID(void)
378
{
379
return ( (void *) ((size_t) pthread_self()) );
380
381
382
} /* __PHYSFS_platformGetThreadID */
383
384
385
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
386
387
388
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(&m->mutex, NULL);
389
390
if (rc != 0)
{
391
allocator.Free(m);
392
393
394
BAIL_MACRO(strerror(rc), NULL);
} /* if */
395
396
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
397
return ((void *) m);
398
399
400
401
402
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
403
404
405
406
407
408
409
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);
410
allocator.Free(m);
411
412
413
414
415
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
416
417
418
419
420
PthreadMutex *m = (PthreadMutex *) mutex;
pthread_t tid = pthread_self();
if (m->owner != tid)
{
if (pthread_mutex_lock(&m->mutex) != 0)
421
return 0;
422
423
424
425
m->owner = tid;
} /* if */
m->count++;
426
return 1;
427
428
429
430
431
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
432
433
434
435
436
437
438
439
440
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 */
441
442
} /* __PHYSFS_platformReleaseMutex */
443
#endif /* !PHYSFS_NO_THREAD_SUPPORT */
444
445
#endif /* PHYSFS_PLATFORM_UNIX */
446
447
/* end of unix.c ... */