summaryrefslogtreecommitdiff
path: root/firmware/common/rbpaths.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/common/rbpaths.c')
-rw-r--r--firmware/common/rbpaths.c432
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)
55static const char rbhome[] = "/sdcard";
56#elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) && !defined(__PCTOOL__)
57const char *rbhome;
58#else
59/* YPR0, YPR1 */
60static 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
78static 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() */
84static 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
107void 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
145static 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
160static 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
200static 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
221int 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
239int app_creat(const char* name, mode_t mode)
240{
241 return app_open(name, O_CREAT|O_WRONLY|O_TRUNC, mode);
242}
243
244int 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
252int 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() */
266struct __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
279struct 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
333DIR* 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
366int 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
375struct 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
396int 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
404int 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(). */
414ssize_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}