diff options
Diffstat (limited to 'firmware/common/rbpaths.c')
-rw-r--r-- | firmware/common/rbpaths.c | 432 |
1 files changed, 0 insertions, 432 deletions
diff --git a/firmware/common/rbpaths.c b/firmware/common/rbpaths.c deleted file mode 100644 index 69543bc3a7..0000000000 --- a/firmware/common/rbpaths.c +++ /dev/null | |||
@@ -1,432 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Thomas Martitz | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | |||
23 | #include <stdio.h> /* snprintf */ | ||
24 | #include <stdlib.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <time.h> | ||
28 | #include <unistd.h> | ||
29 | #include "config.h" | ||
30 | #include "rbpaths.h" | ||
31 | #include "crc32.h" | ||
32 | #include "file.h" /* MAX_PATH */ | ||
33 | #include "logf.h" | ||
34 | #include "gcc_extensions.h" | ||
35 | #include "string-extra.h" | ||
36 | #include "filefuncs.h" | ||
37 | |||
38 | /* In this file we need the actual OS library functions, not the shadowed | ||
39 | * wrapper used within Rockbox' application code (except SDL adds | ||
40 | * another layer) */ | ||
41 | #undef open | ||
42 | #undef creat | ||
43 | #undef remove | ||
44 | #undef rename | ||
45 | #undef opendir | ||
46 | #undef closedir | ||
47 | #undef readdir | ||
48 | #undef mkdir | ||
49 | #undef rmdir | ||
50 | #undef dirent | ||
51 | #undef DIR | ||
52 | #undef readlink | ||
53 | |||
54 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
55 | static const char rbhome[] = "/sdcard"; | ||
56 | #elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) && !defined(__PCTOOL__) | ||
57 | const char *rbhome; | ||
58 | #else | ||
59 | /* YPR0, YPR1 */ | ||
60 | static const char rbhome[] = HOME_DIR; | ||
61 | #endif | ||
62 | |||
63 | #if !(defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) && !defined(__PCTOOL__) | ||
64 | /* Special dirs are user-accessible (and user-writable) dirs which take priority | ||
65 | * over the ones where Rockbox is installed to. Classic example would be | ||
66 | * $HOME/.config/rockbox.org vs /usr/share/rockbox */ | ||
67 | #define HAVE_SPECIAL_DIRS | ||
68 | #endif | ||
69 | |||
70 | /* flags for get_user_file_path() */ | ||
71 | /* whether you need write access to that file/dir, especially true | ||
72 | * for runtime generated files (config.cfg) */ | ||
73 | #define NEED_WRITE (1<<0) | ||
74 | /* file or directory? */ | ||
75 | #define IS_FILE (1<<1) | ||
76 | |||
77 | #ifdef HAVE_MULTIDRIVE | ||
78 | static uint32_t rbhome_hash; | ||
79 | /* A special link is created under e.g. HOME_DIR/<microSD1>, e.g. to access | ||
80 | * external storage in a convinient location, much similar to the mount | ||
81 | * point on our native targets. Here they are treated as symlink (one which | ||
82 | * doesn't actually exist in the filesystem and therefore we have to override | ||
83 | * readlink() */ | ||
84 | static const char *handle_special_links(const char* link, unsigned flags, | ||
85 | char *buf, const size_t bufsize) | ||
86 | { | ||
87 | (void) flags; | ||
88 | char vol_string[VOL_ENUM_POS + 8]; | ||
89 | int len = sprintf(vol_string, VOL_NAMES, 1); | ||
90 | |||
91 | /* link might be passed with or without HOME_DIR expanded. To handle | ||
92 | * both perform substring matching (VOL_NAMES is unique enough) */ | ||
93 | const char *begin = strstr(link, vol_string); | ||
94 | if (begin) | ||
95 | { | ||
96 | /* begin now points to the start of vol_string within link, | ||
97 | * we want to copy the remainder of the paths, prefixed by | ||
98 | * the actual mount point (the remainder might be "") */ | ||
99 | snprintf(buf, bufsize, MULTIDRIVE_DIR"%s", begin + len); | ||
100 | return buf; | ||
101 | } | ||
102 | |||
103 | return link; | ||
104 | } | ||
105 | #endif | ||
106 | |||
107 | void paths_init(void) | ||
108 | { | ||
109 | #ifdef HAVE_SPECIAL_DIRS | ||
110 | /* make sure $HOME/.config/rockbox.org exists, it's needed for config.cfg */ | ||
111 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
112 | mkdir("/sdcard/rockbox", 0777); | ||
113 | mkdir("/sdcard/rockbox/rocks.data", 0777); | ||
114 | #else | ||
115 | char config_dir[MAX_PATH]; | ||
116 | |||
117 | const char *home = getenv("RBROOT"); | ||
118 | if (!home) | ||
119 | { | ||
120 | home = getenv("HOME"); | ||
121 | } | ||
122 | if (!home) | ||
123 | { | ||
124 | logf("HOME environment var not set. Can't write config"); | ||
125 | return; | ||
126 | } | ||
127 | |||
128 | rbhome = home; | ||
129 | snprintf(config_dir, sizeof(config_dir), "%s/.config", home); | ||
130 | mkdir(config_dir, 0777); | ||
131 | snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org", home); | ||
132 | mkdir(config_dir, 0777); | ||
133 | /* Plugin data directory */ | ||
134 | snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org/rocks.data", home); | ||
135 | mkdir(config_dir, 0777); | ||
136 | #endif | ||
137 | #endif /* HAVE_SPECIAL_DIRS */ | ||
138 | |||
139 | #ifdef HAVE_MULTIDRIVE | ||
140 | rbhome_hash = crc_32((const void *) rbhome, strlen(rbhome), 0xffffffff); | ||
141 | #endif /* HAVE_MULTIDRIVE */ | ||
142 | } | ||
143 | |||
144 | #ifdef HAVE_SPECIAL_DIRS | ||
145 | static bool try_path(const char* filename, unsigned flags) | ||
146 | { | ||
147 | if (flags & IS_FILE) | ||
148 | { | ||
149 | if (file_exists(filename)) | ||
150 | return true; | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | if (dir_exists(filename)) | ||
155 | return true; | ||
156 | } | ||
157 | return false; | ||
158 | } | ||
159 | |||
160 | static const char* _get_user_file_path(const char *path, | ||
161 | unsigned flags, | ||
162 | char* buf, | ||
163 | const size_t bufsize) | ||
164 | { | ||
165 | const char *ret = path; | ||
166 | const char *pos = path; | ||
167 | /* replace ROCKBOX_DIR in path with $HOME/.config/rockbox.org */ | ||
168 | pos += ROCKBOX_DIR_LEN; | ||
169 | if (*pos == '/') pos += 1; | ||
170 | |||
171 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
172 | if (snprintf(buf, bufsize, "/sdcard/rockbox/%s", pos) | ||
173 | #else | ||
174 | if (snprintf(buf, bufsize, "%s/.config/rockbox.org/%s", rbhome, pos) | ||
175 | #endif | ||
176 | >= (int)bufsize) | ||
177 | return NULL; | ||
178 | |||
179 | /* always return the replacement buffer (pointing to $HOME) if | ||
180 | * write access is needed */ | ||
181 | if (flags & NEED_WRITE) | ||
182 | ret = buf; | ||
183 | else if (try_path(buf, flags)) | ||
184 | ret = buf; | ||
185 | |||
186 | if (ret != buf) /* not found in $HOME, try ROCKBOX_BASE_DIR, !NEED_WRITE only */ | ||
187 | { | ||
188 | if (snprintf(buf, bufsize, ROCKBOX_SHARE_PATH "/%s", pos) >= (int)bufsize) | ||
189 | return NULL; | ||
190 | |||
191 | if (try_path(buf, flags)) | ||
192 | ret = buf; | ||
193 | } | ||
194 | |||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | #endif | ||
199 | |||
200 | static const char* handle_special_dirs(const char* dir, unsigned flags, | ||
201 | char *buf, const size_t bufsize) | ||
202 | { | ||
203 | (void) flags; (void) buf; (void) bufsize; | ||
204 | #ifdef HAVE_SPECIAL_DIRS | ||
205 | if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN)) | ||
206 | { | ||
207 | const char *p = dir + HOME_DIR_LEN; | ||
208 | while (*p == '/') p++; | ||
209 | snprintf(buf, bufsize, "%s/%s", rbhome, p); | ||
210 | dir = buf; | ||
211 | } | ||
212 | else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN)) | ||
213 | dir = _get_user_file_path(dir, flags, buf, bufsize); | ||
214 | #endif | ||
215 | #ifdef HAVE_MULTIDRIVE | ||
216 | dir = handle_special_links(dir, flags, buf, bufsize); | ||
217 | #endif | ||
218 | return dir; | ||
219 | } | ||
220 | |||
221 | int app_open(const char *name, int o, ...) | ||
222 | { | ||
223 | char realpath[MAX_PATH]; | ||
224 | va_list ap; | ||
225 | int fd; | ||
226 | int flags = IS_FILE; | ||
227 | if (o & (O_CREAT|O_RDWR|O_TRUNC|O_WRONLY)) | ||
228 | flags |= NEED_WRITE; | ||
229 | |||
230 | name = handle_special_dirs(name, flags, realpath, sizeof(realpath)); | ||
231 | |||
232 | va_start(ap, o); | ||
233 | fd = open(name, o, va_arg(ap, unsigned int)); | ||
234 | va_end(ap); | ||
235 | |||
236 | return fd; | ||
237 | } | ||
238 | |||
239 | int app_creat(const char* name, mode_t mode) | ||
240 | { | ||
241 | return app_open(name, O_CREAT|O_WRONLY|O_TRUNC, mode); | ||
242 | } | ||
243 | |||
244 | int app_remove(const char *name) | ||
245 | { | ||
246 | char realpath[MAX_PATH]; | ||
247 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | ||
248 | |||
249 | return remove(fname); | ||
250 | } | ||
251 | |||
252 | int app_rename(const char *old, const char *new) | ||
253 | { | ||
254 | char realpath_old[MAX_PATH], realpath_new[MAX_PATH]; | ||
255 | const char *final_old, *final_new; | ||
256 | |||
257 | final_old = handle_special_dirs(old, NEED_WRITE, realpath_old, sizeof(realpath_old)); | ||
258 | final_new = handle_special_dirs(new, NEED_WRITE, realpath_new, sizeof(realpath_new)); | ||
259 | |||
260 | return rename(final_old, final_new); | ||
261 | } | ||
262 | |||
263 | /* need to wrap around DIR* because we need to save the parent's | ||
264 | * directory path in order to determine dirinfo, required to implement | ||
265 | * get_dir_info() */ | ||
266 | struct __dir { | ||
267 | DIR *dir; | ||
268 | #ifdef HAVE_MULTIDRIVE | ||
269 | int volumes_returned; | ||
270 | /* A crc of rbhome is used to speed op the common case where | ||
271 | * readdir()/get_dir_info() is called on non-rbhome paths, because | ||
272 | * each call needs to check against rbhome for the virtual | ||
273 | * mount point of the external storage */ | ||
274 | uint32_t path_hash; | ||
275 | #endif | ||
276 | char path[]; | ||
277 | }; | ||
278 | |||
279 | struct dirinfo dir_get_info(DIR* _parent, struct dirent *dir) | ||
280 | { | ||
281 | struct __dir *parent = (struct __dir*)_parent; | ||
282 | struct stat s; | ||
283 | struct tm *tm = NULL; | ||
284 | struct dirinfo ret; | ||
285 | char path[MAX_PATH]; | ||
286 | |||
287 | memset(&ret, 0, sizeof(ret)); | ||
288 | |||
289 | #ifdef HAVE_MULTIDRIVE | ||
290 | char vol_string[VOL_ENUM_POS + 8]; | ||
291 | sprintf(vol_string, VOL_NAMES, 1); | ||
292 | if (UNLIKELY(rbhome_hash == parent->path_hash) && | ||
293 | /* compare path anyway because of possible hash collision */ | ||
294 | !strcmp(vol_string, dir->d_name)) | ||
295 | { | ||
296 | ret.attribute = ATTR_LINK; | ||
297 | strcpy(path, MULTIDRIVE_DIR); | ||
298 | } | ||
299 | else | ||
300 | #endif | ||
301 | snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); | ||
302 | |||
303 | |||
304 | if (!lstat(path, &s)) | ||
305 | { | ||
306 | int err = 0; | ||
307 | if (S_ISLNK(s.st_mode)) | ||
308 | { | ||
309 | ret.attribute |= ATTR_LINK; | ||
310 | err = stat(path, &s); | ||
311 | } | ||
312 | if (!err) | ||
313 | { | ||
314 | if (S_ISDIR(s.st_mode)) | ||
315 | ret.attribute |= ATTR_DIRECTORY; | ||
316 | |||
317 | ret.size = s.st_size; | ||
318 | if ((tm = localtime(&(s.st_mtime)))) | ||
319 | { | ||
320 | ret.wrtdate = ((tm->tm_year - 80) << 9) | | ||
321 | ((tm->tm_mon + 1) << 5) | | ||
322 | tm->tm_mday; | ||
323 | ret.wrttime = (tm->tm_hour << 11) | | ||
324 | (tm->tm_min << 5) | | ||
325 | (tm->tm_sec >> 1); | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | |||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | DIR* app_opendir(const char *_name) | ||
334 | { | ||
335 | size_t name_len; | ||
336 | char realpath[MAX_PATH]; | ||
337 | const char *name = handle_special_dirs(_name, 0, realpath, sizeof(realpath)); | ||
338 | name_len = strlen(name); | ||
339 | char *buf = malloc(sizeof(struct __dir) + name_len+1); | ||
340 | if (!buf) | ||
341 | return NULL; | ||
342 | |||
343 | struct __dir *this = (struct __dir*)buf; | ||
344 | /* carefully remove any trailing slash from the input, so that | ||
345 | * hash/path matching in readdir() works properly */ | ||
346 | while (name[name_len-1] == '/' && name_len > 1) | ||
347 | name_len -= 1; | ||
348 | /* strcpy cannot be used because of trailing slashes */ | ||
349 | memcpy(this->path, name, name_len); | ||
350 | this->path[name_len] = 0; | ||
351 | this->dir = opendir(this->path); | ||
352 | |||
353 | if (!this->dir) | ||
354 | { | ||
355 | free(buf); | ||
356 | return NULL; | ||
357 | } | ||
358 | #ifdef HAVE_MULTIDRIVE | ||
359 | this->volumes_returned = 0; | ||
360 | this->path_hash = crc_32((const void *)this->path, name_len, 0xffffffff); | ||
361 | #endif | ||
362 | |||
363 | return (DIR*)this; | ||
364 | } | ||
365 | |||
366 | int app_closedir(DIR *dir) | ||
367 | { | ||
368 | struct __dir *this = (struct __dir*)dir; | ||
369 | int ret = closedir(this->dir); | ||
370 | free(this); | ||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | |||
375 | struct dirent* app_readdir(DIR* dir) | ||
376 | { | ||
377 | struct __dir *d = (struct __dir*)dir; | ||
378 | #ifdef HAVE_MULTIDRIVE | ||
379 | /* this is not MT-safe but OK according to man readdir */ | ||
380 | static struct dirent voldir; | ||
381 | if (UNLIKELY(rbhome_hash == d->path_hash) | ||
382 | && d->volumes_returned < (NUM_VOLUMES-1) | ||
383 | && volume_present(d->volumes_returned+1) | ||
384 | /* compare path anyway because of possible hash collision */ | ||
385 | && !strcmp(d->path, rbhome)) | ||
386 | { | ||
387 | d->volumes_returned += 1; | ||
388 | sprintf(voldir.d_name, VOL_NAMES, d->volumes_returned); | ||
389 | return &voldir; | ||
390 | } | ||
391 | #endif | ||
392 | return readdir(d->dir); | ||
393 | } | ||
394 | |||
395 | |||
396 | int app_mkdir(const char* name) | ||
397 | { | ||
398 | char realpath[MAX_PATH]; | ||
399 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | ||
400 | return mkdir(fname, 0777); | ||
401 | } | ||
402 | |||
403 | |||
404 | int app_rmdir(const char* name) | ||
405 | { | ||
406 | char realpath[MAX_PATH]; | ||
407 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | ||
408 | return rmdir(fname); | ||
409 | } | ||
410 | |||
411 | |||
412 | /* On MD we create a virtual symlink for the external drive, | ||
413 | * for this we need to override readlink(). */ | ||
414 | ssize_t app_readlink(const char *path, char *buf, size_t bufsiz) | ||
415 | { | ||
416 | char _buf[MAX_PATH]; | ||
417 | (void) path; (void) buf; (void) bufsiz; | ||
418 | path = handle_special_dirs(path, 0, _buf, sizeof(_buf)); | ||
419 | #ifdef HAVE_MULTIDRIVE | ||
420 | /* if path == _buf then we can be sure handle_special_dir() did something | ||
421 | * and path is not an ordinary directory */ | ||
422 | if (path == _buf && !strncmp(path, MULTIDRIVE_DIR, sizeof(MULTIDRIVE_DIR)-1)) | ||
423 | { | ||
424 | /* copying NUL is not required as per readlink specification */ | ||
425 | ssize_t len = strlen(path); | ||
426 | memcpy(buf, path, len); | ||
427 | return len; | ||
428 | } | ||
429 | #endif | ||
430 | /* does not append NUL !! */ | ||
431 | return readlink(path, buf, bufsiz); | ||
432 | } | ||