/
platform_unix.c
395 lines (310 loc) · 10 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
#include <sys/mount.h>
27
28
29
30
31
#if (!defined PHYSFS_NO_PTHREADS_SUPPORT)
#include <pthread.h>
#endif
32
33
34
35
36
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
# include <sys/ucred.h>
37
#endif
38
39
40
41
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
42
43
44
#include "physfs_internal.h"
45
46
47
48
49
50
51
52
53
54
55
56
57
int __PHYSFS_platformInit(void)
{
return(1); /* always succeed. */
} /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void)
{
return(1); /* always succeed. */
} /* __PHYSFS_platformDeinit */
58
59
60
#ifdef PHYSFS_NO_CDROM_SUPPORT
/* Stub version for platforms without CD-ROM support. */
61
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
62
63
64
{
} /* __PHYSFS_platformDetectAvailableCDs */
65
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
66
67
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
68
{
69
int i;
70
71
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_WAIT);
72
73
74
for (i = 0; i < mounts; i++)
{
75
76
int add_it = 0;
77
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
78
add_it = 1;
79
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
80
add_it = 1;
81
82
/* add other mount types here */
83
84
if (add_it)
85
cb(data, mntbufp[i].f_mntonname);
86
} /* for */
87
88
} /* __PHYSFS_platformDetectAvailableCDs */
89
#elif (defined PHYSFS_HAVE_MNTENT_H)
90
91
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
92
{
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
/* add other mount types here */
106
107
if (add_it)
108
cb(data, ent->mnt_dir);
109
110
111
} /* while */
endmntent(mounts);
112
113
114
115
} /* __PHYSFS_platformDetectAvailableCDs */
#endif
116
117
118
119
/* this is in posix.c ... */
extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
120
121
122
123
124
125
126
127
/*
* 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.
*
128
* (envr) will be scribbled over, and you are expected to allocator.Free() the
129
130
131
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
132
{
133
134
135
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
136
137
char *ptr;
138
139
BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
140
141
142
do
{
143
144
size_t size;
ptr = strchr(start, ':'); /* find next $PATH separator. */
145
146
147
if (ptr)
*ptr = '\0';
148
149
size = strlen(start) + strlen(bin) + 2;
if (size > alloc_size)
150
{
151
char *x = (char *) allocator.Realloc(exe, size);
152
153
154
if (x == NULL)
{
if (exe != NULL)
155
allocator.Free(exe);
156
157
158
159
160
BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
} /* if */
alloc_size = size;
exe = x;
161
} /* if */
162
163
/* build full binary path... */
164
strcpy(exe, start);
165
if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
166
strcat(exe, "/");
167
168
169
strcat(exe, bin);
if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
170
{
171
172
173
strcpy(exe, start); /* i'm lazy. piss off. */
return(exe);
} /* if */
174
175
start = ptr + 1; /* start points to beginning of next element. */
176
177
} while (ptr != NULL);
178
if (exe != NULL)
179
allocator.Free(exe);
180
181
182
183
184
185
186
return(NULL); /* doesn't exist in path. */
} /* findBinaryInPath */
char *__PHYSFS_platformCalcBaseDir(const char *argv0)
{
187
188
189
190
191
192
193
194
const char *PROC_SELF_EXE = "/proc/self/exe";
char *retval = NULL;
char *envr = NULL;
struct stat stbuf;
/* fast path: default behaviour can handle this. */
if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
return(NULL); /* higher level will parse out real path from argv0. */
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
* 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.
*/
if ((lstat(PROC_SELF_EXE, &stbuf) != -1) && (S_ISLNK(stbuf.st_mode)))
{
const size_t len = stbuf.st_size;
char *buf = (char *) allocator.Malloc(len+1);
if (buf != NULL) /* if NULL, maybe you'll get lucky later. */
{
if (readlink(PROC_SELF_EXE, buf, len) != len)
allocator.Free(buf);
else
{
buf[len] = '\0'; /* readlink doesn't null-terminate. */
retval = buf; /* we're good to go. */
} /* else */
} /* if */
} /* if */
216
217
218
219
220
221
222
223
224
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 */
225
226
227
228
229
return(retval);
} /* __PHYSFS_platformCalcBaseDir */
230
231
232
233
234
235
236
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);
237
retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
238
239
BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, resolved_path);
240
241
242
243
return(retval);
} /* __PHYSFS_platformRealPath */
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
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 */
return(retval);
} /* __PHYSFS_platformCurrentDir */
286
287
288
289
290
291
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
{
return(0); /* just use malloc() and friends. */
} /* __PHYSFS_platformSetDefaultAllocator */
292
#if (defined PHYSFS_NO_PTHREADS_SUPPORT)
293
294
295
296
297
298
299
300
301
PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) { return(0x0001); }
void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); }
void __PHYSFS_platformDestroyMutex(void *mutex) {}
int __PHYSFS_platformGrabMutex(void *mutex) { return(1); }
void __PHYSFS_platformReleaseMutex(void *mutex) {}
#else
302
303
304
305
306
307
308
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/* Just in case; this is a panic value. */
#if ((!defined SIZEOF_INT) || (SIZEOF_INT <= 0))
# define SIZEOF_INT 4
#endif
#if (SIZEOF_INT == 4)
# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint32) (thr)) )
#elif (SIZEOF_INT == 2)
# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint16) (thr)) )
#elif (SIZEOF_INT == 1)
# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint8) (thr)) )
#else
# define PHTREAD_TO_UI64(thr) ((PHYSFS_uint64) (thr))
#endif
323
324
325
PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
{
326
return(PHTREAD_TO_UI64(pthread_self()));
327
328
329
} /* __PHYSFS_platformGetThreadID */
330
331
332
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
333
334
335
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
rc = pthread_mutex_init(&m->mutex, NULL);
336
337
if (rc != 0)
{
338
allocator.Free(m);
339
340
341
BAIL_MACRO(strerror(rc), NULL);
} /* if */
342
343
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
344
345
346
347
348
349
return((void *) m);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
350
351
352
353
354
355
356
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);
357
allocator.Free(m);
358
359
360
361
362
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
363
364
365
366
367
368
369
370
371
372
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++;
373
return(1);
374
375
376
377
378
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
379
380
381
382
383
384
385
386
387
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 */
388
389
} /* __PHYSFS_platformReleaseMutex */
390
391
#endif /* !PHYSFS_NO_PTHREADS_SUPPORT */
392
#endif /* PHYSFS_PLATFORM_UNIX */
393
394
/* end of unix.c ... */