/
platform_unix.c
451 lines (354 loc) · 11.3 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
#include <stdio.h>
#include <stdlib.h>
16
17
18
19
20
21
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
22
#include <sys/param.h>
23
24
25
#include <dirent.h>
#include <time.h>
#include <errno.h>
26
27
#if (!defined PHYSFS_NO_PTHREADS_SUPPORT)
28
29
30
#include <pthread.h>
#endif
31
32
33
34
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
35
# include <sys/mount.h>
36
# include <sys/ucred.h>
37
#endif
38
39
40
41
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
42
43
44
45
46
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
47
48
#include "physfs_internal.h"
49
50
51
52
/* !!! FIXME: we should probably remove MAXPATHLEN entirely, if we can. */
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
53
54
55
int __PHYSFS_platformInit(void)
{
56
return 1; /* always succeed. */
57
58
59
60
61
} /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void)
{
62
return 1; /* always succeed. */
63
64
65
} /* __PHYSFS_platformDeinit */
66
/* Stub version for platforms without CD-ROM support. */
67
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
68
{
69
70
#if (defined PHYSFS_NO_CDROM_SUPPORT)
/* no-op. */
71
72
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
73
int i;
74
75
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_WAIT);
76
77
78
for (i = 0; i < mounts; i++)
{
79
80
int add_it = 0;
81
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
82
add_it = 1;
83
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
84
add_it = 1;
85
86
/* add other mount types here */
87
88
if (add_it)
89
cb(data, mntbufp[i].f_mntonname);
90
} /* for */
91
92
#elif (defined PHYSFS_HAVE_MNTENT_H)
93
94
95
96
FILE *mounts = NULL;
struct mntent *ent = NULL;
mounts = setmntent("/etc/mtab", "r");
97
BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/);
98
99
100
101
102
103
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
104
105
106
107
108
109
110
111
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;
112
113
114
/* !!! FIXME: udf? automount? */
115
/* add other mount types here */
116
117
if (add_it)
118
cb(data, ent->mnt_dir);
119
120
121
} /* while */
endmntent(mounts);
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#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.
144
#endif
145
} /* __PHYSFS_platformDetectAvailableCDs */
146
147
148
149
/* this is in posix.c ... */
extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
150
151
152
153
154
155
156
157
/*
* 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.
*
158
* (envr) will be scribbled over, and you are expected to allocator.Free() the
159
160
161
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
162
{
163
164
165
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
166
167
char *ptr;
168
169
BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
170
171
172
do
{
173
174
size_t size;
ptr = strchr(start, ':'); /* find next $PATH separator. */
175
176
177
if (ptr)
*ptr = '\0';
178
179
size = strlen(start) + strlen(bin) + 2;
if (size > alloc_size)
180
{
181
char *x = (char *) allocator.Realloc(exe, size);
182
183
184
if (x == NULL)
{
if (exe != NULL)
185
allocator.Free(exe);
186
187
188
189
190
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
alloc_size = size;
exe = x;
191
} /* if */
192
193
/* build full binary path... */
194
strcpy(exe, start);
195
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
196
strcat(exe, "/");
197
198
199
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
200
{
201
strcpy(exe, start); /* i'm lazy. piss off. */
202
return exe;
203
} /* if */
204
205
start = ptr + 1; /* start points to beginning of next element. */
206
207
} while (ptr != NULL);
208
if (exe != NULL)
209
allocator.Free(exe);
210
211
return NULL; /* doesn't exist in path. */
212
213
214
} /* findBinaryInPath */
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
244
245
246
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 */
247
248
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
249
250
251
252
253
char *retval = NULL;
char *envr = NULL;
/* fast path: default behaviour can handle this. */
if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
254
return NULL; /* higher level will parse out real path from argv0. */
255
256
257
258
259
260
/*
* 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.
*/
261
retval = readSymLink("/proc/self/exe");
262
263
264
265
266
267
268
269
270
271
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 */
272
if (retval != NULL) /* chop off filename. */
273
{
274
275
276
char *ptr = strrchr(retval, '/');
if (ptr != NULL)
*ptr = '\0';
277
} /* if */
278
279
280
281
282
283
284
285
286
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 */
287
288
289
290
291
292
293
294
295
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 */
296
return retval;
297
298
299
} /* __PHYSFS_platformCalcBaseDir */
300
301
302
303
304
305
306
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);
307
retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
308
309
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, resolved_path);
310
311
return retval;
312
313
} /* __PHYSFS_platformRealPath */
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
349
350
351
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 */
352
return retval;
353
354
355
} /* __PHYSFS_platformCurrentDir */
356
357
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
{
358
return 0; /* just use malloc() and friends. */
359
360
361
} /* __PHYSFS_platformSetDefaultAllocator */
362
#if (defined PHYSFS_NO_PTHREADS_SUPPORT)
363
364
365
void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); }
void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); }
366
void __PHYSFS_platformDestroyMutex(void *mutex) {}
367
int __PHYSFS_platformGrabMutex(void *mutex) { return 1; }
368
369
370
371
void __PHYSFS_platformReleaseMutex(void *mutex) {}
#else
372
373
374
375
376
377
378
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
379
380
void *__PHYSFS_platformGetThreadID(void)
381
{
382
return ( (void *) ((size_t) pthread_self()) );
383
384
385
} /* __PHYSFS_platformGetThreadID */
386
387
388
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
389
390
391
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(&m->mutex, NULL);
392
393
if (rc != 0)
{
394
allocator.Free(m);
395
396
397
BAIL_MACRO(strerror(rc), NULL);
} /* if */
398
399
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
400
return ((void *) m);
401
402
403
404
405
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
406
407
408
409
410
411
412
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);
413
allocator.Free(m);
414
415
416
417
418
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
419
420
421
422
423
PthreadMutex *m = (PthreadMutex *) mutex;
pthread_t tid = pthread_self();
if (m->owner != tid)
{
if (pthread_mutex_lock(&m->mutex) != 0)
424
return 0;
425
426
427
428
m->owner = tid;
} /* if */
m->count++;
429
return 1;
430
431
432
433
434
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
435
436
437
438
439
440
441
442
443
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 */
444
445
} /* __PHYSFS_platformReleaseMutex */
446
#endif /* !PHYSFS_NO_PTHREADS_SUPPORT */
447
448
#endif /* PHYSFS_PLATFORM_UNIX */
449
450
/* end of unix.c ... */