diff options
Diffstat (limited to 'firmware/common')
-rw-r--r-- | firmware/common/rbpaths.c | 117 |
1 files changed, 94 insertions, 23 deletions
diff --git a/firmware/common/rbpaths.c b/firmware/common/rbpaths.c index 8efb6dd238..dba39476fc 100644 --- a/firmware/common/rbpaths.c +++ b/firmware/common/rbpaths.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #undef rmdir | 48 | #undef rmdir |
49 | #undef dirent | 49 | #undef dirent |
50 | #undef DIR | 50 | #undef DIR |
51 | #undef readlink | ||
51 | 52 | ||
52 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | 53 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) |
53 | static const char rbhome[] = "/sdcard"; | 54 | static const char rbhome[] = "/sdcard"; |
@@ -61,6 +62,9 @@ const char *rbhome; | |||
61 | * over the ones where Rockbox is installed to. Classic example would be | 62 | * over the ones where Rockbox is installed to. Classic example would be |
62 | * $HOME/.config/rockbox.org vs /usr/share/rockbox */ | 63 | * $HOME/.config/rockbox.org vs /usr/share/rockbox */ |
63 | #define HAVE_SPECIAL_DIRS | 64 | #define HAVE_SPECIAL_DIRS |
65 | #define IS_HOME(p) (!strcmp(p, rbhome)) | ||
66 | #else | ||
67 | #define IS_HOME(p) (!strcmp(p, HOME_DIR)) | ||
64 | #endif | 68 | #endif |
65 | 69 | ||
66 | /* flags for get_user_file_path() */ | 70 | /* flags for get_user_file_path() */ |
@@ -70,6 +74,35 @@ const char *rbhome; | |||
70 | /* file or directory? */ | 74 | /* file or directory? */ |
71 | #define IS_FILE (1<<1) | 75 | #define IS_FILE (1<<1) |
72 | 76 | ||
77 | #ifdef HAVE_MULTIDRIVE | ||
78 | /* A special link is created under e.g. HOME_DIR/<microSD1>, e.g. to access | ||
79 | * external storage in a convinient location, much similar to the mount | ||
80 | * point on our native targets. Here they are treated as symlink (one which | ||
81 | * doesn't actually exist in the filesystem and therefore we have to override | ||
82 | * readlink() */ | ||
83 | static const char *handle_special_links(const char* link, unsigned flags, | ||
84 | char *buf, const size_t bufsize) | ||
85 | { | ||
86 | (void) flags; | ||
87 | char vol_string[VOL_ENUM_POS + 8]; | ||
88 | int len = sprintf(vol_string, VOL_NAMES, 1); | ||
89 | |||
90 | /* link might be passed with or without HOME_DIR expanded. To handle | ||
91 | * both perform substring matching (VOL_NAMES is unique enough) */ | ||
92 | const char *begin = strstr(link, vol_string); | ||
93 | if (begin) | ||
94 | { | ||
95 | /* begin now points to the start of vol_string within link, | ||
96 | * we want to copy the remainder of the paths, prefixed by | ||
97 | * the actual mount point (the remainder might be "") */ | ||
98 | snprintf(buf, bufsize, MULTIDRIVE_DIR"%s", begin + len); | ||
99 | return buf; | ||
100 | } | ||
101 | |||
102 | return link; | ||
103 | } | ||
104 | #endif | ||
105 | |||
73 | #ifdef HAVE_SPECIAL_DIRS | 106 | #ifdef HAVE_SPECIAL_DIRS |
74 | void paths_init(void) | 107 | void paths_init(void) |
75 | { | 108 | { |
@@ -156,38 +189,31 @@ static const char* _get_user_file_path(const char *path, | |||
156 | return ret; | 189 | return ret; |
157 | } | 190 | } |
158 | 191 | ||
192 | #elif !defined(paths_init) | ||
193 | void paths_init(void) { } | ||
194 | #endif | ||
159 | 195 | ||
160 | static const char* handle_special_dirs(const char* dir, unsigned flags, | 196 | static const char* handle_special_dirs(const char* dir, unsigned flags, |
161 | char *buf, const size_t bufsize) | 197 | char *buf, const size_t bufsize) |
162 | { | 198 | { |
199 | (void) flags; (void) buf; (void) bufsize; | ||
200 | #ifdef HAVE_SPECIAL_DIRS | ||
163 | if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN)) | 201 | if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN)) |
164 | { | 202 | { |
165 | const char *p = dir + HOME_DIR_LEN; | 203 | const char *p = dir + HOME_DIR_LEN; |
166 | while (*p == '/') p++; | 204 | while (*p == '/') p++; |
167 | snprintf(buf, bufsize, "%s/%s", rbhome, p); | 205 | snprintf(buf, bufsize, "%s/%s", rbhome, p); |
168 | return buf; | 206 | dir = buf; |
169 | } | 207 | } |
170 | else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN)) | 208 | else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN)) |
171 | return _get_user_file_path(dir, flags, buf, bufsize); | 209 | dir = _get_user_file_path(dir, flags, buf, bufsize); |
172 | 210 | #endif | |
173 | return dir; | 211 | #ifdef HAVE_MULTIDRIVE |
174 | } | 212 | dir = handle_special_links(dir, flags, buf, bufsize); |
175 | |||
176 | #else /* !HAVE_SPECIAL_DIRS */ | ||
177 | |||
178 | #ifndef paths_init | ||
179 | void paths_init(void) { } | ||
180 | #endif | 213 | #endif |
181 | |||
182 | static const char* handle_special_dirs(const char* dir, unsigned flags, | ||
183 | char *buf, const size_t bufsize) | ||
184 | { | ||
185 | (void) flags; (void) buf; (void) bufsize; | ||
186 | return dir; | 214 | return dir; |
187 | } | 215 | } |
188 | 216 | ||
189 | #endif | ||
190 | |||
191 | int app_open(const char *name, int o, ...) | 217 | int app_open(const char *name, int o, ...) |
192 | { | 218 | { |
193 | char realpath[MAX_PATH]; | 219 | char realpath[MAX_PATH]; |
@@ -235,6 +261,7 @@ int app_rename(const char *old, const char *new) | |||
235 | * get_dir_info() */ | 261 | * get_dir_info() */ |
236 | struct __dir { | 262 | struct __dir { |
237 | DIR *dir; | 263 | DIR *dir; |
264 | IF_MD(int volumes_returned); | ||
238 | char path[]; | 265 | char path[]; |
239 | }; | 266 | }; |
240 | 267 | ||
@@ -246,23 +273,31 @@ struct dirinfo dir_get_info(DIR* _parent, struct dirent *dir) | |||
246 | struct dirinfo ret; | 273 | struct dirinfo ret; |
247 | char path[MAX_PATH]; | 274 | char path[MAX_PATH]; |
248 | 275 | ||
249 | snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); | ||
250 | memset(&ret, 0, sizeof(ret)); | 276 | memset(&ret, 0, sizeof(ret)); |
251 | 277 | ||
278 | #ifdef HAVE_MULTIDRIVE | ||
279 | char vol_string[VOL_ENUM_POS + 8]; | ||
280 | sprintf(vol_string, VOL_NAMES, 1); | ||
281 | if (!strcmp(vol_string, dir->d_name)) | ||
282 | { | ||
283 | ret.attribute = ATTR_LINK; | ||
284 | strcpy(path, MULTIDRIVE_DIR); | ||
285 | } | ||
286 | else | ||
287 | #endif | ||
288 | snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); | ||
289 | |||
252 | if (!stat(path, &s)) | 290 | if (!stat(path, &s)) |
253 | { | 291 | { |
254 | if (S_ISDIR(s.st_mode)) | 292 | if (S_ISDIR(s.st_mode)) |
255 | { | 293 | ret.attribute |= ATTR_DIRECTORY; |
256 | ret.attribute = ATTR_DIRECTORY; | 294 | |
257 | } | ||
258 | ret.size = s.st_size; | 295 | ret.size = s.st_size; |
259 | tm = localtime(&(s.st_mtime)); | 296 | tm = localtime(&(s.st_mtime)); |
260 | } | 297 | } |
261 | 298 | ||
262 | if (!lstat(path, &s) && S_ISLNK(s.st_mode)) | 299 | if (!lstat(path, &s) && S_ISLNK(s.st_mode)) |
263 | { | ||
264 | ret.attribute |= ATTR_LINK; | 300 | ret.attribute |= ATTR_LINK; |
265 | } | ||
266 | 301 | ||
267 | if (tm) | 302 | if (tm) |
268 | { | 303 | { |
@@ -296,6 +331,7 @@ DIR* app_opendir(const char *_name) | |||
296 | free(buf); | 331 | free(buf); |
297 | return NULL; | 332 | return NULL; |
298 | } | 333 | } |
334 | IF_MD(this->volumes_returned = 0); | ||
299 | return (DIR*)this; | 335 | return (DIR*)this; |
300 | } | 336 | } |
301 | 337 | ||
@@ -311,6 +347,18 @@ int app_closedir(DIR *dir) | |||
311 | struct dirent* app_readdir(DIR* dir) | 347 | struct dirent* app_readdir(DIR* dir) |
312 | { | 348 | { |
313 | struct __dir *d = (struct __dir*)dir; | 349 | struct __dir *d = (struct __dir*)dir; |
350 | #ifdef HAVE_MULTIDRIVE | ||
351 | /* this is not MT-safe but OK according to man readdir */ | ||
352 | static struct dirent voldir; | ||
353 | if (d->volumes_returned < (NUM_VOLUMES-1) | ||
354 | && volume_present(d->volumes_returned+1) | ||
355 | && IS_HOME(d->path)) | ||
356 | { | ||
357 | d->volumes_returned += 1; | ||
358 | sprintf(voldir.d_name, VOL_NAMES, d->volumes_returned); | ||
359 | return &voldir; | ||
360 | } | ||
361 | #endif | ||
314 | return readdir(d->dir); | 362 | return readdir(d->dir); |
315 | } | 363 | } |
316 | 364 | ||
@@ -329,3 +377,26 @@ int app_rmdir(const char* name) | |||
329 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | 377 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); |
330 | return rmdir(fname); | 378 | return rmdir(fname); |
331 | } | 379 | } |
380 | |||
381 | |||
382 | /* On MD we create a virtual symlink for the external drive, | ||
383 | * for this we need to override readlink(). */ | ||
384 | ssize_t app_readlink(const char *path, char *buf, size_t bufsiz) | ||
385 | { | ||
386 | char _buf[MAX_PATH]; | ||
387 | (void) path; (void) buf; (void) bufsiz; | ||
388 | path = handle_special_dirs(path, 0, _buf, sizeof(_buf)); | ||
389 | #ifdef HAVE_MULTIDRIVE | ||
390 | /* if path == _buf then we can be sure handle_special_dir() did something | ||
391 | * and path is not an ordinary directory */ | ||
392 | if (path == _buf && !strncmp(path, MULTIDRIVE_DIR, sizeof(MULTIDRIVE_DIR)-1)) | ||
393 | { | ||
394 | /* copying NUL is not required as per readlink specification */ | ||
395 | ssize_t len = strlen(path); | ||
396 | memcpy(buf, path, len); | ||
397 | return len; | ||
398 | } | ||
399 | #endif | ||
400 | /* does not append NUL !! */ | ||
401 | return readlink(path, buf, bufsiz); | ||
402 | } | ||