/
platform_unix.c
371 lines (299 loc) · 9.93 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 <ctype.h>
#include <unistd.h>
16
#include <stdlib.h>
17
18
19
#include <sys/types.h>
#include <pwd.h>
#include <sys/stat.h>
20
#include <sys/param.h>
21
22
23
#include <dirent.h>
#include <time.h>
#include <errno.h>
24
#include <limits.h>
25
26
27
28
29
30
31
32
33
#if PHYSFS_PLATFORM_LINUX && !defined(PHYSFS_HAVE_MNTENT_H)
#define PHYSFS_HAVE_MNTENT_H 1
#elif PHYSFS_PLATFORM_SOLARIS && !defined(PHYSFS_HAVE_SYS_MNTTAB_H)
#define PHYSFS_HAVE_SYS_MNTTAB_H 1
#elif PHYSFS_PLATFORM_BSD && !defined(PHYSFS_HAVE_SYS_UCRED_H)
#define PHYSFS_HAVE_SYS_UCRED_H 1
#endif
34
35
36
37
#ifdef PHYSFS_HAVE_SYS_UCRED_H
# ifdef PHYSFS_HAVE_MNTENT_H
# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
# endif
38
# include <sys/mount.h>
39
# include <sys/ucred.h>
40
#endif
41
42
43
44
#ifdef PHYSFS_HAVE_MNTENT_H
#include <mntent.h>
#endif
45
46
47
48
49
#ifdef PHYSFS_HAVE_SYS_MNTTAB_H
#include <sys/mnttab.h>
#endif
50
51
#include "physfs_internal.h"
52
53
int __PHYSFS_platformInit(void)
{
54
return 1; /* always succeed. */
55
56
57
58
59
} /* __PHYSFS_platformInit */
int __PHYSFS_platformDeinit(void)
{
60
return 1; /* always succeed. */
61
62
63
} /* __PHYSFS_platformDeinit */
64
/* Stub version for platforms without CD-ROM support. */
65
void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
66
{
67
68
#if (defined PHYSFS_NO_CDROM_SUPPORT)
/* no-op. */
69
70
#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
71
int i;
72
73
struct statfs *mntbufp = NULL;
int mounts = getmntinfo(&mntbufp, MNT_WAIT);
74
75
76
for (i = 0; i < mounts; i++)
{
77
78
int add_it = 0;
79
if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
80
add_it = 1;
81
else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
82
add_it = 1;
83
84
/* add other mount types here */
85
86
if (add_it)
87
cb(data, mntbufp[i].f_mntonname);
88
} /* for */
89
90
#elif (defined PHYSFS_HAVE_MNTENT_H)
91
92
93
94
FILE *mounts = NULL;
struct mntent *ent = NULL;
mounts = setmntent("/etc/mtab", "r");
95
BAIL_IF_MACRO(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
96
97
98
99
100
101
while ( (ent = getmntent(mounts)) != NULL )
{
int add_it = 0;
if (strcmp(ent->mnt_type, "iso9660") == 0)
add_it = 1;
102
103
104
105
106
107
108
109
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;
110
111
112
/* !!! FIXME: udf? automount? */
113
/* add other mount types here */
114
115
if (add_it)
116
cb(data, ent->mnt_dir);
117
118
119
} /* while */
endmntent(mounts);
120
121
122
123
124
#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H)
FILE *mounts = fopen(MNTTAB, "r");
struct mnttab ent;
125
BAIL_IF_MACRO(mounts == NULL, PHYSFS_ERR_IO, /*return void*/);
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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.
142
#endif
143
} /* __PHYSFS_platformDetectAvailableCDs */
144
145
146
147
148
149
150
151
/*
* 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.
*
152
* (envr) will be scribbled over, and you are expected to allocator.Free() the
153
154
155
* return value when you're done with it.
*/
static char *findBinaryInPath(const char *bin, char *envr)
156
{
157
158
159
size_t alloc_size = 0;
char *exe = NULL;
char *start = envr;
160
161
char *ptr;
162
163
assert(bin != NULL);
assert(envr != NULL);
164
165
166
do
{
167
size_t size;
168
169
size_t binlen;
170
ptr = strchr(start, ':'); /* find next $PATH separator. */
171
172
173
if (ptr)
*ptr = '\0';
174
175
binlen = strlen(bin);
size = strlen(start) + binlen + 2;
176
if (size >= alloc_size)
177
{
178
char *x = (char *) allocator.Realloc(exe, size);
179
if (!x)
180
181
{
if (exe != NULL)
182
allocator.Free(exe);
183
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
184
185
186
187
} /* 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
exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */
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
char *retval = NULL;
247
const char *envr = NULL;
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/* Try to avoid using argv0 unless forced to. Try system-specific stuff. */
#if PHYSFS_PLATFORM_FREEBSD
{
char fullpath[PATH_MAX];
size_t buflen = sizeof (fullpath);
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1)
retval = __PHYSFS_strdup(fullpath);
}
#elif PHYSFS_PLATFORM_SOLARIS
{
const char *path = getexecname();
if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */
retval = __PHYSFS_strdup(path);
}
#endif
if (retval)
return retval; /* already got it. */
/* If there's a Linux-like /proc filesystem, you can get the full path to
* the current process from a symlink in there.
272
*/
273
274
if (access("/proc", F_OK) == 0)
275
{
276
277
278
279
280
281
282
283
284
285
286
287
retval = readSymLink("/proc/self/exe");
if (!retval) retval = readSymLink("/proc/curproc/file");
if (!retval) retval = readSymLink("/proc/curproc/exe");
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 */
288
289
} /* if */
290
if (retval != NULL) /* chop off filename. */
291
{
292
293
char *ptr = strrchr(retval, '/');
if (ptr != NULL)
294
*(ptr+1) = '\0';
295
296
297
298
299
else /* shouldn't happen, but just in case... */
{
allocator.Free(retval);
retval = NULL;
} /* else */
300
} /* if */
301
302
/* No /proc/self/exe, etc, but we have an argv[0] we can parse? */
303
304
if ((retval == NULL) && (argv0 != NULL))
{
305
306
307
308
/* fast path: default behaviour can handle this. */
if (strchr(argv0, '/') != NULL)
return NULL; /* higher level parses out real path from argv0. */
309
/* If there's no dirsep on argv0, then look through $PATH for it. */
310
311
312
313
314
315
316
317
318
envr = getenv("PATH");
if (envr != NULL)
{
char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1);
BAIL_IF_MACRO(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(path, envr);
retval = findBinaryInPath(argv0, path);
__PHYSFS_smallFree(path);
} /* if */
319
} /* if */
320
321
322
323
324
325
326
327
328
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 */
329
return retval;
330
331
332
} /* __PHYSFS_platformCalcBaseDir */
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/*
* We use XDG's base directory spec, even if you're not on Linux.
* This isn't strictly correct, but the results are relatively sane
* in any case.
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
const char *envr = getenv("XDG_DATA_HOME");
const char *append = "/";
char *retval = NULL;
size_t len = 0;
if (!envr)
{
/* You end up with "$HOME/.local/share/Game Name 2" */
envr = __PHYSFS_getUserDir();
BAIL_IF_MACRO(!envr, ERRPASS, NULL); /* oh well. */
append = ".local/share/";
} /* if */
355
len = strlen(envr) + strlen(append) + strlen(app) + 2;
356
357
retval = (char *) allocator.Malloc(len);
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
358
snprintf(retval, len, "%s%s%s/", envr, append, app);
359
360
361
362
return retval;
} /* __PHYSFS_platformCalcPrefDir */
363
364
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
{
365
return 0; /* just use malloc() and friends. */
366
367
} /* __PHYSFS_platformSetDefaultAllocator */
368
#endif /* PHYSFS_PLATFORM_UNIX */
369
370
/* end of unix.c ... */