Rewrite filesystem code (WIP)

This patch redoes the filesystem code from the FAT driver up to the
clipboard code in onplay.c.

Not every aspect of this is finished therefore it is still "WIP". I
don't wish to do too much at once (haha!). What is left to do is get
dircache back in the sim and find an implementation for the dircache
indicies in the tagcache and playlist code or do something else that
has the same benefit. Leaving these out for now does not make anything
unusable. All the basics are done.

Phone app code should probably get vetted (and app path handling
just plain rewritten as environment expansions); the SDL app and
Android run well.

Main things addressed:
1) Thread safety: There is none right now in the trunk code. Most of
what currently works is luck when multiple threads are involved or
multiple descriptors to the same file are open.

2) POSIX compliance: Many of the functions behave nothing like their
counterparts on a host system. This leads to inconsistent code or very
different behavior from native to hosted. One huge offender was
rename(). Going point by point would fill a book.

3) Actual running RAM usage: Many targets will use less RAM and less
stack space (some more RAM because I upped the number of cache buffers
for large memory). There's very little memory lying fallow in rarely-used
areas (see 'Key core changes' below). Also, all targets may open the same
number of directory streams whereas before those with less than 8MB RAM
were limited to 8, not 12 implying those targets will save slightly
less.

4) Performance: The test_disk plugin shows markedly improved performance,
particularly in the area of (uncached) directory scanning, due partly to
more optimal directory reading and to a better sector cache algorithm.
Uncached times tend to be better while there is a bit of a slowdown in
dircache due to it being a bit heavier of an implementation. It's not
noticeable by a human as far as I can say.

Key core changes:
1) Files and directories share core code and data structures.

2) The filesystem code knows which descriptors refer to same file.
This ensures that changes from one stream are appropriately reflected
in every open descriptor for that file (fileobj_mgr.c).

3) File and directory cache buffers are borrowed from the main sector
cache. This means that when they are not in use by a file, they are not
wasted, but used for the cache. Most of the time, only a few of them
are needed. It also means that adding more file and directory handles
is less expensive. All one must do in ensure a large enough cache to
borrow from.

4) Relative path components are supported and the namespace is unified.
It does not support full relative paths to an implied current directory;
what is does support is use of "." and "..". Adding the former would
not be very difficult. The namespace is unified in the sense that
volumes may be specified several times along with relative parts, e.g.:
"/<0>/foo/../../<1>/bar" :<=> "/<1>/bar".

5) Stack usage is down due to sharing of data, static allocation and
less duplication of strings on the stack. This requires more
serialization than I would like but since the number of threads is
limited to a low number, the tradoff in favor of the stack seems
reasonable.

6) Separates and heirarchicalizes (sic) the SIM and APP filesystem
code. SIM path and volume handling is just like the target. Some
aspects of the APP file code get more straightforward (e.g. no path
hashing is needed).

Dircache:
Deserves its own section. Dircache is new but pays homage to the old.
The old one was not compatible and so it, since it got redone, does
all the stuff it always should have done such as:

1) It may be update and used at any time during the build process.
No longer has one to wait for it to finish building to do basic file
management (create, remove, rename, etc.).

2) It does not need to be either fully scanned or completely disabled;
it can be incomplete (i.e. overfilled, missing paths), still be
of benefit and be correct.

3) Handles mounting and dismounting of individual volumes which means
a full rebuild is not needed just because you pop a new SD card in the
slot. Now, because it reuses its freed entry data, may rebuild only
that volume.

4) Much more fundamental to the file code. When it is built, it is
the keeper of the master file list whether enabled or not ("disabled"
is just a state of the cache). Its must always to ready to be started
and bind all streams opened prior to being enabled.

5) Maintains any short filenames in OEM format which means that it does
not need to be rebuilt when changing the default codepage.

Miscellaneous Compatibility:
1) Update any other code that would otherwise not work such as the
hotswap mounting code in various card drivers.

2) File management: Clipboard needed updating because of the behavioral
changes. Still needs a little more work on some finer points.

3) Remove now-obsolete functionality such as the mutex's "no preempt"
flag (which was only for the prior FAT driver).

4) struct dirinfo uses time_t rather than raw FAT directory entry
time fields. I plan to follow up on genericizing everything there
(i.e. no FAT attributes).

5) unicode.c needed some redoing so that the file code does not try
try to load codepages during a scan, which is actually a problem with
the current code. The default codepage, if any is required, is now
kept in RAM separarately (bufalloced) from codepages specified to
iso_decode() (which must not be bufalloced because the conversion
may be done by playback threads).

Brings with it some additional reusable core code:
1) Revised file functions: Reusable code that does things such as
safe path concatenation and parsing without buffer limitations or
data duplication. Variants that copy or alter the input path may be
based off these.

To do:
1) Put dircache functionality back in the sim. Treating it internally
as a different kind of file system seems the best approach at this
time.

2) Restore use of dircache indexes in the playlist and database or
something effectively the same. Since the cache doesn't have to be
complete in order to be used, not getting a hit on the cache doesn't
unambiguously say if the path exists or not.

Change-Id: Ia30f3082a136253e3a0eae0784e3091d138915c8
Reviewed-on: http://gerrit.rockbox.org/566
Reviewed-by: Michael Sevakis <jethead71@rockbox.org>
Tested: Michael Sevakis <jethead71@rockbox.org>
This commit is contained in:
Michael Sevakis 2013-08-05 22:02:45 -04:00
parent 95a4c3afcd
commit 7d1a47cf13
130 changed files with 15218 additions and 7845 deletions

View File

@ -41,7 +41,7 @@
#include "list.h"
#include "plugin.h"
#include "file.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#define MAX_BOOKMARKS 10
#define MAX_BOOKMARK_SIZE 350
@ -1090,10 +1090,10 @@ static bool generate_bookmark_file_name(const char *in)
{
#ifdef HAVE_MULTIVOLUME
/* The "root" of an extra volume need special handling too. */
bool volume_root = (strip_volume(in, global_bookmark_file_name) &&
!strcmp("/", global_bookmark_file_name));
const char *filename;
path_strip_volume(in, &filename, true);
bool volume_root = *filename == '\0';
#endif
strcpy(global_bookmark_file_name, in);
if(global_bookmark_file_name[len-1] == '/')
len--;

View File

@ -128,7 +128,7 @@ struct codec_api ci = {
logf,
#endif
(qsort_func)qsort,
(void *)qsort,
#ifdef RB_PROFILE
profile_thread,

View File

@ -528,14 +528,19 @@ static const char* dbg_partitions_getname(int selected_item, void *data,
{
(void)data;
int partition = selected_item/2;
struct partinfo* p = disk_partinfo(partition);
struct partinfo p;
if (!disk_partinfo(partition, &p))
return buffer;
if (selected_item%2)
{
snprintf(buffer, buffer_len, " T:%x %ld MB", p->type, p->size / ( 2048 / ( SECTOR_SIZE / 512 )));
snprintf(buffer, buffer_len, " T:%x %ld MB", p.type,
p.size / ( 2048 / ( SECTOR_SIZE / 512 )));
}
else
{
snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p->start);
snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p.start);
}
return buffer;
}
@ -1377,7 +1382,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
simplelist_addline(
"Size: %s", buf);
unsigned long free;
fat_size( IF_MV(0,) NULL, &free );
volume_size( IF_MV(0,) NULL, &free );
simplelist_addline(
"Free: %ld MB", free / 1024);
simplelist_addline(
@ -1469,7 +1474,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
"No timing info");
}
simplelist_addline(
"Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0)));
"Cluster size: %d bytes", volume_get_cluster_size(IF_MV(0)));
#ifdef HAVE_ATA_DMA
i = ata_get_dma_mode();
if (i == 0) {
@ -1496,11 +1501,11 @@ static int disk_callback(int btn, struct gui_synclist *lists)
simplelist_addline(
"Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024);
unsigned long free;
fat_size( IF_MV(0,) NULL, &free );
volume_size( IF_MV(0,) NULL, &free );
simplelist_addline(
"Free: %ld MB", free / 1024);
simplelist_addline(
"Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0)));
"Cluster size: %d bytes", volume_get_cluster_size(IF_MV(0)));
return btn;
}
#endif
@ -1542,29 +1547,64 @@ static bool dbg_disk_info(void)
#ifdef HAVE_DIRCACHE
static int dircache_callback(int btn, struct gui_synclist *lists)
{
(void)lists;
struct dircache_info info;
dircache_get_info(&info);
if (global_settings.dircache)
{
switch (btn)
{
case ACTION_STD_CONTEXT:
splash(HZ/2, "Rebuilding cache");
dircache_suspend();
*(int *)lists->data = dircache_resume();
case ACTION_UNKNOWN:
btn = ACTION_NONE;
break;
#ifdef DIRCACHE_DUMPSTER
case ACTION_STD_OK:
splash(0, "Dumping cache");
dircache_dump();
btn = ACTION_NONE;
break;
#endif /* DIRCACHE_DUMPSTER */
case ACTION_STD_CANCEL:
if (*(int *)lists->data > 0 && info.status == DIRCACHE_SCANNING)
{
splash(HZ, str(LANG_SCANNING_DISK));
btn = ACTION_NONE;
}
break;
}
}
simplelist_set_line_count(0);
simplelist_addline("Cache initialized: %s",
dircache_is_enabled() ? "Yes" : "No");
simplelist_addline("Cache size: %d B",
dircache_get_cache_size());
simplelist_addline("Last size: %d B",
global_status.dircache_size);
simplelist_addline("Limit: %d B",
DIRCACHE_LIMIT);
simplelist_addline("Reserve: %d/%d B",
dircache_get_reserve_used(), DIRCACHE_RESERVE);
simplelist_addline("Scanning took: %d s",
dircache_get_build_ticks() / HZ);
simplelist_addline("Entry count: %d",
dircache_get_entry_count());
simplelist_addline("Cache status: %s", info.statusdesc);
simplelist_addline("Last size: %lu B", info.last_size);
simplelist_addline("Size: %lu B", info.size);
unsigned int utilized = info.size ? 1000ull*info.sizeused / info.size : 0;
simplelist_addline("Used: %lu B (%u.%u%%)", info.sizeused,
utilized / 10, utilized % 10);
simplelist_addline("Limit: %lu B", info.size_limit);
simplelist_addline("Reserve: %lu/%lu B", info.reserve_used, info.reserve);
long ticks = ALIGN_UP(info.build_ticks, HZ / 10);
simplelist_addline("Scanning took: %ld.%ld s",
ticks / HZ, (ticks*10 / HZ) % 10);
simplelist_addline("Entry count: %u", info.entry_count);
if (btn == ACTION_NONE)
btn = ACTION_REDRAW;
return btn;
(void)lists;
}
static bool dbg_dircache_info(void)
{
struct simplelist_info info;
simplelist_info_init(&info, "Dircache Info", 7, NULL);
int syncbuild = 0;
simplelist_info_init(&info, "Dircache Info", 8, &syncbuild);
info.action_callback = dircache_callback;
info.hide_selection = true;
info.scroll_all = true;

View File

@ -376,9 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir)
++files_in_dir;
dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used;
dptr->time_write =
(long)info.wrtdate<<16 |
(long)info.wrttime; /* in one # */
dptr->time_write = info.mtime;
strcpy(dptr->name, (char *)entry->d_name);
name_buffer_used += len + 1;

View File

@ -19,11 +19,12 @@
*
****************************************************************************/
#include "config.h"
#include "system.h"
#include "gcc_extensions.h"
#include "storage.h"
#include "disk.h"
#include "fat.h"
#include "file_internal.h"
#include "lcd.h"
#include "rtc.h"
#include "debug.h"
@ -34,7 +35,6 @@
#include "filetypes.h"
#include "panic.h"
#include "menu.h"
#include "system.h"
#include "usb.h"
#include "powermgmt.h"
#include "adc.h"
@ -203,80 +203,53 @@ int main(void)
root_menu();
}
static int init_dircache(bool preinit) INIT_ATTR;
static int init_dircache(bool preinit)
{
#ifdef HAVE_DIRCACHE
int result = 0;
bool clear = false;
static int INIT_ATTR init_dircache(bool preinit)
{
if (preinit)
dircache_init();
dircache_init(MAX(global_status.dircache_size, 0));
if (!global_settings.dircache)
return 0;
return -1;
# ifdef HAVE_EEPROM_SETTINGS
if (firmware_settings.initialized && firmware_settings.disk_clean
&& preinit)
int result = -1;
#ifdef HAVE_EEPROM_SETTINGS
if (firmware_settings.initialized &&
firmware_settings.disk_clean &&
preinit)
{
result = dircache_load();
if (result < 0)
{
firmware_settings.disk_clean = false;
if (global_status.dircache_size <= 0)
{
/* This will be in default language, settings are not
applied yet. Not really any easy way to fix that. */
splash(0, str(LANG_SCANNING_DISK));
clear = true;
}
dircache_build(global_status.dircache_size);
}
}
else
# endif
#endif /* HAVE_EEPROM_SETTINGS */
if (!preinit)
{
if (preinit)
return -1;
if (!dircache_is_enabled()
&& !dircache_is_initializing())
result = dircache_enable();
if (result != 0)
{
if (global_status.dircache_size <= 0)
if (result > 0)
{
/* Print "Scanning disk..." to the display. */
splash(0, str(LANG_SCANNING_DISK));
clear = true;
dircache_wait();
backlight_on();
show_logo();
}
result = dircache_build(global_status.dircache_size);
}
if (result < 0)
{
/* Initialization of dircache failed. Manual action is
* necessary to enable dircache again.
*/
splashf(0, "Dircache failed, disabled. Result: %d", result);
global_settings.dircache = false;
struct dircache_info info;
dircache_get_info(&info);
global_status.dircache_size = info.size;
status_save();
}
}
if (clear)
{
backlight_on();
show_logo();
global_status.dircache_size = dircache_get_cache_size();
status_save();
/* else don't wait or already enabled by load */
}
return result;
#else
(void)preinit;
return 0;
#endif
}
#endif /* HAVE_DIRCACHE */
#ifdef HAVE_TAGCACHE
static void init_tagcache(void) INIT_ATTR;
@ -363,6 +336,7 @@ static void init(void)
button_init();
powermgmt_init();
backlight_init();
unicode_init();
#ifdef SIMULATOR
sim_tasks_init();
#endif
@ -392,8 +366,10 @@ static void init(void)
settings_reset();
settings_load(SETTINGS_ALL);
settings_apply(true);
#ifdef HAVE_DIRCACHE
init_dircache(true);
init_dircache(false);
#endif
#ifdef HAVE_TAGCACHE
init_tagcache();
#endif
@ -429,6 +405,8 @@ static void init(void)
#else
#include "errno.h"
static void init(void) INIT_ATTR;
static void init(void)
{
@ -443,6 +421,9 @@ static void init(void)
core_allocator_init();
kernel_init();
/* early early early! */
filesystem_init();
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
set_cpu_frequency(CPUFREQ_NORMAL);
#ifdef CPU_COLDFIRE
@ -462,6 +443,7 @@ static void init(void)
/* current_tick should be ticking by now */
CHART("ticking");
unicode_init();
lcd_init();
#ifdef HAVE_REMOTE_LCD
lcd_remote_init();
@ -558,8 +540,6 @@ static void init(void)
}
#endif
disk_init_subsystem();
CHART(">storage_init");
rc = storage_init();
CHART("<storage_init");
@ -661,22 +641,24 @@ static void init(void)
CHART("<settings_load(ALL)");
}
#ifdef HAVE_DIRCACHE
CHART(">init_dircache(true)");
rc = init_dircache(true);
CHART("<init_dircache(true)");
if (rc < 0)
{
#ifdef HAVE_TAGCACHE
if (rc < 0)
remove(TAGCACHE_STATEFILE);
#endif
}
#endif /* HAVE_TAGCACHE */
#endif /* HAVE_DIRCACHE */
CHART(">settings_apply(true)");
settings_apply(true);
CHART("<settings_apply(true)");
#ifdef HAVE_DIRCACHE
CHART(">init_dircache(false)");
init_dircache(false);
CHART("<init_dircache(false)");
#endif
#ifdef HAVE_TAGCACHE
CHART(">init_tagcache");
init_tagcache();

View File

@ -43,6 +43,7 @@
#endif
#include "viewport.h"
#include "statusbar.h" /* statusbar_vals enum*/
#include "rbunicode.h"
#ifdef HAVE_BACKLIGHT
static int filterfirstkeypress_callback(int action,const struct menu_item_ex *this_item)
@ -524,8 +525,25 @@ MAKE_MENU(touchscreen_menu, ID2P(LANG_TOUCHSCREEN_SETTINGS), NULL, Icon_NOICON,
&touchscreen_menu_calibrate, &touchscreen_menu_reset_calibration);
#endif
static int codepage_callback(int action, const struct menu_item_ex *this_item)
{
static int old_codepage;
int new_codepage = global_settings.default_codepage;
(void)this_item;
switch (action)
{
case ACTION_ENTER_MENUITEM:
old_codepage = new_codepage;
break;
case ACTION_EXIT_MENUITEM:
if (new_codepage != old_codepage)
set_codepage(new_codepage);
break;
}
return action;
}
MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, NULL);
MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, codepage_callback);
MAKE_MENU(display_menu, ID2P(LANG_DISPLAY),

View File

@ -48,6 +48,7 @@
#include "time.h"
#include "wps.h"
#include "skin_buffer.h"
#include "disk.h"
static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG};
@ -160,14 +161,14 @@ static const char* info_getname(int selected_item, void *data,
#endif
if (info->new_data)
{
fat_size(IF_MV(0,) &info->size, &info->free);
volume_size(IF_MV(0,) &info->size, &info->free);
#ifdef HAVE_MULTIVOLUME
#ifndef APPLICATION
if (fat_ismounted(1))
fat_size(1, &info->size2, &info->free2);
else
volume_size(1, &info->size2, &info->free2);
#else
info->size2 = 0;
#endif
info->size2 = 0;
#endif
info->new_data = false;
}
@ -347,12 +348,7 @@ static int info_action_callback(int action, struct gui_synclist *lists)
info->new_data = true;
splash(0, ID2P(LANG_SCANNING_DISK));
for (i = 0; i < NUM_VOLUMES; i++)
{
#ifdef HAVE_HOTSWAP
if (fat_ismounted(i))
#endif
fat_recalc_free(IF_MV(i));
}
volume_recalc_free(IF_MV(i));
#else
(void) lists;
#endif

View File

@ -209,12 +209,12 @@ static int dircache_callback(int action,const struct menu_item_ex *this_item)
switch (action)
{
case ACTION_EXIT_MENUITEM: /* on exit */
if (global_settings.dircache && !dircache_is_enabled())
if (global_settings.dircache)
{
if (dircache_build(0) < 0)
if (dircache_enable() < 0)
splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
}
else if (!global_settings.dircache && dircache_is_enabled())
else
{
dircache_disable();
}

View File

@ -28,9 +28,12 @@
#include "misc.h"
#include "system.h"
#include "lcd.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
#include "file.h"
#ifndef __PCTOOL__
#include "filefuncs.h"
#include "pathfuncs.h"
#include "lang.h"
#include "dir.h"
#ifdef HAVE_REMOTE_LCD
@ -744,8 +747,7 @@ int show_logo( void )
*/
void check_bootfile(bool do_rolo)
{
static unsigned short wrtdate = 0;
static unsigned short wrttime = 0;
static time_t mtime = 0;
DIR* dir = NULL;
struct dirent* entry = NULL;
@ -761,10 +763,9 @@ void check_bootfile(bool do_rolo)
{
struct dirinfo info = dir_get_info(dir, entry);
/* found the bootfile */
if(wrtdate && do_rolo)
if(mtime && do_rolo)
{
if((info.wrtdate != wrtdate) ||
(info.wrttime != wrttime))
if(info.mtime != mtime)
{
static const char *lines[] = { ID2P(LANG_BOOT_CHANGED),
ID2P(LANG_REBOOT_NOW) };
@ -777,8 +778,7 @@ void check_bootfile(bool do_rolo)
}
}
}
wrtdate = info.wrtdate;
wrttime = info.wrttime;
mtime = info.mtime;
}
}
closedir(dir);

File diff suppressed because it is too large Load Diff

View File

@ -86,7 +86,7 @@
#include "screens.h"
#include "core_alloc.h"
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "button.h"
#include "filetree.h"
#include "abrepeat.h"
@ -107,6 +107,8 @@
#include "panic.h"
#include "logdiskf.h"
#undef HAVE_DIRCACHE
#define PLAYLIST_CONTROL_FILE_VERSION 2
/*
@ -180,8 +182,8 @@ static int get_next_directory(char *dir);
static int get_next_dir(char *dir, bool is_forward);
static int get_previous_directory(char *dir);
static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
static int format_track_path(char *dest, char *src, int buf_length, int max,
const char *dir);
static ssize_t format_track_path(char *dest, char *src, int buf_length,
const char *dir);
static void display_playlist_count(int count, const unsigned char *fmt,
bool final);
static void display_buffer_full(void);
@ -526,7 +528,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
int result = 0;
/* get emergency buffer so we don't fail horribly */
if (!buflen)
buffer = __builtin_alloca((buflen = 64));
buffer = alloca((buflen = 64));
if(-1 == playlist->fd)
playlist->fd = open_utf8(playlist->filename, O_RDONLY);
@ -1429,7 +1431,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek,
strlcpy(dir_buf, playlist->filename, playlist->dirlen);
return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
return format_track_path(buf, tmp_buf, buf_length, dir_buf);
}
static int get_next_directory(char *dir){
@ -1629,7 +1631,7 @@ static int get_next_dir(char *dir, bool is_forward)
static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
{
int result = -1;
int dirlen = strlen(dir);
size_t dirlen = strlen(dir);
int num_files = 0;
int i;
struct entry *files;
@ -1637,12 +1639,11 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
bool has_subdir = false;
struct tree_context* tc = tree_get_context();
snprintf(
dir + dirlen, MAX_PATH - dirlen,
/* only add a trailing slash if we need one */
dirlen && dir[dirlen - 1] == '/' ? "%s" : "/%s",
subdir
);
if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
MAX_PATH - dirlen)
{
return 0;
}
if (ft_load(tc, dir) < 0)
{
@ -1695,7 +1696,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
}
else
{
strcpy(dir, "/");
strcpy(dir, PATH_ROOTSTR);
}
/* we now need to reload our current directory */
@ -1708,79 +1709,31 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
/*
* Returns absolute path of track
*/
static int format_track_path(char *dest, char *src, int buf_length, int max,
const char *dir)
static ssize_t format_track_path(char *dest, char *src, int buf_length,
const char *dir)
{
int i = 0;
int j;
char *temp_ptr;
size_t len;
/* Look for the end of the string */
while((i < max) &&
(src[i] != '\n') &&
(src[i] != '\r') &&
(src[i] != '\0'))
i++;
/* Now work back killing white space */
while((i > 0) &&
((src[i-1] == ' ') ||
(src[i-1] == '\t')))
i--;
/* Zero-terminate the file name */
src[i]=0;
/* strip whitespace at beginning and end */
len = path_trim_whitespace(src, (const char **)&src);
src[len] = '\0';
/* replace backslashes with forward slashes */
for ( j=0; j<i; j++ )
if ( src[j] == '\\' )
src[j] = '/';
path_correct_separators(src, src);
if('/' == src[0])
{
strlcpy(dest, src, buf_length);
}
else
{
/* handle dos style drive letter */
if (':' == src[1])
strlcpy(dest, &src[2], buf_length);
else if (!strncmp(src, "../", 3))
{
/* handle relative paths */
i=3;
while(!strncmp(&src[i], "../", 3))
i += 3;
for (j=0; j<i/3; j++) {
temp_ptr = strrchr(dir, '/');
if (temp_ptr)
*temp_ptr = '\0';
else
break;
}
snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
}
else if ( '.' == src[0] && '/' == src[1] ) {
snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
}
else {
snprintf(dest, buf_length, "%s/%s", dir, src);
}
}
#ifdef HAVE_MULTIVOLUME
/* handle DOS style drive letter and parse non-greedily so that:
* 1) "c:/foo" becomes "/foo" and the result is absolute
* 2) "c:foo becomes "foo" and the result is relative
* This is how Windows seems to handle it except drive letters are of no
* meaning here. */
path_strip_drive(src, (const char **)&src, false);
char vol_string[VOL_ENUM_POS + 8];
snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, 1);
/* prepends directory only if src is relative */
len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
if (len >= (size_t)buf_length)
return -1; /* buffer too small */
/*check if the playlist is on a external card, and correct path if needed */
if(strstr(dir, vol_string) && (strstr(dest, vol_string) == NULL)){
char temp[buf_length];
strlcpy(temp, dest, buf_length);
snprintf(dest, buf_length, "%s%s", vol_string, temp);
}
#endif
return 0;
return len;
}
/*
@ -3113,8 +3066,7 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
{
int fd;
int max;
char *temp_ptr;
const char *dir;
char *dir;
unsigned char *count_str;
char temp_buf[MAX_PATH+1];
char trackname[MAX_PATH+1];
@ -3139,13 +3091,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
}
/* we need the directory name for formatting purposes */
dir = filename;
temp_ptr = strrchr(filename+1,'/');
if (temp_ptr)
*temp_ptr = 0;
else
dir = "/";
size_t dirlen = path_dirname(filename, (const char **)&dir);
dir = strmemdupa(dir, dirlen);
if (queue)
count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
@ -3183,8 +3130,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
/* we need to format so that relative paths are correctly
handled */
if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
dir) < 0)
if (format_track_path(trackname, temp_buf, sizeof(trackname),
dir) < 0)
{
result = -1;
break;
@ -3223,9 +3170,6 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
close(fd);
if (temp_ptr)
*temp_ptr = '/';
sync_control(playlist, false);
cpu_boost(false);
@ -3537,9 +3481,9 @@ int playlist_save(struct playlist_info* playlist, char *filename,
char path[MAX_PATH+1];
char tmp_buf[MAX_PATH+1];
int result = 0;
bool overwrite_current = false;
int *seek_buf;
bool reparse;
ssize_t pathlen;
ALIGN_BUFFER(temp_buffer, temp_buffer_size, sizeof(int));
seek_buf = temp_buffer;
@ -3557,22 +3501,18 @@ int playlist_save(struct playlist_info* playlist, char *filename,
return -1;
/* use current working directory as base for pathname */
if (format_track_path(path, filename, sizeof(tmp_buf),
strlen(filename)+1, "/") < 0)
pathlen = format_track_path(path, filename, sizeof(path), PATH_ROOTSTR);
if (pathlen < 0)
return -1;
/* Use temporary pathname and overwrite/rename later */
if (strlcat(path, "_temp", sizeof(path)) >= sizeof (path))
return -1;
/* can ignore volatile here, because core_get_data() is called later */
char* old_buffer = (char*)playlist->buffer;
size_t old_buffer_size = playlist->buffer_size;
if (!strncmp(playlist->filename, path, strlen(path)))
{
/* Attempting to overwrite current playlist file.
* use temporary pathname and overwrite later */
strlcat(path, "_temp", sizeof(path));
overwrite_current = true;
}
if (is_m3u8(path))
{
fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
@ -3621,8 +3561,8 @@ int playlist_save(struct playlist_info* playlist, char *filename,
break;
}
if (overwrite_current && !reparse)
seek_buf[count] = lseek(fd, 0, SEEK_CUR);
if (!reparse)
seek_buf[count] = filesize(fd);
if (fdprintf(fd, "%s\n", tmp_buf) < 0)
{
@ -3647,57 +3587,61 @@ int playlist_save(struct playlist_info* playlist, char *filename,
index = (index+1)%playlist->amount;
}
close(fd);
fd = -1;
display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
close(fd);
if (overwrite_current && result >= 0)
if (result >= 0)
{
result = -1;
strmemcpy(tmp_buf, path, pathlen); /* remove "_temp" */
mutex_lock(playlist->control_mutex);
/* Replace the current playlist with the new one and update indices */
close(playlist->fd);
playlist->fd = -1;
if (remove(playlist->filename) >= 0)
if (!rename(path, tmp_buf))
{
if (rename(path, playlist->filename) >= 0)
fd = open_utf8(tmp_buf, O_RDONLY);
if (fsamefile(fd, playlist->fd) > 0)
{
playlist->fd = open_utf8(playlist->filename, O_RDONLY);
if (playlist->fd >= 0)
/* Replace the current playlist with the new one and update
indices */
close(playlist->fd);
playlist->fd = fd;
fd = -1;
if (!reparse)
{
if (!reparse)
index = playlist->first_index;
for (i=0, count=0; i<playlist->amount; i++)
{
index = playlist->first_index;
for (i=0, count=0; i<playlist->amount; i++)
if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
{
if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
{
playlist->indices[index] = seek_buf[count];
count++;
}
index = (index+1)%playlist->amount;
playlist->indices[index] = seek_buf[count];
count++;
}
index = (index+1)%playlist->amount;
}
else
{
NOTEF("reparsing current playlist (slow)");
playlist->amount = 0;
add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
}
/* we need to recreate control because inserted tracks are
now part of the playlist and shuffle has been
invalidated */
result = recreate_control(playlist);
}
else
{
NOTEF("reparsing current playlist (slow)");
playlist->amount = 0;
add_indices_to_playlist(playlist, temp_buffer,
temp_buffer_size);
}
/* we need to recreate control because inserted tracks are
now part of the playlist and shuffle has been invalidated */
result = recreate_control(playlist);
}
}
mutex_unlock(playlist->control_mutex);
}
mutex_unlock(playlist->control_mutex);
}
if (fd >= 0)
close(fd);
cpu_boost(false);
reset_old_buffer:
@ -3759,8 +3703,12 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
if (recurse)
{
/* recursively add directories */
snprintf(buf, sizeof(buf), "%s/%s",
dirname[1]? dirname: "", files[i].name);
if (path_append(buf, dirname, files[i].name, sizeof(buf))
>= sizeof(buf))
{
continue;
}
result = playlist_directory_tracksearch(buf, recurse,
callback, context);
if (result < 0)
@ -3785,8 +3733,11 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse,
}
else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
{
snprintf(buf, sizeof(buf), "%s/%s",
dirname[1]? dirname: "", files[i].name);
if (path_append(buf, dirname, files[i].name, sizeof(buf))
>= sizeof(buf))
{
continue;
}
if (callback(buf, context) != 0)
{

View File

@ -32,7 +32,7 @@
#include "lang.h"
#include "list.h"
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "onplay.h"
#include "playlist.h"
#include "settings.h"

View File

@ -18,6 +18,8 @@
* KIND, either express or implied.
*
****************************************************************************/
#define DIRFUNCTIONS_DEFINED
#define FILEFUNCTIONS_DEFINED
#include "plugin.h"
#include <ctype.h>
#include <string.h>
@ -40,8 +42,9 @@
#include "pcmbuf.h"
#include "errno.h"
#include "diacritic.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "load_code.h"
#include "file.h"
#if CONFIG_CHARGING
#include "power.h"
@ -58,51 +61,7 @@
#include "usbstack/usb_hid.h"
#endif
#if defined (SIMULATOR)
#define PREFIX(_x_) sim_ ## _x_
#elif defined (APPLICATION)
#define PREFIX(_x_) app_ ## _x_
#else
#define PREFIX(_x_) _x_
#endif
#if defined (APPLICATION)
/* For symmetry reasons (we want app_ and sim_ to behave similarly), some
* wrappers are needed */
static int app_close(int fd)
{
return close(fd);
}
static ssize_t app_read(int fd, void *buf, size_t count)
{
return read(fd,buf,count);
}
static off_t app_lseek(int fd, off_t offset, int whence)
{
return lseek(fd,offset,whence);
}
static ssize_t app_write(int fd, const void *buf, size_t count)
{
return write(fd,buf,count);
}
static int app_ftruncate(int fd, off_t length)
{
return ftruncate(fd,length);
}
#endif
#if defined(HAVE_PLUGIN_CHECK_OPEN_CLOSE) && (MAX_OPEN_FILES>32)
#warning "MAX_OPEN_FILES>32, disabling plugin file open/close checking"
#undef HAVE_PLUGIN_CHECK_OPEN_CLOSE
#endif
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
static unsigned int open_files;
#endif
#define WRAPPER(_x_) _x_ ## _wrapper
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE];
@ -122,17 +81,100 @@ static void *current_plugin_handle;
char *plugin_get_current_filename(void);
/* Some wrappers used to monitor open and close and detect leaks*/
static int open_wrapper(const char* pathname, int flags, ...);
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
static int close_wrapper(int fd);
static int creat_wrapper(const char *pathname, mode_t mode);
#endif
static void* plugin_get_audio_buffer(size_t *buffer_size);
static void plugin_release_audio_buffer(void);
static void plugin_tsr(bool (*exit_callback)(bool));
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
/* File handle leak prophylaxis */
#include "bitarray.h"
#include "file_internal.h" /* for MAX_OPEN_FILES */
#define PCOC_WRAPPER(_x_) WRAPPER(_x_)
BITARRAY_TYPE_DECLARE(plugin_check_open_close_bitmap_t, open_files_bitmap,
MAX_OPEN_FILES)
static plugin_check_open_close_bitmap_t open_files_bitmap;
static void plugin_check_open_close__enter(void)
{
if (!current_plugin_handle)
open_files_bitmap_clear(&open_files_bitmap);
}
static void plugin_check_open_close__open(int fildes)
{
if (fildes >= 0)
open_files_bitmap_set_bit(&open_files_bitmap, fildes);
}
static void plugin_check_open_close__close(int fildes)
{
if (fildes < 0)
return;
if (!open_files_bitmap_test_bit(&open_files_bitmap, fildes))
{
logf("double close from plugin");
}
open_files_bitmap_clear_bit(&open_files_bitmap, fildes);
}
static int WRAPPER(open)(const char *path, int oflag, ...)
{
int fildes = FS_PREFIX(open)(path, oflag __OPEN_MODE_ARG);
plugin_check_open_close__open(fildes);
return fildes;
}
static int WRAPPER(creat)(const char *path, mode_t mode)
{
int fildes = FS_PREFIX(creat)(path __CREAT_MODE_ARG);
plugin_check_open_close__open(fildes);
return fildes;
(void)mode;
}
static int WRAPPER(close)(int fildes)
{
int rc = FS_PREFIX(close)(fildes);
if (rc >= 0)
plugin_check_open_close__close(fildes);
return rc;
}
static void plugin_check_open_close__exit(void)
{
if (current_plugin_handle)
return;
if (open_files_bitmap_is_clear(&open_files_bitmap))
return;
logf("Plugin '%s' leaks file handles", plugin);
static const char *lines[] =
{ ID2P(LANG_PLUGIN_ERROR), "#leak-file-handles" };
static const struct text_message message = { lines, 2 };
button_clear_queue(); /* Empty the keyboard buffer */
gui_syncyesno_run(&message, NULL, NULL);
FOR_EACH_BITARRAY_SET_BIT(&open_files_bitmap, fildes)
WRAPPER(close)(fildes);
}
#else /* !HAVE_PLUGIN_CHECK_OPEN_CLOSE */
#define PCOC_WRAPPER(_x_) FS_PREFIX(_x_)
#define plugin_check_open_close__enter()
#define plugin_check_open_close__exit()
#endif /* HAVE_PLUGIN_CHECK_OPEN_CLOSE */
static const struct plugin_api rockbox_api = {
/* lcd */
@ -339,24 +381,16 @@ static const struct plugin_api rockbox_api = {
/* file */
open_utf8,
(open_func)open_wrapper,
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
close_wrapper,
#else
PREFIX(close),
#endif
(read_func)PREFIX(read),
PREFIX(lseek),
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
(creat_func)creat_wrapper,
#else
PREFIX(creat),
#endif
(write_func)PREFIX(write),
PREFIX(remove),
PREFIX(rename),
PREFIX(ftruncate),
filesize,
PCOC_WRAPPER(open),
PCOC_WRAPPER(creat),
PCOC_WRAPPER(close),
FS_PREFIX(read),
FS_PREFIX(lseek),
FS_PREFIX(write),
FS_PREFIX(remove),
FS_PREFIX(rename),
FS_PREFIX(ftruncate),
FS_PREFIX(filesize),
fdprintf,
read_line,
settings_parseline,
@ -369,18 +403,18 @@ static const struct plugin_api rockbox_api = {
#endif /* USING_STORAGE_CALLBACK */
reload_directory,
create_numbered_filename,
file_exists,
FS_PREFIX(file_exists),
strip_extension,
crc_32,
filetype_get_attr,
/* dir */
(opendir_func)opendir,
(closedir_func)closedir,
(readdir_func)readdir,
mkdir,
rmdir,
dir_exists,
FS_PREFIX(opendir),
FS_PREFIX(closedir),
FS_PREFIX(readdir),
FS_PREFIX(mkdir),
FS_PREFIX(rmdir),
FS_PREFIX(dir_exists),
dir_get_info,
/* browsing */
@ -688,10 +722,11 @@ static const struct plugin_api rockbox_api = {
#endif
srand,
rand,
(qsort_func)qsort,
(void *)qsort,
kbd_input,
get_time,
set_time,
gmtime_r,
#if CONFIG_RTC
mktime,
#endif
@ -891,9 +926,7 @@ int plugin_load(const char* plugin, const void* parameter)
/* allow voice to back off if the plugin needs lots of memory */
talk_buffer_set_policy(TALK_BUFFER_LOOSE);
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
open_files = 0;
#endif
plugin_check_open_close__enter();
int rc = p_hdr->entry_point(parameter);
@ -947,24 +980,7 @@ int plugin_load(const char* plugin, const void* parameter)
FOR_NB_SCREENS(i)
viewportmanager_theme_undo(i, true);
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
if(open_files != 0 && !current_plugin_handle)
{
int fd;
logf("Plugin '%s' leaks file handles", plugin);
static const char *lines[] =
{ ID2P(LANG_PLUGIN_ERROR),
"#leak-file-handles" };
static const struct text_message message={ lines, 2 };
button_clear_queue(); /* Empty the keyboard buffer */
gui_syncyesno_run(&message, NULL, NULL);
for(fd=0; fd < MAX_OPEN_FILES; fd++)
if(open_files & (1<<fd))
close_wrapper(fd);
}
#endif
plugin_check_open_close__exit();
if (rc == PLUGIN_ERROR)
splash(HZ*2, str(LANG_PLUGIN_ERROR));
@ -1027,55 +1043,3 @@ char *plugin_get_current_filename(void)
{
return current_plugin;
}
static int open_wrapper(const char* pathname, int flags, ...)
{
/* we don't have an 'open' function. it's a define. and we need
* the real file_open, hence PREFIX() doesn't work here */
int fd;
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
if (flags & O_CREAT)
{
va_list ap;
va_start(ap, flags);
fd = open(pathname, flags, va_arg(ap, unsigned int));
va_end(ap);
}
else
fd = open(pathname, flags);
#else
fd = file_open(pathname,flags);
#endif
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
if(fd >= 0)
open_files |= 1<<fd;
#endif
return fd;
}
#ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE
static int close_wrapper(int fd)
{
if((~open_files) & (1<<fd))
{
logf("double close from plugin");
}
if(fd >= 0)
open_files &= (~(1<<fd));
return PREFIX(close)(fd);
}
static int creat_wrapper(const char *pathname, mode_t mode)
{
(void)mode;
int fd = PREFIX(creat)(pathname, mode);
if(fd >= 0)
open_files |= (1<<fd);
return fd;
}
#endif /* HAVE_PLUGIN_CHECK_OPEN_CLOSE */

View File

@ -75,7 +75,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#include "profile.h"
#endif
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#if (CONFIG_CODEC == SWCODEC)
#include "pcm_mixer.h"
#include "dsp-util.h"
@ -160,12 +160,12 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
#define PLUGIN_API_VERSION 231
#define PLUGIN_API_VERSION 232
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
#define PLUGIN_MIN_API_VERSION 231
#define PLUGIN_MIN_API_VERSION 232
/* plugin return codes */
/* internal returns start at 0x100 to make exit(1..255) work */
@ -433,17 +433,17 @@ struct plugin_api {
/* file */
int (*open_utf8)(const char* pathname, int flags);
int (*open)(const char* pathname, int flags, ...);
int (*close)(int fd);
ssize_t (*read)(int fd, void* buf, size_t count);
off_t (*lseek)(int fd, off_t offset, int whence);
int (*creat)(const char *pathname, mode_t mode);
ssize_t (*write)(int fd, const void* buf, size_t count);
int (*remove)(const char* pathname);
int (*rename)(const char* path, const char* newname);
int (*ftruncate)(int fd, off_t length);
off_t (*filesize)(int fd);
int (*fdprintf)(int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
int (*open)(const char *path, int oflag, ...);
int (*creat)(const char *path, mode_t mode);
int (*close)(int fildes);
ssize_t (*read)(int fildes, void *buf, size_t nbyte);
off_t (*lseek)(int fildes, off_t offset, int whence);
ssize_t (*write)(int fildes, const void *buf, size_t nbyte);
int (*remove)(const char *path);
int (*rename)(const char *old, const char *new);
int (*ftruncate)(int fildes, off_t length);
off_t (*filesize)(int fildes);
int (*fdprintf)(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
int (*read_line)(int fd, char* buffer, int buffer_size);
bool (*settings_parseline)(char* line, char** name, char** value);
void (*storage_sleep)(void);
@ -457,7 +457,7 @@ struct plugin_api {
char *(*create_numbered_filename)(char *buffer, const char *path,
const char *prefix, const char *suffix,
int numberlen IF_CNFN_NUM_(, int *num));
bool (*file_exists)(const char *file);
bool (*file_exists)(const char *path);
char* (*strip_extension)(char* buffer, int buffer_size, const char *filename);
uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32);
@ -466,13 +466,13 @@ struct plugin_api {
/* dir */
DIR* (*opendir)(const char* name);
int (*closedir)(DIR* dir);
struct dirent* (*readdir)(DIR* dir);
int (*mkdir)(const char *name);
int (*rmdir)(const char *name);
bool (*dir_exists)(const char *path);
struct dirinfo (*dir_get_info)(DIR* parent, struct dirent *entry);
DIR * (*opendir)(const char *dirname);
int (*closedir)(DIR *dirp);
struct dirent * (*readdir)(DIR *dirp);
int (*mkdir)(const char *path);
int (*rmdir)(const char *path);
bool (*dir_exists)(const char *dirname);
struct dirinfo (*dir_get_info)(DIR *dirp, struct dirent *entry);
/* browsing */
void (*browse_context_init)(struct browse_context *browse,
@ -838,6 +838,7 @@ struct plugin_api {
int (*kbd_input)(char* buffer, int buflen);
struct tm* (*get_time)(void);
int (*set_time)(const struct tm *tm);
struct tm * (*gmtime_r)(const time_t *timep, struct tm *tm);
#if CONFIG_RTC
time_t (*mktime)(struct tm *t);
#endif

View File

@ -99,13 +99,12 @@ static bool file_properties(char* selected_file)
log = human_size_log((unsigned long)info.size);
rb->snprintf(str_size, sizeof str_size, "%lu %cB",
((unsigned long)info.size) >> (log*10), human_size_prefix[log]);
struct tm tm;
rb->gmtime_r(&info.mtime, &tm);
rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d",
((info.wrtdate >> 9 ) & 0x7F) + 1980, /* year */
((info.wrtdate >> 5 ) & 0x0F), /* month */
((info.wrtdate ) & 0x1F)); /* day */
rb->snprintf(str_time, sizeof str_time, "%02d:%02d",
((info.wrttime >> 11) & 0x1F), /* hour */
((info.wrttime >> 5 ) & 0x3F)); /* minutes */
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d",
tm.tm_hour, tm.tm_min, tm.tm_sec);
num_properties = 5;
@ -175,7 +174,10 @@ static bool _dir_properties(DPS* dps)
dirlen = rb->strlen(dps->dirname);
dir = rb->opendir(dps->dirname);
if (!dir)
{
rb->splashf(HZ*2, "%s", dps->dirname);
return false; /* open error */
}
/* walk through the directory content */
while(result && (0 != (entry = rb->readdir(dir))))

View File

@ -30,7 +30,7 @@
#include "file.h"
#include "string-extra.h"
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "lang.h"
#include "action.h"
#include "list.h"

View File

@ -31,7 +31,7 @@
#include "file.h"
#include "kernel.h"
#include "string-extra.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "core_alloc.h"
#define MAX_RADIOART_IMAGES 10

View File

@ -27,7 +27,7 @@
#include "buffering.h"
#include "dircache.h"
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "settings.h"
#include "wps.h"

View File

@ -56,7 +56,7 @@
#include "timefuncs.h"
#include "debug.h"
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "tree.h"
#include "string.h"
#include "dir.h"

View File

@ -146,11 +146,10 @@ static int browser(void* param)
int i;
for (i = 0; i < NUM_VOLUMES; i++)
{
char vol_string[VOL_ENUM_POS + 8];
char vol_string[VOL_MAX_LEN + 1];
if (!volume_removable(i))
continue;
/* VOL_NAMES contains a %d */
snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, i);
get_volume_name(i, vol_string);
/* test whether we would browse the external card */
if (!volume_present(i) &&
(strstr(last_folder, vol_string)

View File

@ -33,7 +33,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging
#include "core_alloc.h"
#include "settings.h"
#include "ata_idle_notify.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "appevents.h"
#if CONFIG_RTC

View File

@ -822,6 +822,10 @@ void settings_apply(bool read_disk)
#ifdef HAVE_LCD_BITMAP
int rc;
#endif
CHART(">set_codepage");
set_codepage(global_settings.default_codepage);
CHART("<set_codepage");
sound_settings_apply();
#ifdef HAVE_DISK_STORAGE
@ -1008,10 +1012,6 @@ void settings_apply(bool read_disk)
lcd_scroll_delay(global_settings.scroll_delay);
CHART(">set_codepage");
set_codepage(global_settings.default_codepage);
CHART("<set_codepage");
#ifdef HAVE_PLAY_FREQ
settings_apply_play_freq(global_settings.play_frequency, false);
#endif

View File

@ -1724,13 +1724,13 @@ const struct settings_list settings[] = {
OFFON_SETTING(F_BANFROMQS, tagcache_autoupdate, LANG_TAGCACHE_AUTOUPDATE, false,
"tagcache_autoupdate", NULL),
#endif
CHOICE_SETTING(0, default_codepage, LANG_DEFAULT_CODEPAGE, 0,
CHOICE_SETTING(F_TEMPVAR, default_codepage, LANG_DEFAULT_CODEPAGE, 0,
"default codepage",
#ifdef HAVE_LCD_BITMAP
/* The order must match with that in unicode.c */
"iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256,"
"iso8859-9,iso8859-2,cp1250,cp1252,sjis,gb2312,ksx1001,big5,utf-8",
set_codepage, 15,
NULL, 15,
ID2P(LANG_CODEPAGE_LATIN1),
ID2P(LANG_CODEPAGE_GREEK),
ID2P(LANG_CODEPAGE_HEBREW), ID2P(LANG_CODEPAGE_CYRILLIC),
@ -1745,7 +1745,7 @@ const struct settings_list settings[] = {
#else /* !HAVE_LCD_BITMAP */
/* The order must match with that in unicode.c */
"iso8859-1,iso8859-7,cp1251,iso8859-9,iso8859-2,cp1250,cp1252,utf-8",
set_codepage, 8,
NULL, 8,
ID2P(LANG_CODEPAGE_LATIN1), ID2P(LANG_CODEPAGE_GREEK),
ID2P(LANG_CODEPAGE_CYRILLIC), ID2P(LANG_CODEPAGE_TURKISH),
ID2P(LANG_CODEPAGE_LATIN_EXTENDED),

View File

@ -37,7 +37,7 @@
#include "misc.h"
#include "tree.h"
#include "splash.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "filetypes.h"
#include "shortcuts.h"
#include "onplay.h"

View File

@ -79,7 +79,7 @@
#include "misc.h"
#include "settings.h"
#include "dir.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "structec.h"
#include "debug.h"
@ -88,6 +88,8 @@
#include "eeprom_settings.h"
#endif
#undef HAVE_DIRCACHE
#ifdef __PCTOOL__
#define yield() do { } while(0)
#define sim_sleep(timeout) do { } while(0)
@ -2986,20 +2988,21 @@ static bool commit(void)
/* Try to steal every buffer we can :) */
if (tempbuf_size == 0)
local_allocation = true;
#if 0 /* FIXME: How much big? dircache buffer can no longer be taken but
may be freed to make room and the cache resumed. --jethead71 */
#ifdef HAVE_DIRCACHE
if (tempbuf_size == 0)
{
/* Try to steal the dircache buffer. */
tempbuf = dircache_steal_buffer(&tempbuf_size);
tempbuf_size &= ~0x03;
/* Shut down dircache to free its allocation. */
dircache_free_buffer();
if (tempbuf_size > 0)
{
dircache_buffer_stolen = true;
}
}
#endif
#endif
#endif
#ifdef HAVE_TC_RAMCACHE
if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0)
@ -4462,7 +4465,7 @@ static bool check_dir(const char *dirname, int add_files)
tc_stat.curentry = curpath;
/* Add a new entry to the temporary db file. */
add_tagcache(curpath, (info.wrtdate << 16) | info.wrttime
add_tagcache(curpath, info.mtime
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
, dir->internal_entry
#endif
@ -4780,7 +4783,7 @@ void tagcache_shutdown(void)
/* Flush the command queue. */
run_command_queue(true);
#ifdef HAVE_EEPROM_SETTINGS
#if defined(HAVE_EEPROM_SETTINGS) && defined(HAVE_TC_RAMCACHE)
if (tc_stat.ramcache)
tagcache_dumpsave();
#endif

View File

@ -52,7 +52,7 @@
#include "talk.h"
#include "filetypes.h"
#include "misc.h"
#include "filefuncs.h"
#include "pathfuncs.h"
#include "filetree.h"
#include "tagtree.h"
#ifdef HAVE_RECORDING
@ -1205,26 +1205,36 @@ void tree_flush(void)
#endif
#ifdef HAVE_DIRCACHE
{
int old_val = global_status.dircache_size;
if (global_settings.dircache)
{
if (!dircache_is_initializing())
global_status.dircache_size = dircache_get_cache_size();
# ifdef HAVE_EEPROM_SETTINGS
if (firmware_settings.initialized)
dircache_save();
# endif
dircache_suspend();
}
else
{
global_status.dircache_size = 0;
}
if (old_val != global_status.dircache_size)
status_save();
}
int old_val = global_status.dircache_size;
#ifdef HAVE_EEPROM_SETTINGS
bool savecache = false;
#endif
if (global_settings.dircache)
{
dircache_suspend();
struct dircache_info info;
dircache_get_info(&info);
global_status.dircache_size = info.last_size;
#ifdef HAVE_EEPROM_SETTINGS
savecache = firmware_settings.initialized;
#endif
}
else
{
global_status.dircache_size = 0;
}
if (old_val != global_status.dircache_size)
status_save();
#ifdef HAVE_EEPROM_SETTINGS
if (savecache)
dircache_save();
#endif
#endif /* HAVE_DIRCACHE */
}
void tree_restore(void)
@ -1238,15 +1248,14 @@ void tree_restore(void)
#endif
#ifdef HAVE_DIRCACHE
remove(DIRCACHE_FILE);
if (global_settings.dircache)
if (global_settings.dircache && dircache_resume() > 0)
{
/* Print "Scanning disk..." to the display. */
splash(0, str(LANG_SCANNING_DISK));
dircache_build(global_status.dircache_size);
dircache_wait();
}
#endif
#ifdef HAVE_TAGCACHE
tagcache_start_scan();
#endif

View File

@ -22,6 +22,7 @@
#include "../kernel-internal.h"
#include "storage.h"
#include "ata-target.h"
#include "file_internal.h"
#include "disk.h"
#include "font.h"
#include "backlight.h"
@ -73,6 +74,8 @@ void main(void)
ret = storage_init();
if(ret)
printf("ATA error: %d", ret);
filesystem_init();
/* If no button is held, start the OF */
if(button_read_device() == 0)
@ -93,8 +96,6 @@ void main(void)
}
else
{
disk_init();
ret = disk_mount_all();
if (ret <= 0)
error(EDISK, ret, true);

View File

@ -21,13 +21,16 @@
#include "config.h"
#include "system.h"
#include <stdio.h>
#include <errno.h>
#include "../kernel-internal.h"
#include "gcc_extensions.h"
#include "string.h"
#include "adc.h"
#include "powermgmt.h"
#include "storage.h"
#include "file_internal.h"
#include "dir.h"
#include "file.h"
#include "disk.h"
#include "common.h"
#include "rb-loader.h"
@ -219,7 +222,7 @@ static void untar(int tar_fd)
/* Create the dir */
ret = mkdir(path);
if (ret < 0 && ret != -4)
if (ret < 0 && errno != EEXIST)
{
printf("failed to create dir (%d)", ret);
}
@ -233,14 +236,14 @@ static void handle_untar(void)
char buf[MAX_PATH];
char tarstring[6];
char model[5];
struct dirent_uncached* entry;
DIR_UNCACHED* dir;
struct dirent* entry;
DIR* dir;
int fd;
int rc;
dir = opendir_uncached(basedir);
dir = opendir(basedir);
while ((entry = readdir_uncached(dir)))
while ((entry = readdir(dir)))
{
if (*entry->d_name == '.')
continue;
@ -262,7 +265,6 @@ static void handle_untar(void)
verbose = true;
printf("Found rockbox binary. Moving...");
close(fd);
remove( BOOTDIR "/" BOOTFILE);
int ret = rename(buf, BOOTDIR "/" BOOTFILE);
printf("returned %d", ret);
sleep(HZ);
@ -365,7 +367,7 @@ void main(void)
if(rc)
error(EATA, rc, true);
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc <= 0)

View File

@ -30,6 +30,7 @@
#include "../kernel-internal.h"
#include "storage.h"
#include "fat.h"
#include "file_internal.h"
#include "disk.h"
#include "font.h"
#include "adc.h"
@ -185,7 +186,7 @@ void main(void)
error(EATA, rc, true);
}
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc<=0)

View File

@ -196,7 +196,7 @@ void main(void)
if(rc)
error(EATA, rc, true);
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc<=0)

View File

@ -35,6 +35,7 @@
#include "rb-loader.h"
#include "loader_strerror.h"
#include "storage.h"
#include "file_internal.h"
#include "disk.h"
#include "panic.h"
#include "power.h"
@ -171,7 +172,7 @@ void main(uint32_t arg, uint32_t addr)
if(ret < 0)
error(EATA, ret, true);
disk_init_subsystem();
filesystem_init();
/* NOTE: disk_mount_all to fail since we can do USB after.
* We need this order to determine the correct logical sector size */

View File

@ -33,6 +33,7 @@
#include "../kernel-internal.h"
#include "ata.h"
#include "fat.h"
#include "file_internal.h"
#include "disk.h"
#include "font.h"
#include "adc.h"
@ -295,7 +296,7 @@ void* main(void)
int rc;
bool haveramos;
bool button_was_held;
struct partinfo* pinfo;
struct partinfo pinfo;
unsigned short* identify_info;
/* Check the button hold status as soon as possible - to
@ -353,7 +354,8 @@ void* main(void)
printf("ATA: %d", i);
}
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc<=0)
{
@ -361,9 +363,9 @@ void* main(void)
fatal_error();
}
pinfo = disk_partinfo(1);
disk_partinfo(1, &pinfo);
printf("Partition 1: 0x%02x %ld sectors",
pinfo->type, pinfo->size);
pinfo.type, pinfo.size);
if (button_was_held || (btn==BUTTON_MENU)) {
/* If either the hold switch was on, or the Menu button was held, then

View File

@ -32,6 +32,7 @@
#include "lcd.h"
#include "i2c-s5l8700.h"
#include "../kernel-internal.h"
#include "file_internal.h"
#include "storage.h"
#include "fat.h"
#include "disk.h"
@ -213,7 +214,8 @@ void main(void)
fatal_error();
}
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc<=0)
{

View File

@ -31,6 +31,7 @@
#include "scroll_engine.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "usb.h"
#include "disk.h"
#include "font.h"
@ -596,7 +597,7 @@ void main(void)
}
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc<=0)

View File

@ -31,6 +31,7 @@
#include "scroll_engine.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "usb.h"
#include "disk.h"
#include "font.h"
@ -349,8 +350,7 @@ void main(void)
while(!(button_get(true) & BUTTON_REL));
}
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc<=0)

View File

@ -26,12 +26,13 @@
#include <stdlib.h>
#include "common.h"
#include "cpu.h"
#include "file.h"
#include "system.h"
#include "../kernel-internal.h"
#include "lcd.h"
#include "font.h"
#include "storage.h"
#include "file_internal.h"
#include "file.h"
#include "button.h"
#include "disk.h"
#include "crc32-mi4.h"
@ -92,7 +93,7 @@ void* main(void)
int num_partitions;
int crc32;
char sector[512];
struct partinfo* pinfo;
struct partinfo pinfo;
system_init();
kernel_init();
@ -117,7 +118,7 @@ void* main(void)
printf("");
i=storage_init();
disk_init(IF_MV(0));
filesystem_init();
num_partitions = disk_mount_all();
if (num_partitions<=0)
@ -125,17 +126,17 @@ void* main(void)
error(EDISK, num_partitions, true);
}
pinfo = disk_partinfo(1);
disk_partinfo(1, &pinfo);
#if 0 /* not needed in release builds */
printf("--- Partition info ---");
printf("start: %x", pinfo->start);
printf("size: %x", pinfo->size);
printf("type: %x", pinfo->type);
printf("start: %x", pinfo.start);
printf("size: %x", pinfo.size);
printf("type: %x", pinfo.type);
printf("reading: %x", (START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK)*512);
#endif
storage_read_sectors(pinfo->start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK,
storage_read_sectors(pinfo.start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK,
1 , sector);
crc32 = chksum_crc32 (sector, 512);
@ -157,7 +158,7 @@ void* main(void)
memcpy(&sector[HACK_OFFSET], changedBytes,
sizeof(changedBytes)/sizeof(*changedBytes));
storage_write_sectors(
pinfo->start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK,
pinfo.start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK,
1 , sector);
printf("Firmware unlocked");
printf("Proceed to Step 2");

View File

@ -33,6 +33,7 @@
#include "lcd.h"
#include "font.h"
#include "storage.h"
#include "file_internal.h"
#include "adc.h"
#include "button.h"
#include "disk.h"
@ -291,7 +292,7 @@ void* main(void)
int btn;
int rc;
int num_partitions;
struct partinfo* pinfo;
struct partinfo pinfo;
#if !(CONFIG_STORAGE & STORAGE_SD)
char buf[256];
unsigned short* identify_info;
@ -370,7 +371,7 @@ void* main(void)
}
#endif
disk_init(IF_MV(0));
filesystem_init();
num_partitions = disk_mount_all();
if (num_partitions<=0)
{
@ -381,9 +382,9 @@ void* main(void)
that have more than that */
for(i=0; i<NUM_PARTITIONS; i++)
{
pinfo = disk_partinfo(i);
disk_partinfo(i, &pinfo);
printf("Partition %d: 0x%02x %ld MB",
i, pinfo->type, pinfo->size / 2048);
i, pinfo.type, pinfo.size / 2048);
}
/* Now that storage is initialized, check for USB connection */
@ -430,10 +431,10 @@ void* main(void)
#if (CONFIG_STORAGE & STORAGE_SD)
/* First try a (hidden) firmware partition */
printf("Trying firmware partition");
pinfo = disk_partinfo(1);
if(pinfo->type == PARTITION_TYPE_OS2_HIDDEN_C_DRIVE)
disk_partinfo(1, &pinfo);
if(pinfo.type == PARTITION_TYPE_OS2_HIDDEN_C_DRIVE)
{
rc = load_mi4_part(loadbuffer, pinfo, MAX_LOADSIZE,
rc = load_mi4_part(loadbuffer, &pinfo, MAX_LOADSIZE,
usb == USB_INSERTED);
if (rc <= EFILE_EMPTY) {
printf("Can't load from partition");

View File

@ -29,6 +29,7 @@
#include "lcd.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "fat.h"
#include "disk.h"
#include "font.h"
@ -88,7 +89,7 @@ int main(void)
error(EATA, rc, true);
}
disk_init(IF_MD(0));
filesystem_init();
rc = disk_mount_all();
if (rc<=0)
{

View File

@ -29,6 +29,7 @@
#include "lcd.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "usb.h"
#include "disk.h"
#include "font.h"
@ -202,7 +203,7 @@ static void rb_boot(void)
if(rc)
error(EATA, rc, true);
disk_init();
filesystem_init();
rc = disk_mount_all();
if (rc <= 0)

View File

@ -26,6 +26,7 @@
#include "lcd.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "fat.h"
#include "disk.h"
#include "font.h"
@ -123,8 +124,8 @@ void main(void)
error(EATA, rc, true);
}
printf("disk");
disk_init();
printf("filesystem");
filesystem_init();
printf("mount");
rc = disk_mount_all();

View File

@ -33,6 +33,7 @@
#include "rb-loader.h"
#include "loader_strerror.h"
#include "storage.h"
#include "file_internal.h"
#include "disk.h"
#include "string.h"
#include "adc.h"
@ -269,6 +270,8 @@ int main(void)
show_logo();
filesystem_init();
rc = storage_init();
if(rc)
error(EATA, rc, true);

View File

@ -12,6 +12,7 @@
#include "button.h"
#include "common.h"
#include "storage.h"
#include "file_internal.h"
#include "disk.h"
#include "panic.h"
#include "power.h"
@ -146,8 +147,7 @@ void main(void)
if(ret < 0)
error(EATA, ret, true);
while(!disk_init(IF_MV(0)))
panicf("disk_init failed!");
filesystem_init();
while((ret = disk_mount_all()) <= 0)
error(EDISK, ret, true);

View File

@ -39,6 +39,7 @@
#include "rb-loader.h"
#include "loader_strerror.h"
#include "storage.h"
#include "file_internal.h"
#include "disk.h"
#include "panic.h"
#include "power.h"
@ -119,6 +120,8 @@ void main(void)
if(ret < 0)
error(EATA, ret, true);
filesystem_init();
#ifdef USE_ROCKBOX_USB
usb_init();
usb_start_monitoring();
@ -128,13 +131,6 @@ void main(void)
usb_mode();
#endif /* USE_ROCKBOX_USB */
while(!disk_init(IF_MV(0)))
#ifdef USE_ROCKBOX_USB
usb_mode();
#else
panicf("disk_init failed!");
#endif
while((ret = disk_mount_all()) <= 0)
{
#ifdef USE_ROCKBOX_USB

View File

@ -21,6 +21,7 @@
#include "lcd.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "disk.h"
#include "font.h"
#include "backlight.h"
@ -72,7 +73,7 @@ void main(void)
if(ret)
printf("SD error: %d", ret);
disk_init(IF_MD(0));
filesystem_init();
ret = disk_mount_all();
if (ret <= 0)

View File

@ -30,6 +30,7 @@
#include "lcd.h"
#include "../kernel-internal.h"
#include "storage.h"
#include "file_internal.h"
#include "fat.h"
#include "disk.h"
#include "font.h"
@ -162,6 +163,8 @@ void* main(void)
error(EATA, rc, true);
}
filesystem_init();
printf("mount");
rc = disk_mount_all();
if (rc<=0)

View File

@ -31,6 +31,7 @@
#include "kernel.h"
#include "thread.h"
#include "storage.h"
#include "file_internal.h"
#include "fat.h"
#include "disk.h"
#include "font.h"
@ -56,7 +57,7 @@ void* main(void)
i=storage_init();
disk_init();
filesystem_init();
rc = disk_mount_all();
#if 0

View File

@ -59,17 +59,27 @@ target/hosted/sdl/lcd-remote-bitmap.c
target/hosted/sdl/lcd-sdl.c
target/hosted/sdl/system-sdl.c
#ifdef HAVE_SDL_THREADS
target/hosted/sdl/thread-sdl.c
#else
target/hosted/sdl/filesystem-sdl.c
#endif
target/hosted/sdl/load_code-sdl.c
target/hosted/sdl/timer-sdl.c
#ifdef HAVE_TOUCHSCREEN
target/hosted/sdl/key_to_touch-sdl.c
#endif
#ifdef APPLICATION
target/hosted/sdl/app/load_code-sdl-app.c
target/hosted/sdl/app/button-application.c
#endif
#endif
#ifdef WIN32
target/hosted/filesystem-win32.c
#else /* !WIN32 */
target/hosted/filesystem-unix.c
#endif /* WIN32 */
#endif /* APPLICATION */
#endif /* HAVE_SDL */
#ifdef APPLICATION
target/hosted/filesystem-app.c
#endif /* APPLICATION */
#if defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)
target/hosted/kernel-unix.c
@ -163,19 +173,19 @@ common/crc32-mi4.c
common/crc32-rkw.c
#endif
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
common/dir_uncached.c
common/dir.c
common/disk_cache.c
common/file.c
common/file_internal.c
common/disk.c
common/fileobj_mgr.c
#endif /* PLATFORM_NATIVE */
#ifdef HAVE_DIRCACHE
common/dircache.c
#endif /* HAVE_DIRCACHE */
common/filefuncs.c
common/pathfuncs.c
common/format.c
common/linked_list.c
#ifdef APPLICATION
common/rbpaths.c
#endif
common/strcasecmp.c
common/strcasestr.c
common/strnatcmp.c
@ -1816,7 +1826,20 @@ target/hosted/android/app/button-application.c
drivers/audio/android.c
#endif
#endif /* defined(SIMULATOR) */
#else /* defined(SIMULATOR) */
#ifdef WIN32
asm/mempcpy.c
target/hosted/filesystem-win32.c
#else /* !WIN32 */
target/hosted/filesystem-unix.c
#endif /* WIN32 */
target/hosted/sdl/load_code-sdl.c
#ifdef HAVE_SDL_THREADS
target/hosted/sdl/filesystem-sdl.c
#endif
#endif /* !defined(SIMULATOR) */
#if defined(HAVE_TOUCHPAD) && !defined(HAS_BUTTON_HOLD)
drivers/touchpad.c
@ -1826,9 +1849,7 @@ drivers/touchpad.c
#ifdef HAVE_CORELOCK_OBJECT
kernel/corelock.c
#endif
#if 0 /* pending dependent code */
kernel/mrsw_lock.c
#endif
kernel/mutex.c
kernel/queue.c
#ifdef HAVE_SEMAPHORE_OBJECTS

View File

@ -63,13 +63,13 @@ memcpy:
SWHI t0, 0(a0)
addu a0, t1
chk8w:
chk8w:
andi t0, a2, 0x1f # 32 or more bytes left?
beq t0, a2, chk1w
subu a3, a2, t0 # Yes
addu a3, a1 # a3 = end address of loop
move a2, t0 # a2 = what will be left after loop
lop8w:
lop8w:
lw t0, 0(a1) # Loop taking 8 words at a time
lw t1, 4(a1)
lw t2, 8(a1)
@ -90,34 +90,34 @@ lop8w:
bne a1, a3, lop8w
sw t7, -4(a0)
chk1w:
chk1w:
andi t0, a2, 0x3 # 4 or more bytes left?
beq t0, a2, last8
subu a3, a2, t0 # Yes, handle them one word at a time
addu a3, a1 # a3 again end address
move a2, t0
lop1w:
lop1w:
lw t0, 0(a1)
addiu a0, 4
addiu a1, 4
bne a1, a3, lop1w
sw t0, -4(a0)
last8:
last8:
blez a2, lst8e # Handle last 8 bytes, one at a time
addu a3, a2, a1
lst8l:
lst8l:
lb t0, 0(a1)
addiu a0, 1
addiu a1, 1
bne a1, a3, lst8l
sb t0, -1(a0)
lst8e:
lst8e:
jr ra # Bye, bye
nop
shift:
subu a3, zero, a0 # Src and Dest unaligned
shift:
subu a3, zero, a0 # Src and Dest unaligned
andi a3, 0x3 # (unoptimized case...)
beq a3, zero, shft1
subu a2, a3 # a2 = bytes left
@ -126,11 +126,11 @@ shift:
addu a1, a3
SWHI t0, 0(a0)
addu a0, a3
shft1:
shft1:
andi t0, a2, 0x3
subu a3, a2, t0
addu a3, a1
shfth:
shfth:
LWHI t1, 0(a1) # Limp through, word by word
LWLO t1, 3(a1)
addiu a0, 4

View File

@ -60,13 +60,13 @@ ___memcpy_fwd_entry:
cmp/hs r0,r6 /* at least 11 bytes to copy? (ensures 2 aligned longs) */
add r5,r6 /* r6 = source_end */
bf .start_b2 /* no: jump directly to byte loop */
mov #3,r0
neg r5,r3
and r0,r3 /* r3 = (4 - align_offset) % 4 */
tst r3,r3 /* already aligned? */
bt .end_b1 /* yes: skip leading byte loop */
add r5,r3 /* r3 = first source long bound */
/* leading byte loop: copies 0..3 bytes */
@ -89,7 +89,7 @@ ___memcpy_fwd_entry:
mov r6,r3 /* move end address to r3 */
jmp @r1 /* and jump to it */
add #-7,r3 /* adjust end addr for main loops doing 2 longs/pass */
/** main loops, copying 2 longs per pass to profit from fast page mode **/
/* long aligned destination (fastest) */
@ -102,11 +102,11 @@ ___memcpy_fwd_entry:
mov.l r0,@-r4 /* store second long */
mov.l r1,@-r4 /* store first long; NOT ALIGNED - no speed loss here! */
bt .loop_do0
add #4,r3 /* readjust end address */
cmp/hi r5,r3 /* one long left? */
bf .start_b2 /* no, jump to trailing byte loop */
mov.l @r5+,r0 /* load last long & increment source addr */
add #4,r4 /* increment dest addr */
bra .start_b2 /* jump to trailing byte loop */

413
firmware/common/dir.c Normal file
View File

@ -0,0 +1,413 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Björn Stenberg
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#define DIRFUNCTIONS_DEFINED
#include "config.h"
#include <errno.h>
#include <string.h>
#include "debug.h"
#include "dir.h"
#include "pathfuncs.h"
#include "fileobj_mgr.h"
#include "dircache_redirect.h"
/* structure used for open directory streams */
static struct dirstr_desc
{
struct filestr_base stream; /* basic stream info (first!) */
struct dirscan_info scan; /* directory scan cursor */
struct dirent entry; /* current parsed entry information */
#ifdef HAVE_MULTIVOLUME
int volumecounter; /* counter for root volume entries */
#endif
} open_streams[MAX_OPEN_DIRS];
/* check and return a struct dirstr_desc* from a DIR* */
static struct dirstr_desc * get_dirstr(DIR *dirp)
{
struct dirstr_desc *dir = (struct dirstr_desc *)dirp;
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
dir = NULL;
else if (dir->stream.flags & FDO_BUSY)
return dir;
int errnum;
if (!dir)
{
errnum = EFAULT;
}
else if (dir->stream.flags == FV_NONEXIST)
{
DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams));
errnum = ENXIO;
}
else
{
DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams));
errnum = EBADF;
}
errno = errnum;
return NULL;
}
#define GET_DIRSTR(type, dirp) \
({ \
file_internal_lock_##type(); \
struct dirstr_desc *_dir = get_dirstr(dirp); \
if (_dir) \
FILESTR_LOCK(type, &_dir->stream); \
else \
file_internal_unlock_##type(); \
_dir; \
})
/* release the lock on the dirstr_desc* */
#define RELEASE_DIRSTR(type, dir) \
({ \
FILESTR_UNLOCK(type, &(dir)->stream); \
file_internal_unlock_##type(); \
})
/* find a free dir stream descriptor */
static struct dirstr_desc * alloc_dirstr(void)
{
for (unsigned int dd = 0; dd < MAX_OPEN_DIRS; dd++)
{
struct dirstr_desc *dir = &open_streams[dd];
if (!dir->stream.flags)
return dir;
}
DEBUGF("Too many dirs open\n");
return NULL;
}
#ifdef HAVE_MULTIVOLUME
static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry)
{
/* Volumes (secondary file systems) get inserted into the system root
* directory. If the path specified volume 0, enumeration will not
* include other volumes, but just its own files and directories.
*
* Fake special directories, which don't really exist, that will get
* redirected upon opendir()
*/
while (++dir->volumecounter < NUM_VOLUMES)
{
/* on the system root */
if (!fat_ismounted(dir->volumecounter))
continue;
get_volume_name(dir->volumecounter, entry->d_name);
dir->entry.info.attr = ATTR_MOUNT_POINT;
dir->entry.info.size = 0;
dir->entry.info.wrtdate = 0;
dir->entry.info.wrttime = 0;
return 1;
}
/* do normal directory entry fetching */
return 0;
}
#endif /* HAVE_MULTIVOLUME */
static inline int readdir_volume(struct dirstr_desc *dir,
struct dirent *entry)
{
#ifdef HAVE_MULTIVOLUME
/* fetch virtual volume entries? */
if (dir->volumecounter < NUM_VOLUMES)
return readdir_volume_inner(dir, entry);
#endif /* HAVE_MULTIVOLUME */
/* do normal directory entry fetching */
return 0;
(void)dir; (void)entry;
}
/** POSIX interface **/
/* open a directory */
DIR * opendir(const char *dirname)
{
DEBUGF("opendir(dirname=\"%s\"\n", dirname);
DIR *dirp = NULL;
file_internal_lock_WRITER();
int rc;
struct dirstr_desc * const dir = alloc_dirstr();
if (!dir)
FILE_ERROR(EMFILE, RC);
rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL);
if (rc < 0)
{
DEBUGF("Open failed: %d\n", rc);
FILE_ERROR(ERRNO, RC);
}
#ifdef HAVE_MULTIVOLUME
/* volume counter is relevant only to the system root */
dir->volumecounter = rc > 1 ? 0 : INT_MAX;
#endif /* HAVE_MULTIVOLUME */
fat_rewind(&dir->stream.fatstr);
rewinddir_dirent(&dir->scan);
dirp = (DIR *)dir;
file_error:
file_internal_unlock_WRITER();
return dirp;
}
/* close a directory stream */
int closedir(DIR *dirp)
{
int rc;
file_internal_lock_WRITER();
/* needs to work even if marked "nonexistant" */
struct dirstr_desc * const dir = (struct dirstr_desc *)dirp;
if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS))
FILE_ERROR(EFAULT, -1);
if (!dir->stream.flags)
{
DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams));
FILE_ERROR(EBADF, -2);
}
rc = close_stream_internal(&dir->stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 3);
file_error:
file_internal_unlock_WRITER();
return rc;
}
/* read a directory */
struct dirent * readdir(DIR *dirp)
{
struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
if (!dir)
FILE_ERROR_RETURN(ERRNO, NULL);
struct dirent *res = NULL;
int rc = readdir_volume(dir, &dir->entry);
if (rc == 0)
{
rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry);
if (rc < 0)
FILE_ERROR(EIO, RC);
}
if (rc > 0)
res = &dir->entry;
file_error:
RELEASE_DIRSTR(READER, dir);
if (rc > 1)
iso_decode_d_name(res->d_name);
return res;
}
#if 0 /* not included now but probably should be */
/* read a directory (reentrant) */
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{
if (!result)
FILE_ERROR_RETURN(EFAULT, -2);
*result = NULL;
if (!entry)
FILE_ERROR_RETURN(EFAULT, -3);
struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
if (!dir)
FILE_ERROR_RETURN(ERRNO, -1);
int rc = readdir_volume(dir, entry);
if (rc == 0)
{
rc = readdir_dirent(&dir->stream, &dir->scan, entry);
if (rc < 0)
FILE_ERROR(EIO, rc * 10 - 4);
}
file_error:
RELEASE_DIRSTR(READER, dir);
if (rc > 0)
{
if (rc > 1)
iso_decode_d_name(entry->d_name);
*result = entry;
rc = 0;
}
return rc;
}
/* reset the position of a directory stream to the beginning of a directory */
void rewinddir(DIR *dirp)
{
struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp);
if (!dir)
FILE_ERROR_RETURN(ERRNO);
rewinddir_dirent(&dir->scan);
#ifdef HAVE_MULTIVOLUME
if (dir->volumecounter != INT_MAX)
dir->volumecounter = 0;
#endif /* HAVE_MULTIVOLUME */
RELEASE_DIRSTR(READER, dir);
}
#endif /* 0 */
/* make a directory */
int mkdir(const char *path)
{
DEBUGF("mkdir(path=\"%s\")\n", path);
int rc;
file_internal_lock_WRITER();
struct filestr_base stream;
struct path_component_info compinfo;
rc = open_stream_internal(path, FF_DIR, &stream, &compinfo);
if (rc < 0)
{
DEBUGF("Can't open parent dir or path is not a directory\n");
FILE_ERROR(ERRNO, rc * 10 - 1);
}
else if (rc > 0)
{
DEBUGF("File exists\n");
FILE_ERROR(EEXIST, -2);
}
rc = create_stream_internal(&compinfo.parentinfo, compinfo.name,
compinfo.length, ATTR_NEW_DIRECTORY,
FO_DIRECTORY, &stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 3);
rc = 0;
file_error:
close_stream_internal(&stream);
file_internal_unlock_WRITER();
return rc;
}
/* remove a directory */
int rmdir(const char *name)
{
DEBUGF("rmdir(name=\"%s\")\n", name);
if (name)
{
/* path may not end with "." */
const char *basename;
size_t len = path_basename(name, &basename);
if (basename[0] == '.' && len == 1)
{
DEBUGF("Invalid path; last component is \".\"\n");
FILE_ERROR_RETURN(EINVAL, -9);
}
}
file_internal_lock_WRITER();
int rc = remove_stream_internal(name, NULL, FF_DIR);
file_internal_unlock_WRITER();
return rc;
}
/** Extended interface **/
/* return if two directory streams refer to the same directory */
int samedir(DIR *dirp1, DIR *dirp2)
{
struct dirstr_desc * const dir1 = GET_DIRSTR(WRITER, dirp1);
if (!dir1)
FILE_ERROR_RETURN(ERRNO, -1);
int rc = -2;
struct dirstr_desc * const dir2 = get_dirstr(dirp2);
if (dir2)
rc = dir1->stream.bindp == dir2->stream.bindp ? 1 : 0;
RELEASE_DIRSTR(WRITER, dir1);
return rc;
}
/* test directory existence (returns 'false' if a file) */
bool dir_exists(const char *dirname)
{
file_internal_lock_WRITER();
bool rc = test_stream_exists_internal(dirname, FF_DIR) > 0;
file_internal_unlock_WRITER();
return rc;
}
/* get the portable info from the native entry */
struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry)
{
int rc;
if (!dirp || !entry)
FILE_ERROR(EFAULT, RC);
if (entry->d_name[0] == '\0')
FILE_ERROR(ENOENT, RC);
if ((file_size_t)entry->info.size > FILE_SIZE_MAX)
FILE_ERROR(EOVERFLOW, RC);
return (struct dirinfo)
{
.attribute = entry->info.attr,
.size = entry->info.size,
.mtime = fattime_mktime(entry->info.wrtdate, entry->info.wrttime),
};
file_error:
return (struct dirinfo){ .attribute = 0 };
}

View File

@ -1,312 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $
*
* Copyright (C) 2002 by Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include "fat.h"
#include "dir.h"
#include "debug.h"
#include "filefuncs.h"
#if (MEMORYSIZE > 8)
#define MAX_OPEN_DIRS 12
#else
#define MAX_OPEN_DIRS 8
#endif
static DIR_UNCACHED opendirs[MAX_OPEN_DIRS];
// release all dir handles on a given volume "by force", to avoid leaks
int release_dirs(int volume)
{
DIR_UNCACHED* pdir = opendirs;
int dd;
int closed = 0;
for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
{
#ifdef HAVE_MULTIVOLUME
if (pdir->fatdir.file.volume == volume)
#else
(void)volume;
#endif
{
pdir->busy = false; /* mark as available, no further action */
closed++;
}
}
return closed; /* return how many we did */
}
DIR_UNCACHED* opendir_uncached(const char* name)
{
char namecopy[MAX_PATH];
char* part;
char* end;
struct fat_direntry entry;
int dd;
DIR_UNCACHED* pdir = opendirs;
#ifdef HAVE_MULTIVOLUME
int volume;
#endif
if ( name[0] != '/' ) {
DEBUGF("Only absolute paths supported right now\n");
return NULL;
}
/* find a free dir descriptor */
for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
if ( !pdir->busy )
break;
if ( dd == MAX_OPEN_DIRS ) {
DEBUGF("Too many dirs open\n");
errno = EMFILE;
return NULL;
}
pdir->busy = true;
#ifdef HAVE_MULTIVOLUME
/* try to extract a heading volume name, if present */
volume = strip_volume(name, namecopy);
pdir->volumecounter = 0;
#else
strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */
#endif
if ( fat_opendir(IF_MV(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
DEBUGF("Failed opening root dir\n");
pdir->busy = false;
return NULL;
}
for ( part = strtok_r(namecopy, "/", &end); part;
part = strtok_r(NULL, "/", &end)) {
/* scan dir for name */
while (1) {
if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
(!entry.name[0])) {
pdir->busy = false;
return NULL;
}
if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
(!strcasecmp(part, entry.name)) ) {
/* In reality, the parent_dir parameter of fat_opendir seems
* useless because it's sole purpose it to have a way to
* update the file metadata, but here we are only reading
* a directory so there's no need for that kind of stuff.
* However, the rmdir_uncached function uses a ugly hack to
* avoid opening a directory twice when deleting it and thus
* needs those information. That's why we pass pdir->fatdir both
* as the parent directory and the resulting one (this is safe,
* in doubt, check fat_open(dir) code) which will allow this kind of
* (ugly) things */
if ( fat_opendir(IF_MV(volume,)
&pdir->fatdir,
entry.firstcluster,
&pdir->fatdir) < 0 ) {
DEBUGF("Failed opening dir '%s' (%ld)\n",
part, entry.firstcluster);
pdir->busy = false;
return NULL;
}
#ifdef HAVE_MULTIVOLUME
pdir->volumecounter = -1; /* n.a. to subdirs */
#endif
break;
}
}
}
return pdir;
}
int closedir_uncached(DIR_UNCACHED* dir)
{
dir->busy=false;
return 0;
}
struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir)
{
struct fat_direntry entry;
struct dirent_uncached* theent = &(dir->theent);
if (!dir->busy)
return NULL;
#ifdef HAVE_MULTIVOLUME
/* Volumes (secondary file systems) get inserted into the root directory
of the first volume, since we have no separate top level. */
if (dir->volumecounter >= 0 /* on a root dir */
&& dir->volumecounter < NUM_VOLUMES /* in range */
&& dir->fatdir.file.volume == 0) /* at volume 0 */
{ /* fake special directories, which don't really exist, but
will get redirected upon opendir_uncached() */
while (++dir->volumecounter < NUM_VOLUMES)
{
if (fat_ismounted(dir->volumecounter))
{
memset(theent, 0, sizeof(*theent));
theent->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
snprintf(theent->d_name, sizeof(theent->d_name),
VOL_NAMES, dir->volumecounter);
return theent;
}
}
}
#endif
/* normal directory entry fetching follows here */
if (fat_getnext(&(dir->fatdir),&entry) < 0)
return NULL;
if ( !entry.name[0] )
return NULL;
strlcpy(theent->d_name, entry.name, sizeof(theent->d_name));
theent->info.attribute = entry.attr;
theent->info.wrtdate = entry.wrtdate;
theent->info.wrttime = entry.wrttime;
theent->info.size = entry.filesize;
theent->startcluster = entry.firstcluster;
return theent;
}
int mkdir_uncached(const char *name)
{
DIR_UNCACHED *dir;
char namecopy[MAX_PATH];
char* end;
char *basename;
char *parent;
struct dirent_uncached *entry;
int dd;
DIR_UNCACHED* pdir = opendirs;
struct fat_dir *newdir;
int rc;
if ( name[0] != '/' ) {
DEBUGF("mkdir: Only absolute paths supported right now\n");
return -1;
}
/* find a free dir descriptor */
for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
if ( !pdir->busy )
break;
if ( dd == MAX_OPEN_DIRS ) {
DEBUGF("Too many dirs open\n");
errno = EMFILE;
return -5;
}
pdir->busy = true;
newdir = &pdir->fatdir;
strlcpy(namecopy, name, sizeof(namecopy));
/* Split the base name and the path */
end = strrchr(namecopy, '/');
*end = 0;
basename = end+1;
if(namecopy == end) /* Root dir? */
parent = "/";
else
parent = namecopy;
DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename);
dir = opendir_uncached(parent);
if(!dir) {
DEBUGF("mkdir: can't open parent dir\n");
pdir->busy = false;
return -2;
}
if(basename[0] == 0) {
DEBUGF("mkdir: Empty dir name\n");
pdir->busy = false;
errno = EINVAL;
return -3;
}
/* Now check if the name already exists */
while ((entry = readdir_uncached(dir))) {
if ( !strcasecmp(basename, entry->d_name) ) {
DEBUGF("mkdir error: file exists\n");
errno = EEXIST;
closedir_uncached(dir);
pdir->busy = false;
return - 4;
}
}
memset(newdir, 0, sizeof(struct fat_dir));
rc = fat_create_dir(basename, newdir, &(dir->fatdir));
closedir_uncached(dir);
pdir->busy = false;
return rc;
}
int rmdir_uncached(const char* name)
{
int rc;
DIR_UNCACHED* dir;
struct dirent_uncached* entry;
dir = opendir_uncached(name);
if (!dir)
{
errno = ENOENT; /* open error */
return -1;
}
/* check if the directory is empty */
while ((entry = readdir_uncached(dir)))
{
if (strcmp(entry->d_name, ".") &&
strcmp(entry->d_name, ".."))
{
DEBUGF("rmdir error: not empty\n");
errno = ENOTEMPTY;
closedir_uncached(dir);
return -2;
}
}
rc = fat_remove(&(dir->fatdir.file));
if ( rc < 0 ) {
DEBUGF("Failed removing dir: %d\n", rc);
errno = EIO;
rc = rc * 10 - 3;
}
closedir_uncached(dir);
return rc;
}

File diff suppressed because it is too large Load Diff

View File

@ -19,14 +19,25 @@
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "kernel.h"
#include "storage.h"
#include "debug.h"
#include "fat.h"
#include "dir.h" /* for release_dirs() */
#include "file.h" /* for release_files() */
#include "disk_cache.h"
#include "fileobj_mgr.h"
#include "dir.h"
#include "dircache_redirect.h"
#include "disk.h"
#include <string.h>
#ifndef CONFIG_DEFAULT_PARTNUM
#define CONFIG_DEFAULT_PARTNUM 0
#endif
#define disk_reader_lock() file_internal_lock_READER()
#define disk_reader_unlock() file_internal_unlock_READER()
#define disk_writer_lock() file_internal_lock_WRITER()
#define disk_writer_unlock() file_internal_unlock_WRITER()
/* Partition table entry layout:
-----------------------
@ -42,11 +53,18 @@
12-15: nr of sectors in partition
*/
#define BYTES2INT32(array,pos) \
((long)array[pos] | ((long)array[pos+1] << 8 ) | \
((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
#define BYTES2INT32(array, pos) \
(((uint32_t)array[pos+0] << 0) | \
((uint32_t)array[pos+1] << 8) | \
((uint32_t)array[pos+2] << 16) | \
((uint32_t)array[pos+3] << 24))
static const unsigned char fat_partition_types[] = {
#define BYTES2INT16(array, pos) \
(((uint32_t)array[pos+0] << 0) | \
((uint32_t)array[pos+1] << 8))
static const unsigned char fat_partition_types[] =
{
0x0b, 0x1b, /* FAT32 + hidden variant */
0x0c, 0x1c, /* FAT32 (LBA) + hidden variant */
#ifdef HAVE_FAT16SUPPORT
@ -56,113 +74,14 @@ static const unsigned char fat_partition_types[] = {
#endif
};
static struct partinfo part[NUM_DRIVES*4]; /* space for 4 partitions on 2 drives */
static int vol_drive[NUM_VOLUMES]; /* mounted to which drive (-1 if none) */
static struct mutex disk_mutex;
#ifdef MAX_LOG_SECTOR_SIZE
static int disk_sector_multiplier[NUM_DRIVES] = {[0 ... NUM_DRIVES-1] = 1};
int disk_get_sector_multiplier(IF_MD_NONVOID(int drive))
{
#ifdef HAVE_MULTIDRIVE
return disk_sector_multiplier[drive];
#else
return disk_sector_multiplier[0];
#endif
}
#endif
struct partinfo* disk_init(IF_MD_NONVOID(int drive))
{
int i;
#ifdef HAVE_MULTIDRIVE
/* For each drive, start at a different position, in order not to destroy
the first entry of drive 0.
That one is needed to calculate config sector position. */
struct partinfo* pinfo = &part[drive*4];
if ((size_t)drive >= sizeof(part)/sizeof(*part)/4)
return NULL; /* out of space in table */
#else
struct partinfo* pinfo = part;
const int drive = 0;
(void)drive;
#endif
unsigned char* sector = fat_get_sector_buffer();
storage_read_sectors(IF_MD(drive,) 0,1, sector);
/* check that the boot sector is initialized */
if ( (sector[510] != 0x55) ||
(sector[511] != 0xaa)) {
fat_release_sector_buffer();
DEBUGF("Bad boot sector signature\n");
return NULL;
}
/* parse partitions */
for ( i=0; i<4; i++ ) {
unsigned char* ptr = sector + 0x1be + 16*i;
pinfo[i].type = ptr[4];
pinfo[i].start = BYTES2INT32(ptr, 8);
pinfo[i].size = BYTES2INT32(ptr, 12);
DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n",
i,pinfo[i].type,pinfo[i].start,pinfo[i].size);
/* extended? */
if ( pinfo[i].type == 5 ) {
/* not handled yet */
}
}
fat_release_sector_buffer();
return pinfo;
}
struct partinfo* disk_partinfo(int partition)
{
return &part[partition];
}
void disk_init_subsystem(void)
{
mutex_init(&disk_mutex);
}
int disk_mount_all(void)
{
int mounted=0;
int i;
#ifdef HAVE_HOTSWAP
mutex_lock(&disk_mutex);
#endif
fat_init(); /* reset all mounted partitions */
for (i=0; i<NUM_VOLUMES; i++)
vol_drive[i] = -1; /* mark all as unassigned */
#ifndef HAVE_MULTIDRIVE
mounted = disk_mount(0);
#else
for(i=0;i<NUM_DRIVES;i++)
{
#ifdef HAVE_HOTSWAP
if (storage_present(i))
#endif
mounted += disk_mount(i);
}
#endif
#ifdef HAVE_HOTSWAP
mutex_unlock(&disk_mutex);
#endif
return mounted;
}
/* space for 4 partitions on 2 drives */
static struct partinfo part[NUM_DRIVES*4];
/* mounted to which drive (-1 if none) */
static int vol_drive[NUM_VOLUMES];
static int get_free_volume(void)
{
int i;
for (i=0; i<NUM_VOLUMES; i++)
for (int i = 0; i < NUM_VOLUMES; i++)
{
if (vol_drive[i] == -1) /* unassigned? */
return i;
@ -171,44 +90,119 @@ static int get_free_volume(void)
return -1; /* none found */
}
#ifdef MAX_LOG_SECTOR_SIZE
static int disk_sector_multiplier[NUM_DRIVES] =
{ [0 ... NUM_DRIVES-1] = 1 };
int disk_get_sector_multiplier(IF_MD_NONVOID(int drive))
{
if (!CHECK_DRV(drive))
return 0;
disk_reader_lock();
int multiplier = disk_sector_multiplier[IF_MD_DRV(drive)];
disk_reader_unlock();
return multiplier;
}
#endif /* MAX_LOG_SECTOR_SIZE */
bool disk_init(IF_MD_NONVOID(int drive))
{
if (!CHECK_DRV(drive))
return false; /* out of space in table */
unsigned char *sector = dc_get_buffer();
if (!sector)
return false;
memset(sector, 0, SECTOR_SIZE);
storage_read_sectors(IF_MD(drive,) 0, 1, sector);
bool init = false;
/* check that the boot sector is initialized */
if (BYTES2INT16(sector, 510) == 0xaa55)
{
/* For each drive, start at a different position, in order not to
destroy the first entry of drive 0. That one is needed to calculate
config sector position. */
struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4];
disk_writer_lock();
/* parse partitions */
for (int i = 0; i < 4; i++)
{
unsigned char* ptr = sector + 0x1be + 16*i;
pinfo[i].type = ptr[4];
pinfo[i].start = BYTES2INT32(ptr, 8);
pinfo[i].size = BYTES2INT32(ptr, 12);
DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n",
i,pinfo[i].type,pinfo[i].start,pinfo[i].size);
/* extended? */
if ( pinfo[i].type == 5 )
{
/* not handled yet */
}
}
disk_writer_unlock();
init = true;
}
else
{
DEBUGF("Bad boot sector signature\n");
}
dc_release_buffer(sector);
return init;
}
bool disk_partinfo(int partition, struct partinfo *info)
{
if (partition < 0 || partition >= (int)ARRAYLEN(part) || !info)
return false;
disk_reader_lock();
*info = part[partition];
disk_reader_unlock();
return true;
}
int disk_mount(int drive)
{
int mounted = 0; /* reset partition-on-drive flag */
int volume;
struct partinfo* pinfo;
#ifdef HAVE_HOTSWAP
mutex_lock(&disk_mutex);
#endif
disk_writer_lock();
volume = get_free_volume();
pinfo = disk_init(IF_MD(drive));
#ifdef MAX_LOG_SECTOR_SIZE
disk_sector_multiplier[drive] = 1;
#endif
int volume = get_free_volume();
if (pinfo == NULL)
if (!disk_init(IF_MD(drive)))
{
#ifdef HAVE_HOTSWAP
mutex_unlock(&disk_mutex);
#endif
disk_writer_unlock();
return 0;
}
#if defined(TOSHIBA_GIGABEAT_S)
int i = 1; /* For the Gigabeat S, we mount the second partition */
#else
int i = 0;
struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4];
#ifdef MAX_LOG_SECTOR_SIZE
disk_sector_multiplier[IF_MD_DRV(drive)] = 1;
#endif
for (; volume != -1 && i<4 && mounted<NUM_VOLUMES_PER_DRIVE; i++)
for (int i = CONFIG_DEFAULT_PARTNUM;
volume != -1 && i < 4 && mounted < NUM_VOLUMES_PER_DRIVE;
i++)
{
if (memchr(fat_partition_types, pinfo[i].type,
sizeof(fat_partition_types)) == NULL)
continue; /* not an accepted partition type */
#ifdef MAX_LOG_SECTOR_SIZE
int j;
for (j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
bool success = false;
#ifdef MAX_LOG_SECTOR_SIZE
for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1)
{
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start * j))
{
@ -218,93 +212,242 @@ int disk_mount(int drive)
vol_drive[volume] = drive; /* remember the drive for this volume */
volume = get_free_volume(); /* prepare next entry */
disk_sector_multiplier[drive] = j;
success = true;
break;
}
}
#else
#else /* ndef MAX_LOG_SECTOR_SIZE */
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start))
{
mounted++;
vol_drive[volume] = drive; /* remember the drive for this volume */
volume = get_free_volume(); /* prepare next entry */
success = true;
}
#endif
#endif /* MAX_LOG_SECTOR_SIZE */
if (success)
volume_onmount_internal(IF_MV(volume));
}
if (mounted == 0 && volume != -1) /* none of the 4 entries worked? */
{ /* try "superfloppy" mode */
DEBUGF("No partition found, trying to mount sector 0.\n");
if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0))
{
#ifdef MAX_LOG_SECTOR_SIZE
disk_sector_multiplier[drive] = fat_get_bytes_per_sector(IF_MV(volume))/SECTOR_SIZE;
#endif
#ifdef MAX_LOG_SECTOR_SIZE
disk_sector_multiplier[drive] =
fat_get_bytes_per_sector(IF_MV(volume)) / SECTOR_SIZE;
#endif
mounted = 1;
vol_drive[volume] = drive; /* remember the drive for this volume */
volume_onmount_internal(IF_MV(volume));
}
}
#ifdef HAVE_HOTSWAP
mutex_unlock(&disk_mutex);
#endif
disk_writer_unlock();
return mounted;
}
int disk_mount_all(void)
{
int mounted = 0;
disk_writer_lock();
/* reset all mounted partitions */
volume_onunmount_internal(IF_MV(-1));
fat_init();
for (int i = 0; i < NUM_VOLUMES; i++)
vol_drive[i] = -1; /* mark all as unassigned */
for (int i = 0; i < NUM_DRIVES; i++)
{
#ifdef HAVE_HOTSWAP
if (storage_present(i))
#endif
mounted += disk_mount(i);
}
disk_writer_unlock();
return mounted;
}
int disk_unmount(int drive)
{
if (!CHECK_DRV(drive))
return 0;
int unmounted = 0;
int i;
#ifdef HAVE_HOTSWAP
mutex_lock(&disk_mutex);
#endif
for (i=0; i<NUM_VOLUMES; i++)
disk_writer_lock();
for (int i = 0; i < NUM_VOLUMES; i++)
{
if (vol_drive[i] == drive)
{ /* force releasing resources */
vol_drive[i] = -1; /* mark unused */
volume_onunmount_internal(IF_MV(i));
fat_unmount(IF_MV(i));
unmounted++;
release_files(i);
release_dirs(i);
fat_unmount(i, false);
}
}
#ifdef HAVE_HOTSWAP
mutex_unlock(&disk_mutex);
#endif
disk_writer_unlock();
return unmounted;
}
int disk_unmount_all(void)
{
#ifndef HAVE_MULTIDRIVE
return disk_unmount(0);
#else /* HAVE_MULTIDRIVE */
int unmounted = 0;
int i;
for (i = 0; i < NUM_DRIVES; i++)
disk_writer_lock();
volume_onunmount_internal(IF_MV(-1));
for (int i = 0; i < NUM_DRIVES; i++)
{
#ifdef HAVE_HOTSWAP
#ifdef HAVE_HOTSWAP
if (storage_present(i))
#endif
#endif
unmounted += disk_unmount(i);
}
disk_writer_unlock();
return unmounted;
#endif /* HAVE_MULTIDRIVE */
}
bool disk_present(IF_MD_NONVOID(int drive))
{
int rc = -1;
if (CHECK_DRV(drive))
{
void *sector = dc_get_buffer();
if (sector)
{
rc = storage_read_sectors(IF_MD(drive,) 0, 1, sector);
dc_release_buffer(sector);
}
}
return rc == 0;
}
/** Volume-centric functions **/
void volume_recalc_free(IF_MV_NONVOID(int volume))
{
if (!CHECK_VOL(volume))
return;
/* FIXME: this is crummy but the only way to ensure a correct freecount
if other threads are writing and changing the fsinfo; it is possible
to get multiple threads calling here and also writing and get correct
freespace counts, however a bit complicated to do; if thou desireth I
shall implement the concurrent version -- jethead71 */
disk_writer_lock();
fat_recalc_free(IF_MV(volume));
disk_writer_unlock();
}
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume))
{
if (!CHECK_VOL(volume))
return 0;
disk_reader_lock();
unsigned int clustersize = fat_get_cluster_size(IF_MV(volume));
disk_reader_unlock();
return clustersize;
}
void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
{
disk_reader_lock();
if (!CHECK_VOL(volume) || !fat_size(IF_MV(volume,) sizep, freep))
{
if (freep) *sizep = 0;
if (freep) *freep = 0;
}
disk_reader_unlock();
}
#if defined (HAVE_HOTSWAP) || defined (HAVE_MULTIDRIVE) \
|| defined (HAVE_DIRCACHE)
enum volume_info_type
{
#ifdef HAVE_HOTSWAP
VP_REMOVABLE,
VP_PRESENT,
#endif
#if defined (HAVE_MULTIDRIVE) || defined (HAVE_DIRCACHE)
VP_DRIVE,
#endif
};
static int volume_properties(int volume, enum volume_info_type infotype)
{
int res = -1;
disk_reader_lock();
if (CHECK_VOL(volume))
{
int vd = vol_drive[volume];
switch (infotype)
{
#ifdef HAVE_HOTSWAP
case VP_REMOVABLE:
res = storage_removable(vd) ? 1 : 0;
break;
case VP_PRESENT:
res = storage_present(vd) ? 1 : 0;
break;
#endif
#if defined(HAVE_MULTIDRIVE) || defined(HAVE_DIRCACHE)
case VP_DRIVE:
res = vd;
break;
#endif
}
}
disk_reader_unlock();
return res;
}
#ifdef HAVE_HOTSWAP
bool volume_removable(int volume)
{
if(vol_drive[volume] == -1)
return false;
return storage_removable(vol_drive[volume]);
return volume_properties(volume, VP_REMOVABLE) > 0;
}
bool volume_present(int volume)
{
if(vol_drive[volume] == -1)
return false;
return storage_present(vol_drive[volume]);
return volume_properties(volume, VP_PRESENT) > 0;
}
#endif
#endif /* HAVE_HOTSWAP */
#ifdef HAVE_MULTIDRIVE
int volume_drive(int volume)
{
return volume_properties(volume, VP_DRIVE);
}
#endif /* HAVE_MULTIDRIVE */
#ifdef HAVE_DIRCACHE
bool volume_ismounted(IF_MV_NONVOID(int volume))
{
return volume_properties(IF_MV_VOL(volume), VP_DRIVE) >= 0;
}
#endif /* HAVE_DIRCACHE */
#endif /* HAVE_HOTSWAP || HAVE_MULTIDRIVE || HAVE_DIRCACHE */

View File

@ -0,0 +1,343 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "debug.h"
#include "system.h"
#include "linked_list.h"
#include "disk_cache.h"
#include "fat.h" /* for SECTOR_SIZE */
#include "bitarray.h"
/* Cache: LRU cache with separately-chained hashtable
*
* Each entry of the map is the mapped location of the hashed sector value
* where each bit in each map entry indicates which corresponding cache
* entries are occupied by sector values that collide in that map entry.
*
* Each volume is given its own bit map.
*
* To probe for a specific key, each bit in the map entry must be examined,
* its position used as an index into the cache_entry array and the actual
* sector information compared for that cache entry. If the search exhausts
* all bits, the sector is not cached.
*
* To avoid long chains, the map entry count should be much greater than the
* number of cache entries. Since the cache is an LRU design, no buffer entry
* in the array is intrinsically associated with any particular sector number
* or volume.
*
* Example 6-sector cache with 8-entry map:
* cache entry 543210
* cache map 100000 <- sector number hashes into map
* 000000
* 000100
* 000000
* 010000
* 000000
* 001001 <- collision
* 000000
* volume map 111101 <- entry usage by the volume (OR of all map entries)
*/
enum dce_flags /* flags for each cache entry */
{
DCE_INUSE = 0x01, /* entry in use and valid */
DCE_DIRTY = 0x02, /* entry is dirty in need of writeback */
DCE_BUF = 0x04, /* entry is being used as a general buffer */
};
struct disk_cache_entry
{
struct lldc_node node; /* LRU list links */
unsigned char flags; /* entry flags */
#ifdef HAVE_MULTIVOLUME
unsigned char volume; /* volume of sector */
#endif
unsigned long sector; /* cached disk sector number */
};
BITARRAY_TYPE_DECLARE(cache_map_entry_t, cache_map, DC_NUM_ENTRIES)
static inline unsigned int map_sector(unsigned long sector)
{
/* keep sector hash simple for now */
return sector % DC_MAP_NUM_ENTRIES;
}
static struct lldc_head cache_lru; /* LRU cache list (head = LRU item) */
static struct disk_cache_entry cache_entry[DC_NUM_ENTRIES];
static cache_map_entry_t cache_map_entry[NUM_VOLUMES][DC_MAP_NUM_ENTRIES];
static cache_map_entry_t cache_vol_map[NUM_VOLUMES] IBSS_ATTR;
static uint8_t cache_buffer[DC_NUM_ENTRIES][DC_CACHE_BUFSIZE] CACHEALIGN_ATTR;
struct mutex disk_cache_mutex SHAREDBSS_ATTR;
#define CACHE_MAP_ENTRY(volume, mapnum) \
cache_map_entry[IF_MV_VOL(volume)][mapnum]
#define CACHE_VOL_MAP(volume) \
cache_vol_map[IF_MV_VOL(volume)]
#define DCE_LRU() ((struct disk_cache_entry *)cache_lru.head)
#define DCE_NEXT(fce) ((struct disk_cache_entry *)(fce)->node.next)
#define NODE_DCE(node) ((struct disk_cache_entry *)(node))
/* get the cache index from a pointer to a buffer */
#define DCIDX_FROM_BUF(buf) \
((uint8_t (*)[DC_CACHE_BUFSIZE])(buf) - cache_buffer)
#define DCIDX_FROM_DCE(dce) \
((dce) - cache_entry)
/* set the in-use bit in the map */
static inline void cache_bitmap_set_bit(int volume, unsigned int mapnum,
unsigned int bitnum)
{
cache_map_set_bit(&CACHE_MAP_ENTRY(volume, mapnum), bitnum);
cache_map_set_bit(&CACHE_VOL_MAP(volume), bitnum);
(void)volume;
}
/* clear the in-use bit in the map */
static inline void cache_bitmap_clear_bit(int volume, unsigned int mapnum,
unsigned int bitnum)
{
cache_map_clear_bit(&CACHE_MAP_ENTRY(volume, mapnum), bitnum);
cache_map_clear_bit(&CACHE_VOL_MAP(volume), bitnum);
(void)volume;
}
/* make entry MRU by moving it to the list tail */
static inline void touch_cache_entry(struct disk_cache_entry *which)
{
struct lldc_node *lru = cache_lru.head;
struct lldc_node *node = &which->node;
if (node == lru->prev) /* already MRU */
; /**/
else if (node == lru) /* is the LRU? just rotate list */
cache_lru.head = lru->next;
else /* somewhere else; move it */
{
lldc_remove(&cache_lru, node);
lldc_insert_last(&cache_lru, node);
}
}
/* remove LRU entry from the cache list to use as a buffer */
static struct disk_cache_entry * cache_remove_lru_entry(void)
{
struct lldc_node *lru = cache_lru.head;
/* at least one is reserved for client */
if (lru == lru->next)
return NULL;
/* remove it; next-LRU becomes the LRU */
lldc_remove(&cache_lru, lru);
return NODE_DCE(lru);
}
/* return entry to the cache list and set it LRU */
static void cache_return_lru_entry(struct disk_cache_entry *fce)
{
lldc_insert_first(&cache_lru, &fce->node);
}
/* discard the entry's data and mark it unused */
static inline void cache_discard_entry(struct disk_cache_entry *dce,
unsigned int index)
{
cache_bitmap_clear_bit(IF_MV_VOL(dce->volume), map_sector(dce->sector),
index);
dce->flags = 0;
}
/* search the cache for the specified sector, returning a buffer, either
to the specified sector, if it exists, or a new/evicted entry that must
be filled */
void * dc_cache_probe(IF_MV(int volume,) unsigned long sector,
unsigned int *flagsp)
{
unsigned int mapnum = map_sector(sector);
FOR_EACH_BITARRAY_SET_BIT(&CACHE_MAP_ENTRY(volume, mapnum), index)
{
struct disk_cache_entry *dce = &cache_entry[index];
if (dce->sector == sector)
{
*flagsp = DCE_INUSE;
touch_cache_entry(dce);
return cache_buffer[index];
}
}
/* sector not found so the LRU is the victim */
struct disk_cache_entry *dce = DCE_LRU();
cache_lru.head = dce->node.next;
unsigned int index = DCIDX_FROM_DCE(dce);
void *buf = cache_buffer[index];
unsigned int old_flags = dce->flags;
if (old_flags)
{
int old_volume = IF_MV_VOL(dce->volume);
unsigned long sector = dce->sector;
unsigned int old_mapnum = map_sector(sector);
if (old_flags & DCE_DIRTY)
dc_writeback_callback(IF_MV(old_volume,) sector, buf);
if (mapnum == old_mapnum IF_MV( && volume == old_volume ))
goto finish_setup;
cache_bitmap_clear_bit(old_volume, old_mapnum, index);
}
cache_bitmap_set_bit(IF_MV_VOL(volume), mapnum, index);
finish_setup:
dce->flags = DCE_INUSE;
#ifdef HAVE_MULTIVOLUME
dce->volume = volume;
#endif
dce->sector = sector;
*flagsp = 0;
return buf;
}
/* mark in-use cache entry as dirty by buffer */
void dc_dirty_buf(void *buf)
{
unsigned int index = DCIDX_FROM_BUF(buf);
if (index >= DC_NUM_ENTRIES)
return;
/* dirt remains, sticky until flushed */
struct disk_cache_entry *fce = &cache_entry[index];
if (fce->flags & DCE_INUSE)
fce->flags |= DCE_DIRTY;
}
/* discard in-use cache entry by buffer */
void dc_discard_buf(void *buf)
{
unsigned int index = DCIDX_FROM_BUF(buf);
if (index >= DC_NUM_ENTRIES)
return;
struct disk_cache_entry *dce = &cache_entry[index];
if (dce->flags & DCE_INUSE)
cache_discard_entry(dce, index);
}
/* commit all dirty cache entries to storage for a specified volume */
void dc_commit_all(IF_MV_NONVOID(int volume))
{
DEBUGF("dc_commit_all()\n");
FOR_EACH_BITARRAY_SET_BIT(&CACHE_VOL_MAP(volume), index)
{
struct disk_cache_entry *dce = &cache_entry[index];
unsigned int flags = dce->flags;
if (flags & DCE_DIRTY)
{
dc_writeback_callback(IF_MV(volume,) dce->sector,
cache_buffer[index]);
dce->flags = flags & ~DCE_DIRTY;
}
}
}
/* discard all cache entries from the specified volume */
void dc_discard_all(IF_MV_NONVOID(int volume))
{
DEBUGF("dc_discard_all()\n");
FOR_EACH_BITARRAY_SET_BIT(&CACHE_VOL_MAP(volume), index)
cache_discard_entry(&cache_entry[index], index);
}
/* expropriate a buffer from the cache */
void * dc_get_buffer(void)
{
dc_lock_cache();
void *buf = NULL;
struct disk_cache_entry *dce = cache_remove_lru_entry();
if (dce)
{
unsigned int index = DCIDX_FROM_DCE(dce);
unsigned int flags = dce->flags;
buf = cache_buffer[index];
if (flags)
{
/* must first commit this sector if dirty */
if (flags & DCE_DIRTY)
dc_writeback_callback(IF_MV(dce->volume,) dce->sector, buf);
cache_discard_entry(dce, index);
}
dce->flags = DCE_BUF;
}
/* cache is out of buffers */
dc_unlock_cache();
return buf;
}
/* return buffer to the cache by buffer */
void dc_release_buffer(void *buf)
{
unsigned int index = DCIDX_FROM_BUF(buf);
if (index >= DC_NUM_ENTRIES)
return;
dc_lock_cache();
struct disk_cache_entry *dce = &cache_entry[index];
if (dce->flags & DCE_BUF)
{
dce->flags = 0;
cache_return_lru_entry(dce);
}
dc_unlock_cache();
}
/* one-time init at startup */
void dc_init(void)
{
mutex_init(&disk_cache_mutex);
lldc_init(&cache_lru);
for (unsigned int i = 0; i < DC_NUM_ENTRIES; i++)
lldc_insert_last(&cache_lru, &cache_entry[i].node);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,776 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include <errno.h>
#include "system.h"
#include "debug.h"
#include "panic.h"
#include "pathfuncs.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
#include "dir.h"
#include "dircache_redirect.h"
#include "dircache.h"
#include "string-extra.h"
#include "rbunicode.h"
/** Internal common filesystem service functions **/
/* for internal functions' scanning use to save quite a bit of stack space -
access must be serialized by the writer lock */
#if defined(CPU_SH) || defined(IAUDIO_M5)
/* otherwise, out of IRAM */
struct fat_direntry dir_fatent;
#else
struct fat_direntry dir_fatent IBSS_ATTR;
#endif
struct mrsw_lock file_internal_mrsw SHAREDBSS_ATTR;
/** File stream sector caching **/
/* initialize a new cache structure */
void file_cache_init(struct filestr_cache *cachep)
{
cachep->buffer = NULL;
cachep->sector = INVALID_SECNUM;
cachep->flags = 0;
}
/* discard and mark the cache buffer as unused */
void file_cache_reset(struct filestr_cache *cachep)
{
cachep->sector = INVALID_SECNUM;
cachep->flags = 0;
}
/* allocate resources attached to the cache */
void file_cache_alloc(struct filestr_cache *cachep)
{
/* if this fails, it is a bug; check for leaks and that the cache has
enough buffers for the worst case */
if (!cachep->buffer && !(cachep->buffer = dc_get_buffer()))
panicf("file_cache_alloc - OOM");
}
/* free resources attached to the cache */
void file_cache_free(struct filestr_cache *cachep)
{
if (cachep && cachep->buffer)
{
dc_release_buffer(cachep->buffer);
cachep->buffer = NULL;
}
file_cache_reset(cachep);
}
/** Stream base APIs **/
static inline void filestr_clear(struct filestr_base *stream)
{
stream->flags = 0;
stream->bindp = NULL;
#if 0
stream->mtx = NULL;
#endif
}
/* actually late-allocate the assigned cache */
void filestr_alloc_cache(struct filestr_base *stream)
{
file_cache_alloc(stream->cachep);
}
/* free the stream's cache buffer if it's its own */
void filestr_free_cache(struct filestr_base *stream)
{
if (stream->cachep == &stream->cache)
file_cache_free(stream->cachep);
}
/* assign a cache to the stream */
void filestr_assign_cache(struct filestr_base *stream,
struct filestr_cache *cachep)
{
if (cachep)
{
filestr_free_cache(stream);
stream->cachep = cachep;
}
else /* assign own cache */
{
file_cache_reset(&stream->cache);
stream->cachep = &stream->cache;
}
}
/* duplicate a cache into a stream's local cache */
void filestr_copy_cache(struct filestr_base *stream,
struct filestr_cache *cachep)
{
stream->cachep = &stream->cache;
stream->cache.sector = cachep->sector;
stream->cache.flags = cachep->flags;
if (cachep->buffer)
{
file_cache_alloc(&stream->cache);
memcpy(stream->cache.buffer, cachep->buffer, DC_CACHE_BUFSIZE);
}
else
{
file_cache_free(&stream->cache);
}
}
/* discard cache contents and invalidate it */
void filestr_discard_cache(struct filestr_base *stream)
{
file_cache_reset(stream->cachep);
}
/* Initialize the base descriptor */
void filestr_base_init(struct filestr_base *stream)
{
filestr_clear(stream);
file_cache_init(&stream->cache);
stream->cachep = &stream->cache;
}
/* free base descriptor resources */
void filestr_base_destroy(struct filestr_base *stream)
{
filestr_clear(stream);
filestr_free_cache(stream);
}
/** Internal directory service functions **/
/* read the next directory entry and return its FS info */
int uncached_readdir_internal(struct filestr_base *stream,
struct file_base_info *infop,
struct fat_direntry *fatent)
{
return fat_readdir(&stream->fatstr, &infop->fatfile.e,
filestr_get_cache(stream), fatent);
}
/* rewind the FS directory to the beginning */
void uncached_rewinddir_internal(struct file_base_info *infop)
{
fat_rewinddir(&infop->fatfile.e);
}
/* check if the directory is empty (ie. only "." and/or ".." entries
exist at most) */
int test_dir_empty_internal(struct filestr_base *stream)
{
int rc;
struct file_base_info info;
fat_rewind(&stream->fatstr);
rewinddir_internal(&info);
while ((rc = readdir_internal(stream, &info, &dir_fatent)) > 0)
{
/* no OEM decoding is recessary for this simple check */
if (!is_dotdir_name(dir_fatent.name))
{
DEBUGF("Directory not empty\n");
FILE_ERROR_RETURN(ENOTEMPTY, -1);
}
}
if (rc < 0)
{
DEBUGF("I/O error checking directory: %d\n", rc);
FILE_ERROR_RETURN(EIO, rc * 10 - 2);
}
return 0;
}
/* iso decode the name to UTF-8 */
void iso_decode_d_name(char *d_name)
{
if (is_dotdir_name(d_name))
return;
char shortname[13];
size_t len = strlcpy(shortname, d_name, sizeof (shortname));
/* This MUST be the default codepage thus not something that could be
loaded on call */
iso_decode(shortname, d_name, -1, len + 1);
}
#ifdef HAVE_DIRCACHE
/* nullify all the fields of the struct dirent */
void empty_dirent(struct dirent *entry)
{
entry->d_name[0] = '\0';
entry->info.attr = 0;
entry->info.size = 0;
entry->info.wrtdate = 0;
entry->info.wrttime = 0;
}
/* fill the native dirinfo from the static dir_fatent */
void fill_dirinfo_native(struct dirinfo_native *dinp)
{
struct fat_direntry *fatent = get_dir_fatent();
dinp->attr = fatent->attr;
dinp->size = fatent->filesize;
dinp->wrtdate = fatent->wrtdate;
dinp->wrttime = fatent->wrttime;
}
#endif /* HAVE_DIRCACHE */
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
struct dirent *entry)
{
struct fat_direntry fatent;
int rc = fat_readdir(&stream->fatstr, &scanp->fatscan,
filestr_get_cache(stream), &fatent);
/* FAT driver clears the struct fat_dirent if nothing is returned */
strcpy(entry->d_name, fatent.name);
entry->info.attr = fatent.attr;
entry->info.size = fatent.filesize;
entry->info.wrtdate = fatent.wrtdate;
entry->info.wrttime = fatent.wrttime;
return rc;
}
/* rewind the FS directory pointer */
void uncached_rewinddir_dirent(struct dirscan_info *scanp)
{
fat_rewinddir(&scanp->fatscan);
}
/** open_stream_internal() helpers and types **/
struct pathwalk
{
const char *path; /* current location in input path */
unsigned int callflags; /* callflags parameter */
struct path_component_info *compinfo; /* compinfo parameter */
file_size_t filesize; /* size of the file */
};
struct pathwalk_component
{
struct file_base_info info; /* basic file information */
const char *name; /* component name location in path */
uint16_t length; /* length of name of component */
uint16_t attr; /* attributes of this component */
struct pathwalk_component *nextp; /* parent if in use else next free */
};
#define WALK_RC_NOT_FOUND 0 /* successfully not found */
#define WALK_RC_FOUND 1 /* found and opened */
#define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */
#define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */
/* return another struct pathwalk_component from the pool, or NULL if the
pool is completely used */
static void * pathwalk_comp_alloc_(struct pathwalk_component *parentp)
{
/* static pool that goes to a depth of STATIC_COMP_NUM before allocating
elements from the stack */
static struct pathwalk_component aux_pathwalk[STATIC_PATHCOMP_NUM];
struct pathwalk_component *compp = NULL;
if (!parentp)
compp = &aux_pathwalk[0]; /* root */
else if (PTR_IN_ARRAY(aux_pathwalk, parentp, STATIC_PATHCOMP_NUM-1))
compp = parentp + 1;
return compp;
}
/* allocates components from the pool or stack depending upon the depth */
#define pathwalk_comp_alloc(parentp) \
({ \
void *__c = pathwalk_comp_alloc_(parentp); \
if (!__builtin_constant_p(parentp) && !__c) \
__c = alloca(sizeof (struct pathwalk_component)); \
(struct pathwalk_component *)__c; \
})
/* fill in the details of the struct path_component_info for caller */
static int fill_path_compinfo(struct pathwalk *walkp,
struct pathwalk_component *compp,
int rc)
{
if (rc == -ENOENT)
{
/* this component wasn't found; see if more of them exist or path
has trailing separators; if it does, this component should be
interpreted as a directory even if it doesn't exist and it's the
final one; also, this has to be the last part or it's an error*/
const char *p = GOBBLE_PATH_SEPCH(walkp->path);
if (!*p)
{
if (p > walkp->path)
compp->attr |= ATTR_DIRECTORY;
rc = WALK_RC_NOT_FOUND; /* successfully not found */
}
}
if (rc >= 0)
{
struct path_component_info *compinfo = walkp->compinfo;
compinfo->name = compp->name;
compinfo->length = compp->length;
compinfo->attr = compp->attr;
compinfo->filesize = walkp->filesize;
compinfo->parentinfo = (compp->nextp ?: compp)->info;
}
return rc;
}
/* open the final stream itself, if found */
static int walk_open_info(struct pathwalk *walkp,
struct pathwalk_component *compp,
int rc,
struct filestr_base *stream)
{
/* this may make adjustments to things; do it first */
if (walkp->compinfo)
rc = fill_path_compinfo(walkp, compp, rc);
if (rc < 0 || rc == WALK_RC_NOT_FOUND)
return rc;
unsigned int callflags = walkp->callflags;
bool isdir = compp->attr & ATTR_DIRECTORY;
/* type must match what is called for */
switch (callflags & FF_TYPEMASK)
{
case FF_FILE:
if (!isdir) break;
DEBUGF("File is a directory\n");
return -EISDIR;
case FF_DIR:
if (isdir) break;
DEBUGF("File is not a directory\n");
return -ENOTDIR;
/* FF_ANYTYPE: basically, ignore FF_FILE/FF_DIR */
}
/* FO_DIRECTORY must match type */
if (isdir)
callflags |= FO_DIRECTORY;
else
callflags &= ~FO_DIRECTORY;
fileop_onopen_internal(stream, &compp->info, callflags);
return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT;
}
/* check the component against the prefix test info */
static void walk_check_prefix(struct pathwalk *walkp,
struct pathwalk_component *compp)
{
if (compp->attr & ATTR_PREFIX)
return;
if (!fat_file_is_same(&compp->info.fatfile,
&walkp->compinfo->prefixp->fatfile))
return;
compp->attr |= ATTR_PREFIX;
}
/* opens the component named by 'comp' in the directory 'parent' */
static NO_INLINE int open_path_component(struct pathwalk *walkp,
struct pathwalk_component *compp,
struct filestr_base *stream)
{
int rc;
/* create a null-terminated copy of the component name */
char *compname = strmemdupa(compp->name, compp->length);
unsigned int callflags = walkp->callflags;
struct pathwalk_component *parentp = compp->nextp;
/* children inherit the prefix coloring from the parent */
compp->attr = parentp->attr & ATTR_PREFIX;
/* most of the next would be abstracted elsewhere if doing other
filesystems */
/* scan parent for name; stream is converted to this parent */
file_cache_reset(stream->cachep);
stream->infop = &parentp->info;
fat_filestr_init(&stream->fatstr, &parentp->info.fatfile);
rewinddir_internal(&compp->info);
while ((rc = readdir_internal(stream, &compp->info, &dir_fatent)) > 0)
{
if (rc > 1 && !(callflags & FF_NOISO))
iso_decode_d_name(dir_fatent.name);
if (!strcasecmp(compname, dir_fatent.name))
break;
}
if (rc == 0)
{
DEBUGF("File/directory not found\n");
return -ENOENT;
}
else if (rc < 0)
{
DEBUGF("I/O error reading directory %d\n", rc);
return -EIO;
}
rc = fat_open(stream->fatstr.fatfilep, dir_fatent.firstcluster,
&compp->info.fatfile);
if (rc < 0)
{
DEBUGF("I/O error opening file/directory %s (%d)\n",
compname, rc);
return -EIO;
}
walkp->filesize = dir_fatent.filesize;
compp->attr |= dir_fatent.attr;
if (callflags & FF_CHECKPREFIX)
walk_check_prefix(walkp, compp);
return WALK_RC_FOUND;
}
/* parse a path component, open it and process the next */
static NO_INLINE int
walk_path(struct pathwalk *walkp, struct pathwalk_component *compp,
struct filestr_base *stream)
{
int rc = WALK_RC_FOUND;
if (walkp->callflags & FF_CHECKPREFIX)
walk_check_prefix(walkp, compp);
/* alloca is used in a loop, but we reuse any blocks previously allocated
if we went up then back down; if the path takes us back to the root, then
everything is cleaned automatically */
struct pathwalk_component *freep = NULL;
const char *name;
ssize_t len;
while ((len = parse_path_component(&walkp->path, &name)))
{
/* whatever is to be a parent must be a directory */
if (!(compp->attr & ATTR_DIRECTORY))
return -ENOTDIR;
switch (len)
{
case 1:
case 2:
/* check for "." and ".." */
if (name[0] == '.')
{
if (len == 2 && name[1] == '.')
{
struct pathwalk_component *parentp = compp->nextp;
if (!parentp)
return WALK_RC_CONT_AT_ROOT;
compp->nextp = freep;
freep = compp;
compp = parentp;
}
break;
}
/* fallthrough */
default:
if (len >= MAX_NAME)
return -ENAMETOOLONG;
struct pathwalk_component *newp = freep;
if (!newp)
newp = pathwalk_comp_alloc(compp);
else
freep = freep->nextp;
newp->nextp = compp;
compp = newp;
compp->name = name;
compp->length = len;
rc = open_path_component(walkp, compp, stream);
if (rc < 0)
break;
}
}
return walk_open_info(walkp, compp, rc, stream);
}
/* open a stream given a path to the resource */
int open_stream_internal(const char *path, unsigned int callflags,
struct filestr_base *stream,
struct path_component_info *compinfo)
{
DEBUGF("%s(path=\"%s\",flg=%X,str=%p,compinfo=%p)\n", path, callflags,
stream, compinfo);
int rc;
filestr_base_init(stream);
if (!path_is_absolute(path))
{
/* while this supports relative components, there is currently no
current working directory concept at this level by which to
fully qualify the path (though that would not be excessively
difficult to add) */
DEBUGF("\"%s\" is not an absolute path\n"
"Only absolute paths currently supported.\n", path);
FILE_ERROR(path ? ENOENT : EFAULT, -1);
}
/* if !compinfo, then the result of this check is not visible anyway */
if (!compinfo)
callflags &= ~FF_CHECKPREFIX;
struct pathwalk walk;
walk.path = path;
walk.callflags = callflags;
walk.compinfo = compinfo;
walk.filesize = 0;
struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL);
rootp->nextp = NULL;
rootp->attr = ATTR_DIRECTORY;
#ifdef HAVE_MULTIVOLUME
int volume = 0, rootrc = WALK_RC_FOUND;
#endif /* HAVE_MULTIVOLUME */
while (1)
{
const char *pathptr = walk.path;
#ifdef HAVE_MULTIVOLUME
/* this seamlessly integrates secondary filesystems into the
root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */
const char *p;
volume = path_strip_volume(pathptr, &p, false);
if (!CHECK_VOL(volume))
{
DEBUGF("No such device or address: %d\n", volume);
FILE_ERROR(ENXIO, -2);
}
/* the root of this subpath is the system root? */
rootrc = p == pathptr ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND;
walk.path = p;
#endif /* HAVE_MULTIVOLUME */
/* set name to start at last leading separator; names of volume
specifiers will be returned as "/<fooN>" */
rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1;
rootp->length =
IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1;
rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile);
if (rc < 0)
{
/* not mounted */
DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume));
rc = -ENXIO;
break;
}
get_rootinfo_internal(&rootp->info);
rc = walk_path(&walk, rootp, stream);
if (rc != WALK_RC_CONT_AT_ROOT)
break;
}
switch (rc)
{
case WALK_RC_FOUND_ROOT:
IF_MV( rc = rootrc; )
case WALK_RC_NOT_FOUND:
case WALK_RC_FOUND:
break;
default: /* utter, abject failure :`( */
DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno);
filestr_base_destroy(stream);
FILE_ERROR(-rc, -2);
}
file_cache_reset(stream->cachep);
file_error:
return rc;
}
/* close the stream referenced by 'stream' */
int close_stream_internal(struct filestr_base *stream)
{
int rc;
unsigned int foflags = fileobj_get_flags(stream);
if ((foflags & (FO_SINGLE|FO_REMOVED)) == (FO_SINGLE|FO_REMOVED))
{
/* nothing is referencing it so now remove the file's data */
rc = fat_remove(&stream->infop->fatfile, FAT_RM_DATA);
if (rc < 0)
{
DEBUGF("I/O error removing file data: %d\n", rc);
FILE_ERROR(EIO, rc * 10 - 1);
}
}
rc = 0;
file_error:
/* close no matter what */
fileop_onclose_internal(stream);
return rc;
}
/* create a new stream in the parent directory */
int create_stream_internal(struct file_base_info *parentinfop,
const char *basename, size_t length,
unsigned int attr, unsigned int callflags,
struct filestr_base *stream)
{
/* assumes an attempt was made beforehand to open *stream with
open_stream_internal() which returned zero (successfully not found),
so does not initialize it here */
const char * const name = strmemdupa(basename, length);
DEBUGF("Creating \"%s\"\n", name);
struct file_base_info info;
int rc = fat_create_file(&parentinfop->fatfile, name, attr,
&info.fatfile, get_dir_fatent_dircache());
if (rc < 0)
{
DEBUGF("Create failed: %d\n", rc);
FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 1);
}
/* dir_fatent is implicit arg */
fileop_oncreate_internal(stream, &info, callflags, parentinfop, name);
rc = 0;
file_error:
return rc;
}
/* removes files and directories - back-end to remove() and rmdir() */
int remove_stream_internal(const char *path, struct filestr_base *stream,
unsigned int callflags)
{
/* Only FF_* flags should be in callflags */
int rc;
struct filestr_base opened_stream;
if (!stream)
stream = &opened_stream;
if (stream == &opened_stream)
{
/* no stream provided so open local one */
rc = open_stream_internal(path, callflags, stream, NULL);
if (rc < 0)
{
DEBUGF("Failed opening path: %d\n", rc);
FILE_ERROR(ERRNO, rc * 10 - 1);
}
}
/* else ignore the 'path' argument */
if (callflags & FF_DIR)
{
/* directory to be removed must be empty */
rc = test_dir_empty_internal(stream);
if (rc < 0)
FILE_ERROR(ERRNO, rc * 10 - 2);
}
/* save old info since fat_remove() will destroy the dir info */
struct file_base_info oldinfo = *stream->infop;
rc = fat_remove(&stream->infop->fatfile, FAT_RM_DIRENTRIES);
if (rc < 0)
{
DEBUGF("I/O error removing dir entries: %d\n", rc);
FILE_ERROR(EIO, rc * 10 - 3);
}
fileop_onremove_internal(stream, &oldinfo);
rc = 0;
file_error:
if (stream == &opened_stream)
{
/* will do removal of data below if this is the only reference */
int rc2 = close_stream_internal(stream);
if (rc2 < 0 && rc >= 0)
{
rc = rc2 * 10 - 4;
DEBUGF("Success but failed closing stream: %d\n", rc);
}
}
return rc;
}
/* test file/directory existence with constraints */
int test_stream_exists_internal(const char *path, unsigned int callflags)
{
/* only FF_* flags should be in callflags */
struct filestr_base stream;
int rc = open_stream_internal(path, callflags, &stream, NULL);
if (rc > 0)
close_stream_internal(&stream);
return rc;
}
/* one-time init at startup */
void filesystem_init(void)
{
mrsw_init(&file_internal_mrsw);
dc_init();
fileobj_mgr_init();
}

View File

@ -1,102 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "dir.h"
#include "stdlib.h"
#include "string.h"
#include "debug.h"
#include "file.h"
#include "filefuncs.h"
#include "string-extra.h"
#ifndef __PCTOOL__
#ifdef HAVE_MULTIVOLUME
/* returns on which volume this is, and copies the reduced name
(sortof a preprocessor for volume-decorated pathnames) */
int strip_volume(const char* name, char* namecopy)
{
int volume = 0;
const char *temp = name;
while (*temp == '/') /* skip all leading slashes */
++temp;
if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS))
{
temp += VOL_ENUM_POS; /* behind special name */
volume = atoi(temp); /* number is following */
temp = strchr(temp, '/'); /* search for slash behind */
if (temp != NULL)
name = temp; /* use the part behind the volume */
else
name = "/"; /* else this must be the root dir */
}
strlcpy(namecopy, name, MAX_PATH);
return volume;
}
#endif /* #ifdef HAVE_MULTIVOLUME */
#endif /* __PCTOOL__ */
/* Test file existence, using dircache of possible */
bool file_exists(const char *file)
{
int fd;
#ifdef DEBUG
if (!file || !*file)
{
DEBUGF("%s(%p): Invalid parameter!\n", __func__, file);
return false;
}
#endif
#ifdef HAVE_DIRCACHE
if (dircache_is_enabled())
return (dircache_get_entry_id(file) >= 0);
#endif
fd = open(file, O_RDONLY);
if (fd < 0)
return false;
close(fd);
return true;
}
bool dir_exists(const char *path)
{
DIR* d = opendir(path);
if (!d)
return false;
closedir(d);
return true;
}
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SIMULATOR)
struct dirinfo dir_get_info(DIR* parent, struct dirent *entry)
{
(void)parent;
return entry->info;
}
#endif

View File

@ -0,0 +1,396 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "system.h"
#include "debug.h"
#include "file.h"
#include "dir.h"
#include "disk_cache.h"
#include "fileobj_mgr.h"
#include "dircache_redirect.h"
/**
* Manages file and directory streams on all volumes
*
* Intended for internal use by disk, file and directory code
*/
/* there will always be enough of these for all user handles, thus these
functions don't return failure codes */
#define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS)
/* describes the file as an image on the storage medium */
static struct fileobj_binding
{
struct file_base_binding bind; /* base info list item (first!) */
uint16_t flags; /* F(D)(O)_* bits of this file/dir */
uint16_t writers; /* number of writer streams */
struct filestr_cache cache; /* write mode shared cache */
file_size_t size; /* size of this file */
struct ll_head list; /* open streams for this file/dir */
} fobindings[MAX_FILEOBJS];
static struct mutex stream_mutexes[MAX_FILEOBJS] SHAREDBSS_ATTR;
static struct ll_head free_bindings;
static struct ll_head busy_bindings[NUM_VOLUMES];
#define BUSY_BINDINGS(volume) \
(&busy_bindings[IF_MV_VOL(volume)])
#define BASEBINDING_LIST(bindp) \
(BUSY_BINDINGS(BASEBINDING_VOL(bindp)))
#define FREE_BINDINGS() \
(&free_bindings)
#define BINDING_FIRST(type, volume...) \
((struct fileobj_binding *)type##_BINDINGS(volume)->head)
#define BINDING_NEXT(fobp) \
((struct fileobj_binding *)(fobp)->bind.node.next)
#define FOR_EACH_BINDING(volume, fobp) \
for (struct fileobj_binding *fobp = BINDING_FIRST(BUSY, volume); \
fobp; fobp = BINDING_NEXT(fobp))
#define STREAM_FIRST(fobp) \
((struct filestr_base *)(fobp)->list.head)
#define STREAM_NEXT(s) \
((struct filestr_base *)(s)->node.next)
#define STREAM_THIS(s) \
(s)
#define FOR_EACH_STREAM(what, start, s) \
for (struct filestr_base *s = STREAM_##what(start); \
s; s = STREAM_NEXT(s))
/* syncs information for the stream's old and new parent directory if any are
currently opened */
static void fileobj_sync_parent(const struct file_base_info *infop[],
int count)
{
FOR_EACH_BINDING(infop[0]->volume, fobp)
{
if ((fobp->flags & (FO_DIRECTORY|FO_REMOVED)) != FO_DIRECTORY)
continue; /* not directory or removed can't be parent of anything */
struct filestr_base *parentstrp = STREAM_FIRST(fobp);
struct fat_file *parentfilep = &parentstrp->infop->fatfile;
for (int i = 0; i < count; i++)
{
if (!fat_dir_is_parent(parentfilep, &infop[i]->fatfile))
continue;
/* discard scan/read caches' parent dir info */
FOR_EACH_STREAM(THIS, parentstrp, s)
filestr_discard_cache(s);
}
}
}
/* see if this file has open streams and return that fileobj_binding if so,
else grab a new one from the free list; returns true if this stream is
the only open one */
static bool binding_assign(const struct file_base_info *srcinfop,
struct fileobj_binding **fobpp)
{
FOR_EACH_BINDING(srcinfop->fatfile.volume, fobp)
{
if (fobp->flags & FO_REMOVED)
continue;
if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile))
{
/* already has open streams */
*fobpp = fobp;
return false;
}
}
/* not found - allocate anew */
*fobpp = BINDING_FIRST(FREE);
ll_remove_first(FREE_BINDINGS());
ll_init(&(*fobpp)->list);
return true;
}
/* mark descriptor as unused and return to the free list */
static void binding_add_to_free_list(struct fileobj_binding *fobp)
{
fobp->flags = 0;
ll_insert_last(FREE_BINDINGS(), &fobp->bind.node);
}
/** File and directory internal interface **/
void file_binding_insert_last(struct file_base_binding *bindp)
{
ll_insert_last(BASEBINDING_LIST(bindp), &bindp->node);
}
void file_binding_remove(struct file_base_binding *bindp)
{
ll_remove(BASEBINDING_LIST(bindp), &bindp->node);
}
#ifdef HAVE_DIRCACHE
void file_binding_insert_first(struct file_base_binding *bindp)
{
ll_insert_first(BASEBINDING_LIST(bindp), &bindp->node);
}
void file_binding_remove_next(struct file_base_binding *prevp,
struct file_base_binding *bindp)
{
ll_remove_next(BASEBINDING_LIST(bindp), &prevp->node);
(void)bindp;
}
#endif /* HAVE_DIRCACHE */
/* opens the file object for a new stream and sets up the caches;
* the stream must already be opened at the FS driver level and *stream
* initialized.
*
* NOTE: switches stream->infop to the one kept in common for all streams of
* the same file, making a copy for only the first stream
*/
void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags)
{
struct fileobj_binding *fobp;
bool first = binding_assign(srcinfop, &fobp);
/* add stream to this file's list */
ll_insert_last(&fobp->list, &stream->node);
/* initiate the new stream into the enclave */
stream->flags = FDO_BUSY | (callflags & (FD_WRITE|FD_WRONLY|FD_APPEND));
stream->infop = &fobp->bind.info;
stream->fatstr.fatfilep = &fobp->bind.info.fatfile;
stream->bindp = &fobp->bind;
stream->mtx = &stream_mutexes[fobp - fobindings];
if (first)
{
/* first stream for file */
fobp->bind.info = *srcinfop;
fobp->flags = FDO_BUSY | FO_SINGLE |
(callflags & (FO_DIRECTORY|FO_TRUNC));
fobp->writers = 0;
fobp->size = 0;
if (callflags & FD_WRITE)
{
/* first one is a writer */
fobp->writers = 1;
file_cache_init(&fobp->cache);
filestr_assign_cache(stream, &fobp->cache);
}
fileobj_bind_file(&fobp->bind);
}
else
{
/* additional stream for file */
fobp->flags &= ~FO_SINGLE;
fobp->flags |= callflags & FO_TRUNC;
/* once a file/directory, always a file/directory; such a change
is a bug */
if ((callflags ^ fobp->flags) & FO_DIRECTORY)
{
DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n",
__func__, stream, callflags);
}
if (fobp->writers)
{
/* already writers present */
fobp->writers++;
filestr_assign_cache(stream, &fobp->cache);
}
else if (callflags & FD_WRITE)
{
/* first writer */
fobp->writers = 1;
file_cache_init(&fobp->cache);
FOR_EACH_STREAM(FIRST, fobp, s)
filestr_assign_cache(s, &fobp->cache);
}
/* else another reader */
}
}
/* close the stream and free associated resources */
void fileobj_fileop_close(struct filestr_base *stream)
{
switch (stream->flags)
{
case 0: /* not added to manager */
case FV_NONEXIST: /* forced-closed by unmounting */
filestr_base_destroy(stream);
return;
}
struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp;
unsigned int foflags = fobp->flags;
ll_remove(&fobp->list, &stream->node);
if ((foflags & FO_SINGLE) || fobp->writers == 0)
{
if (foflags & FO_SINGLE)
{
/* last stream for file; close everything */
fileobj_unbind_file(&fobp->bind);
if (fobp->writers)
file_cache_free(&fobp->cache);
binding_add_to_free_list(fobp);
}
}
else if ((stream->flags & FD_WRITE) && --fobp->writers == 0)
{
/* only readers remain; switch back to stream-local caching */
FOR_EACH_STREAM(FIRST, fobp, s)
filestr_copy_cache(s, &fobp->cache);
file_cache_free(&fobp->cache);
}
if (!(foflags & FO_SINGLE) && fobp->list.head == fobp->list.tail)
fobp->flags |= FO_SINGLE; /* only one open stream remaining */
filestr_base_destroy(stream);
}
/* informs manager that file has been created */
void fileobj_fileop_create(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags)
{
fileobj_fileop_open(stream, srcinfop, callflags);
fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
}
/* informs manager that file has been removed */
void fileobj_fileop_remove(struct filestr_base *stream,
const struct file_base_info *oldinfop)
{
((struct fileobj_binding *)stream->bindp)->flags |= FO_REMOVED;
fileobj_sync_parent((const struct file_base_info *[]){ oldinfop }, 1);
}
/* informs manager that file has been renamed */
void fileobj_fileop_rename(struct filestr_base *stream,
const struct file_base_info *oldinfop)
{
/* if there is old info then this was a move and the old parent has to be
informed */
int count = oldinfop ? 2 : 1;
fileobj_sync_parent(&(const struct file_base_info *[])
{ oldinfop, stream->infop }[2 - count],
count);
}
/* informs manager than directory entries have been updated */
void fileobj_fileop_sync(struct filestr_base *stream)
{
fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1);
}
/* inform manager that file has been truncated */
void fileobj_fileop_truncate(struct filestr_base *stream)
{
/* let caller update internal info */
FOR_EACH_STREAM(FIRST, (struct fileobj_binding *)stream->bindp, s)
ftruncate_internal_callback(stream, s);
}
/* query for the pointer to the size storage for the file object */
file_size_t * fileobj_get_sizep(const struct filestr_base *stream)
{
if (!stream->bindp)
return NULL;
return &((struct fileobj_binding *)stream->bindp)->size;
}
/* query manager bitflags for the file object */
unsigned int fileobj_get_flags(const struct filestr_base *stream)
{
if (!stream->bindp)
return 0;
return ((struct fileobj_binding *)stream->bindp)->flags;
}
/* change manager bitflags for the file object */
void fileobj_change_flags(struct filestr_base *stream,
unsigned int flags, unsigned int mask)
{
struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp;
if (fobp)
fobp->flags = (fobp->flags & ~mask) | (flags & mask);
}
/* mark all open streams on a device as "nonexistant" */
void fileobj_mgr_unmount(IF_MV_NONVOID(int volume))
{
/* right now, there is nothing else to be freed when marking a descriptor
as "nonexistant" but a callback could be added if that changes */
FOR_EACH_VOLUME(volume, v)
{
struct fileobj_binding *fobp;
while ((fobp = BINDING_FIRST(BUSY, v)))
{
struct filestr_base *s;
while ((s = STREAM_FIRST(fobp)))
{
/* keep it "busy" to avoid races; any valid file/directory
descriptor returned by an open call should always be
closed by whomever opened it (of course!) */
fileop_onclose_internal(s);
s->flags = FV_NONEXIST;
}
}
}
}
/* one-time init at startup */
void fileobj_mgr_init(void)
{
for (unsigned int i = 0; i < NUM_VOLUMES; i++)
ll_init(BUSY_BINDINGS(i));
ll_init(FREE_BINDINGS());
for (unsigned int i = 0; i < MAX_FILEOBJS; i++)
{
mutex_init(&stream_mutexes[i]);
binding_add_to_free_list(&fobindings[i]);
}
}

421
firmware/common/pathfuncs.c Normal file
View File

@ -0,0 +1,421 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <string.h>
#include <ctype.h>
#include "system.h"
#include "pathfuncs.h"
#include "string-extra.h"
#ifdef HAVE_MULTIVOLUME
#include <stdio.h>
#include "storage.h"
enum storage_name_dec_indexes
{
#if (CONFIG_STORAGE & STORAGE_ATA)
STORAGE_DEC_IDX_ATA,
#endif
#if (CONFIG_STORAGE & STORAGE_MMC)
STORAGE_DEC_IDX_MMC,
#endif
#if (CONFIG_STORAGE & STORAGE_SD)
STORAGE_DEC_IDX_SD,
#endif
#if (CONFIG_STORAGE & STORAGE_NAND)
STORAGE_DEC_IDX_NAND,
#endif
#if (CONFIG_STORAGE & STORAGE_RAMDISK)
STORAGE_DEC_IDX_RAMDISK,
#endif
#if (CONFIG_STORAGE & STORAGE_HOSTFS)
STORAGE_DEC_IDX_HOSTFS,
#endif
STORAGE_NUM_DEC_IDX,
};
static const char * const storage_dec_names[STORAGE_NUM_DEC_IDX+1] =
{
#if (CONFIG_STORAGE & STORAGE_ATA)
[STORAGE_DEC_IDX_ATA] = ATA_VOL_DEC,
#endif
#if (CONFIG_STORAGE & STORAGE_MMC)
[STORAGE_DEC_IDX_MMC] = MMC_VOL_DEC,
#endif
#if (CONFIG_STORAGE & STORAGE_SD)
[STORAGE_DEC_IDX_SD] = SD_VOL_DEC,
#endif
#if (CONFIG_STORAGE & STORAGE_NAND)
[STORAGE_DEC_IDX_NAND] = NAND_VOL_DEC,
#endif
#if (CONFIG_STORAGE & STORAGE_RAMDISK)
[STORAGE_DEC_IDX_RAMDISK] = RAMDISK_VOL_DEC,
#endif
#if (CONFIG_STORAGE & STORAGE_HOSTFS)
[STORAGE_DEC_IDX_HOSTFS] = HOSTFS_VOL_DEC,
#endif
[STORAGE_NUM_DEC_IDX] = DEFAULT_VOL_DEC,
};
static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] =
{
[0 ... STORAGE_NUM_TYPES] = STORAGE_NUM_DEC_IDX,
#if (CONFIG_STORAGE & STORAGE_ATA)
[STORAGE_ATA_NUM] = STORAGE_DEC_IDX_ATA,
#endif
#if (CONFIG_STORAGE & STORAGE_MMC)
[STORAGE_MMC_NUM] = STORAGE_DEC_IDX_MMC,
#endif
#if (CONFIG_STORAGE & STORAGE_SD)
[STORAGE_SD_NUM] = STORAGE_DEC_IDX_SD,
#endif
#if (CONFIG_STORAGE & STORAGE_NAND)
[STORAGE_NAND_NUM] = STORAGE_DEC_IDX_NAND,
#endif
#if (CONFIG_STORAGE & STORAGE_RAMDISK)
[STORAGE_RAMDISK_NUM] = STORAGE_DEC_IDX_RAMDISK,
#endif
#if (CONFIG_STORAGE & STORAGE_HOSTFS)
[STORAGE_HOSTFS_NUM] = STORAGE_DEC_IDX_HOSTFS,
#endif
};
/* Returns on which volume this is and sets *nameptr to the portion of the
* path after the volume specifier, which could be the null if the path is
* just a volume root. If *nameptr > name, then a volume specifier was
* found. If 'greedy' is 'true', then it all separators after the volume
* specifier are consumed, if one was found.
*/
int path_strip_volume(const char *name, const char **nameptr, bool greedy)
{
int volume = 0;
const char *t = name;
int c, v = 0;
/* format: "/<xxx##>/foo/bar"
* the "xxx" is pure decoration; only an unbroken trailing string of
* digits within the brackets is parsed as the volume number and of
* those, only the last ones VOL_MUM_MAX allows.
*/
c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */
if (c != VOL_START_TOK) /* missing start token? no volume */
goto volume0;
do
{
switch (c)
{
case '0' ... '9': /* digit; parse volume number */
v = (v * 10 + c - '0') % VOL_NUM_MAX;
break;
case '\0':
case PATH_SEPCH: /* no closing bracket; no volume */
goto volume0;
default: /* something else; reset volume */
v = 0;
}
}
while ((c = *++t) != VOL_END_TOK); /* found end token? */
if (!(c = *++t)) /* no more path and no '/' is ok */
;
else if (c != PATH_SEPCH) /* more path and no separator after end */
goto volume0;
else if (greedy)
t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */
/* if 'greedy' is true and **nameptr == '\0' then it's only a volume
root whether or not it has trailing separators */
volume = v;
name = t;
volume0:
if (nameptr)
*nameptr = name;
return volume;
}
/* Returns the volume specifier decorated with the storage type name.
* Assumes the supplied buffer size is at least {VOL_MAX_LEN}+1.
*/
int get_volume_name(int volume, char *buffer)
{
if (volume < 0)
{
*buffer = '\0';
return 0;
}
volume %= VOL_NUM_MAX; /* as path parser would have it */
int type = storage_driver_type(volume_drive(volume));
if (type < 0 || type > STORAGE_NUM_TYPES)
type = STORAGE_NUM_TYPES;
const char *voldec = storage_dec_names[storage_dec_indexes[type]];
return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c",
VOL_START_TOK, voldec, volume, VOL_END_TOK);
}
#endif /* HAVE_MULTIVOLUME */
/* Just like path_strip_volume() but strips a leading drive specifier and
* returns the drive number (A=0, B=1, etc.). -1 means no drive was found.
* If 'greedy' is 'true', all separators after the volume are consumed.
*/
int path_strip_drive(const char *name, const char **nameptr, bool greedy)
{
int c = toupper(*name);
if (c >= 'A' && c <= 'Z' && name[1] == PATH_DRVSEPCH)
{
name = &name[2];
if (greedy)
name = GOBBLE_PATH_SEPCH(name);
*nameptr = name;
return c - 'A';
}
*nameptr = name;
return -1;
}
/* Strips leading and trailing whitespace from a path
* " a/b \txyz" *nameptr->a, len=3: "a/b"
*/
size_t path_trim_whitespace(const char *name, const char **nameptr)
{
int c;
while ((c = *name) <= ' ' && c)
++name;
const char *first = name;
const char *last = name;
while (1)
{
if (c < ' ')
{
*nameptr = first;
return last - first;
}
while ((c = *++name) > ' ');
last = name;
while (c == ' ') c = *++name;
}
}
/* Strips directory components from the path
* "" *nameptr->NUL, len=0: ""
* "/" *nameptr->/, len=1: "/"
* "//" *nameptr->2nd /, len=1: "/"
* "/a" *nameptr->a, len=1: "a"
* "/a/bc" *nameptr->b, len=2: "bc"
* "d" *nameptr->d, len=1: "d"
* "ef/gh" *nameptr->g, len=2: "gh"
*/
size_t path_basename(const char *name, const char **nameptr)
{
const char *p = name;
const char *q = p;
const char *r = q;
while (*(p = GOBBLE_PATH_SEPCH(p)))
{
q = p;
p = GOBBLE_PATH_COMP(++p);
r = p;
}
if (r == name && p > name)
q = p, r = q--; /* root - return last slash */
/* else path is an empty string */
*nameptr = q;
return r - q;
}
/* Strips the trailing component from the path
* "" *nameptr->NUL, len=0: ""
* "/" *nameptr->/, len=1: "/"
* "//" *nameptr->2nd /, len=1: "/"
* "/a" *nameptr->/, len=1: "/"
* "/a/bc" *nameptr->/, len=2: "/a"
* "d" *nameptr->d, len=0: ""
* "ef/gh" *nameptr->e, len=2: "ef"
*/
size_t path_dirname(const char *name, const char **nameptr)
{
const char *p = GOBBLE_PATH_SEPCH(name);
const char *q = name;
const char *r = p;
while (*(p = GOBBLE_PATH_COMP(p)))
{
const char *s = p;
if (!*(p = GOBBLE_PATH_SEPCH(p)))
break;
q = s;
}
if (q == name && r > name)
name = r, q = name--; /* root - return last slash */
*nameptr = name;
return q - name;
}
/* Removes trailing separators from a path
* "" *nameptr->NUL, len=0: ""
* "/" *nameptr->/, len=1: "/"
* "//" *nameptr->2nd /, len=1: "/"
* "/a/" *nameptr->/, len=2: "/a"
* "//b/" *nameptr->1st /, len=3: "//b"
* "/c/" *nameptr->/, len=2: "/c"
*/
size_t path_strip_trailing_separators(const char *name, const char **nameptr)
{
const char *p;
size_t len = path_basename(name, &p);
if (len == 1 && *p == '/' && p > name)
{
*nameptr = p;
name = p - 1; /* root with multiple separators */
}
else
{
*nameptr = name;
p += len; /* length to end of basename */
}
return p - name;
}
/* Transforms "wrong" separators into the correct ones
* "c:\windows\system32" -> "c:/windows/system32"
*
* 'path' and 'dstpath' may either be the same buffer or non-overlapping
*/
void path_correct_separators(char *dstpath, const char *path)
{
char *dstp = dstpath;
const char *p = path;
while (1)
{
const char *next = strchr(p, PATH_BADSEPCH);
if (!next)
break;
size_t size = next - p;
if (dstpath != path)
memcpy(dstp, p, size); /* not in-place */
dstp += size;
*dstp++ = PATH_SEPCH;
p = next + 1;
}
if (dstpath != path)
strcpy(dstp, p);
}
/* Appends one path to another, adding separators between components if needed.
* Return value and behavior is otherwise as strlcpy so that truncation may be
* detected.
*
* For basepath and component:
* PA_SEP_HARD adds a separator even if the base path is empty
* PA_SEP_SOFT adds a separator only if the base path is not empty
*/
size_t path_append(char *buf, const char *basepath,
const char *component, size_t bufsize)
{
const char *base = basepath && basepath[0] ? basepath : buf;
if (!base)
return bufsize; /* won't work to get lengths from buf */
if (!buf)
bufsize = 0;
if (path_is_absolute(component))
{
/* 'component' is absolute; replace all */
basepath = component;
component = "";
}
/* if basepath is not null or empty, buffer contents are replaced,
otherwise buf contains the base path */
size_t len = base == buf ? strlen(buf) : strlcpy(buf, basepath, bufsize);
bool separate = false;
if (!basepath || !component)
separate = !len || base[len-1] != PATH_SEPCH;
else if (component[0])
separate = len && base[len-1] != PATH_SEPCH;
/* caller might lie about size of buf yet use buf as the base */
if (base == buf && bufsize && len >= bufsize)
buf[bufsize - 1] = '\0';
buf += len;
bufsize -= MIN(len, bufsize);
if (separate && (len++, bufsize > 0) && --bufsize > 0)
*buf++ = PATH_SEPCH;
return len + strlcpy(buf, component ?: "", bufsize);
}
/* Returns the location and length of the next path component, consuming the
* input in the process.
*
* "/a/bc/d" breaks into:
* start: *namep->1st /
* call 1: *namep->a, *pathp->2nd / len=1: "a"
* call 2: *namep->b, *pathp->3rd / len=2: "bc"
* call 3: *namep->d, *pathp->NUL, len=1: "d"
* call 4: *namep->NUL, *pathp->NUL, len=0: ""
*
* Returns: 0 if the input has been consumed
* The length of the component otherwise
*/
ssize_t parse_path_component(const char **pathp, const char **namep)
{
/* a component starts at a non-separator and continues until the next
separator or null */
const char *p = GOBBLE_PATH_SEPCH(*pathp);
const char *name = p;
if (*p)
p = GOBBLE_PATH_COMP(++p);
*pathp = p;
*namep = name;
return p - name;
}

View File

@ -1,432 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2010 Thomas Martitz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h> /* snprintf */
#include <stdlib.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "config.h"
#include "rbpaths.h"
#include "crc32.h"
#include "file.h" /* MAX_PATH */
#include "logf.h"
#include "gcc_extensions.h"
#include "string-extra.h"
#include "filefuncs.h"
/* In this file we need the actual OS library functions, not the shadowed
* wrapper used within Rockbox' application code (except SDL adds
* another layer) */
#undef open
#undef creat
#undef remove
#undef rename
#undef opendir
#undef closedir
#undef readdir
#undef mkdir
#undef rmdir
#undef dirent
#undef DIR
#undef readlink
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
static const char rbhome[] = "/sdcard";
#elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) && !defined(__PCTOOL__)
const char *rbhome;
#else
/* YPR0, YPR1 */
static const char rbhome[] = HOME_DIR;
#endif
#if !(defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) && !defined(__PCTOOL__)
/* Special dirs are user-accessible (and user-writable) dirs which take priority
* over the ones where Rockbox is installed to. Classic example would be
* $HOME/.config/rockbox.org vs /usr/share/rockbox */
#define HAVE_SPECIAL_DIRS
#endif
/* flags for get_user_file_path() */
/* whether you need write access to that file/dir, especially true
* for runtime generated files (config.cfg) */
#define NEED_WRITE (1<<0)
/* file or directory? */
#define IS_FILE (1<<1)
#ifdef HAVE_MULTIDRIVE
static uint32_t rbhome_hash;
/* A special link is created under e.g. HOME_DIR/<microSD1>, e.g. to access
* external storage in a convinient location, much similar to the mount
* point on our native targets. Here they are treated as symlink (one which
* doesn't actually exist in the filesystem and therefore we have to override
* readlink() */
static const char *handle_special_links(const char* link, unsigned flags,
char *buf, const size_t bufsize)
{
(void) flags;
char vol_string[VOL_ENUM_POS + 8];
int len = sprintf(vol_string, VOL_NAMES, 1);
/* link might be passed with or without HOME_DIR expanded. To handle
* both perform substring matching (VOL_NAMES is unique enough) */
const char *begin = strstr(link, vol_string);
if (begin)
{
/* begin now points to the start of vol_string within link,
* we want to copy the remainder of the paths, prefixed by
* the actual mount point (the remainder might be "") */
snprintf(buf, bufsize, MULTIDRIVE_DIR"%s", begin + len);
return buf;
}
return link;
}
#endif
void paths_init(void)
{
#ifdef HAVE_SPECIAL_DIRS
/* make sure $HOME/.config/rockbox.org exists, it's needed for config.cfg */
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
mkdir("/sdcard/rockbox", 0777);
mkdir("/sdcard/rockbox/rocks.data", 0777);
#else
char config_dir[MAX_PATH];
const char *home = getenv("RBROOT");
if (!home)
{
home = getenv("HOME");
}
if (!home)
{
logf("HOME environment var not set. Can't write config");
return;
}
rbhome = home;
snprintf(config_dir, sizeof(config_dir), "%s/.config", home);
mkdir(config_dir, 0777);
snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org", home);
mkdir(config_dir, 0777);
/* Plugin data directory */
snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org/rocks.data", home);
mkdir(config_dir, 0777);
#endif
#endif /* HAVE_SPECIAL_DIRS */
#ifdef HAVE_MULTIDRIVE
rbhome_hash = crc_32((const void *) rbhome, strlen(rbhome), 0xffffffff);
#endif /* HAVE_MULTIDRIVE */
}
#ifdef HAVE_SPECIAL_DIRS
static bool try_path(const char* filename, unsigned flags)
{
if (flags & IS_FILE)
{
if (file_exists(filename))
return true;
}
else
{
if (dir_exists(filename))
return true;
}
return false;
}
static const char* _get_user_file_path(const char *path,
unsigned flags,
char* buf,
const size_t bufsize)
{
const char *ret = path;
const char *pos = path;
/* replace ROCKBOX_DIR in path with $HOME/.config/rockbox.org */
pos += ROCKBOX_DIR_LEN;
if (*pos == '/') pos += 1;
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
if (snprintf(buf, bufsize, "/sdcard/rockbox/%s", pos)
#else
if (snprintf(buf, bufsize, "%s/.config/rockbox.org/%s", rbhome, pos)
#endif
>= (int)bufsize)
return NULL;
/* always return the replacement buffer (pointing to $HOME) if
* write access is needed */
if (flags & NEED_WRITE)
ret = buf;
else if (try_path(buf, flags))
ret = buf;
if (ret != buf) /* not found in $HOME, try ROCKBOX_BASE_DIR, !NEED_WRITE only */
{
if (snprintf(buf, bufsize, ROCKBOX_SHARE_PATH "/%s", pos) >= (int)bufsize)
return NULL;
if (try_path(buf, flags))
ret = buf;
}
return ret;
}
#endif
static const char* handle_special_dirs(const char* dir, unsigned flags,
char *buf, const size_t bufsize)
{
(void) flags; (void) buf; (void) bufsize;
#ifdef HAVE_SPECIAL_DIRS
if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN))
{
const char *p = dir + HOME_DIR_LEN;
while (*p == '/') p++;
snprintf(buf, bufsize, "%s/%s", rbhome, p);
dir = buf;
}
else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN))
dir = _get_user_file_path(dir, flags, buf, bufsize);
#endif
#ifdef HAVE_MULTIDRIVE
dir = handle_special_links(dir, flags, buf, bufsize);
#endif
return dir;
}
int app_open(const char *name, int o, ...)
{
char realpath[MAX_PATH];
va_list ap;
int fd;
int flags = IS_FILE;
if (o & (O_CREAT|O_RDWR|O_TRUNC|O_WRONLY))
flags |= NEED_WRITE;
name = handle_special_dirs(name, flags, realpath, sizeof(realpath));
va_start(ap, o);
fd = open(name, o, va_arg(ap, unsigned int));
va_end(ap);
return fd;
}
int app_creat(const char* name, mode_t mode)
{
return app_open(name, O_CREAT|O_WRONLY|O_TRUNC, mode);
}
int app_remove(const char *name)
{
char realpath[MAX_PATH];
const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath));
return remove(fname);
}
int app_rename(const char *old, const char *new)
{
char realpath_old[MAX_PATH], realpath_new[MAX_PATH];
const char *final_old, *final_new;
final_old = handle_special_dirs(old, NEED_WRITE, realpath_old, sizeof(realpath_old));
final_new = handle_special_dirs(new, NEED_WRITE, realpath_new, sizeof(realpath_new));
return rename(final_old, final_new);
}
/* need to wrap around DIR* because we need to save the parent's
* directory path in order to determine dirinfo, required to implement
* get_dir_info() */
struct __dir {
DIR *dir;
#ifdef HAVE_MULTIDRIVE
int volumes_returned;
/* A crc of rbhome is used to speed op the common case where
* readdir()/get_dir_info() is called on non-rbhome paths, because
* each call needs to check against rbhome for the virtual
* mount point of the external storage */
uint32_t path_hash;
#endif
char path[];
};
struct dirinfo dir_get_info(DIR* _parent, struct dirent *dir)
{
struct __dir *parent = (struct __dir*)_parent;
struct stat s;
struct tm *tm = NULL;
struct dirinfo ret;
char path[MAX_PATH];
memset(&ret, 0, sizeof(ret));
#ifdef HAVE_MULTIDRIVE
char vol_string[VOL_ENUM_POS + 8];
sprintf(vol_string, VOL_NAMES, 1);
if (UNLIKELY(rbhome_hash == parent->path_hash) &&
/* compare path anyway because of possible hash collision */
!strcmp(vol_string, dir->d_name))
{
ret.attribute = ATTR_LINK;
strcpy(path, MULTIDRIVE_DIR);
}
else
#endif
snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name);
if (!lstat(path, &s))
{
int err = 0;
if (S_ISLNK(s.st_mode))
{
ret.attribute |= ATTR_LINK;
err = stat(path, &s);
}
if (!err)
{
if (S_ISDIR(s.st_mode))
ret.attribute |= ATTR_DIRECTORY;
ret.size = s.st_size;
if ((tm = localtime(&(s.st_mtime))))
{
ret.wrtdate = ((tm->tm_year - 80) << 9) |
((tm->tm_mon + 1) << 5) |
tm->tm_mday;
ret.wrttime = (tm->tm_hour << 11) |
(tm->tm_min << 5) |
(tm->tm_sec >> 1);
}
}
}
return ret;
}
DIR* app_opendir(const char *_name)
{
size_t name_len;
char realpath[MAX_PATH];
const char *name = handle_special_dirs(_name, 0, realpath, sizeof(realpath));
name_len = strlen(name);
char *buf = malloc(sizeof(struct __dir) + name_len+1);
if (!buf)
return NULL;
struct __dir *this = (struct __dir*)buf;
/* carefully remove any trailing slash from the input, so that
* hash/path matching in readdir() works properly */
while (name[name_len-1] == '/' && name_len > 1)
name_len -= 1;
/* strcpy cannot be used because of trailing slashes */
memcpy(this->path, name, name_len);
this->path[name_len] = 0;
this->dir = opendir(this->path);
if (!this->dir)
{
free(buf);
return NULL;
}
#ifdef HAVE_MULTIDRIVE
this->volumes_returned = 0;
this->path_hash = crc_32((const void *)this->path, name_len, 0xffffffff);
#endif
return (DIR*)this;
}
int app_closedir(DIR *dir)
{
struct __dir *this = (struct __dir*)dir;
int ret = closedir(this->dir);
free(this);
return ret;
}
struct dirent* app_readdir(DIR* dir)
{
struct __dir *d = (struct __dir*)dir;
#ifdef HAVE_MULTIDRIVE
/* this is not MT-safe but OK according to man readdir */
static struct dirent voldir;
if (UNLIKELY(rbhome_hash == d->path_hash)
&& d->volumes_returned < (NUM_VOLUMES-1)
&& volume_present(d->volumes_returned+1)
/* compare path anyway because of possible hash collision */
&& !strcmp(d->path, rbhome))
{
d->volumes_returned += 1;
sprintf(voldir.d_name, VOL_NAMES, d->volumes_returned);
return &voldir;
}
#endif
return readdir(d->dir);
}
int app_mkdir(const char* name)
{
char realpath[MAX_PATH];
const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath));
return mkdir(fname, 0777);
}
int app_rmdir(const char* name)
{
char realpath[MAX_PATH];
const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath));
return rmdir(fname);
}
/* On MD we create a virtual symlink for the external drive,
* for this we need to override readlink(). */
ssize_t app_readlink(const char *path, char *buf, size_t bufsiz)
{
char _buf[MAX_PATH];
(void) path; (void) buf; (void) bufsiz;
path = handle_special_dirs(path, 0, _buf, sizeof(_buf));
#ifdef HAVE_MULTIDRIVE
/* if path == _buf then we can be sure handle_special_dir() did something
* and path is not an ordinary directory */
if (path == _buf && !strncmp(path, MULTIDRIVE_DIR, sizeof(MULTIDRIVE_DIR)-1))
{
/* copying NUL is not required as per readlink specification */
ssize_t len = strlen(path);
memcpy(buf, path, len);
return len;
}
#endif
/* does not append NUL !! */
return readlink(path, buf, bufsiz);
}

View File

@ -28,161 +28,227 @@
#include <stdio.h>
#include "config.h"
#include "system.h"
#include "thread.h"
#include "file.h"
#include "debug.h"
#include "rbunicode.h"
#include "rbpaths.h"
#include "pathfuncs.h"
#include "core_alloc.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_NOISODECODE
#define O_NOISODECODE 0
#endif
static int default_codepage = 0;
static int loaded_cp_table = 0;
#define getle16(p) (p[0] | (p[1] >> 8))
#define getbe16(p) ((p[1] << 8) | p[0])
#if !defined (__PCTOOL__) && (CONFIG_PLATFORM & PLATFORM_NATIVE)
/* Because file scanning uses the default CP table when matching entries,
on-demand loading is not feasible; we also must use the filesystem lock */
#include "file_internal.h"
#else /* APPLICATION */
#ifdef __PCTOOL__
#define yield()
#endif
#define open_noiso_internal open
#endif /* !APPLICATION */
#if 0 /* not needed just now (will probably end up a spinlock) */
#include "mutex.h"
static struct mutex cp_mutex SHAREDBSS_ATTR;
#define cp_lock_init() mutex_init(&cp_mutex)
#define cp_lock_enter() mutex_lock(&cp_mutex)
#define cp_lock_leave() mutex_unlock(&cp_mutex)
#else
#define cp_lock_init() do {} while (0)
#define cp_lock_enter() asm volatile ("")
#define cp_lock_leave() asm volatile ("")
#endif
enum cp_tid
{
CP_TID_NONE = -1,
CP_TID_ISO,
CP_TID_932,
CP_TID_936,
CP_TID_949,
CP_TID_950,
};
struct cp_info
{
int8_t tid;
const char *filename;
const char *name;
};
#ifdef HAVE_LCD_BITMAP
#define MAX_CP_TABLE_SIZE 32768
#define NUM_TABLES 5
static const char * const filename[NUM_TABLES] =
#define CPF_ISO "iso.cp"
#define CPF_932 "932.cp" /* SJIS */
#define CPF_936 "936.cp" /* GB2312 */
#define CPF_949 "949.cp" /* KSX1001 */
#define CPF_950 "950.cp" /* BIG5 */
static const struct cp_info cp_info[NUM_CODEPAGES+1] =
{
CODEPAGE_DIR"/iso.cp",
CODEPAGE_DIR"/932.cp", /* SJIS */
CODEPAGE_DIR"/936.cp", /* GB2312 */
CODEPAGE_DIR"/949.cp", /* KSX1001 */
CODEPAGE_DIR"/950.cp" /* BIG5 */
[0 ... NUM_CODEPAGES] = { CP_TID_NONE, NULL , "unknown" },
[ISO_8859_1] = { CP_TID_NONE, NULL , "ISO-8859-1" },
[ISO_8859_7] = { CP_TID_ISO , CPF_ISO, "ISO-8859-7" },
[ISO_8859_8] = { CP_TID_ISO , CPF_ISO, "ISO-8859-8" },
[WIN_1251] = { CP_TID_ISO , CPF_ISO, "CP1251" },
[ISO_8859_11] = { CP_TID_ISO , CPF_ISO, "ISO-8859-11" },
[WIN_1256] = { CP_TID_ISO , CPF_ISO, "CP1256" },
[ISO_8859_9] = { CP_TID_ISO , CPF_ISO, "ISO-8859-9" },
[ISO_8859_2] = { CP_TID_ISO , CPF_ISO, "ISO-8859-2" },
[WIN_1250] = { CP_TID_ISO , CPF_ISO, "CP1250" },
[WIN_1252] = { CP_TID_ISO , CPF_ISO, "CP1252" },
[SJIS] = { CP_TID_932 , CPF_932, "SJIS" },
[GB_2312] = { CP_TID_936 , CPF_936, "GB-2312" },
[KSX_1001] = { CP_TID_949 , CPF_949, "KSX-1001" },
[BIG_5] = { CP_TID_950 , CPF_950, "BIG5" },
[UTF_8] = { CP_TID_NONE, NULL , "UTF-8" },
};
static const char cp_2_table[NUM_CODEPAGES] =
{
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 0
};
static const char * const name_codepages[NUM_CODEPAGES+1] =
{
"ISO-8859-1",
"ISO-8859-7",
"ISO-8859-8",
"CP1251",
"ISO-8859-11",
"CP1256",
"ISO-8859-9",
"ISO-8859-2",
"CP1250",
"CP1252",
"SJIS",
"GB-2312",
"KSX-1001",
"BIG5",
"UTF-8",
"unknown"
};
#if defined(APPLICATION) && defined(__linux__)
static const char * const name_codepages_linux[NUM_CODEPAGES+1] =
{
/* "ISO-8859-1" */ "iso8859-1",
/* "ISO-8859-7" */ "iso8859-7",
/* "ISO-8859-8" */ "iso8859-8",
/* "CP1251" */ "cp1251",
/* "ISO-8859-11"*/ "iso8859-11",
/* "CP1256" */ "cp1256",
/* "ISO-8859-9" */ "iso8859-9",
/* "ISO-8859-2" */ "iso8859-2",
/* "CP1250" */ "cp1250",
/* "CP1252" */ "iso8859-15", /* closest, linux doesnt have a codepage named cp1252 */
/* "SJIS" */ "cp932",
/* "GB-2312" */ "cp936",
/* "KSX-1001" */ "cp949",
/* "BIG5" */ "cp950",
/* "UTF-8" */ "utf8",
/* "unknown" */ "cp437"
};
const char *get_current_codepage_name_linux(void)
{
if (default_codepage < 0 || default_codepage >= NUM_CODEPAGES)
return name_codepages_linux[NUM_CODEPAGES];
return name_codepages_linux[default_codepage];
}
#endif
#else /* !HAVE_LCD_BITMAP, reduced support */
#define MAX_CP_TABLE_SIZE 768
#define NUM_TABLES 1
static const char * const filename[NUM_TABLES] = {
CODEPAGE_DIR"/isomini.cp"
};
#define CPF_ISOMINI "isomini.cp"
static const char cp_2_table[NUM_CODEPAGES] =
static const struct cp_info cp_info[NUM_CODEPAGES+1] =
{
0, 1, 1, 1, 1, 1, 1, 0
[0 ... NUM_CODEPAGES] = { CP_TID_NONE, NULL , "unknown" },
[ISO_8859_1] = { CP_TID_NONE, NULL , "ISO-8859-1" },
[ISO_8859_7] = { CP_TID_ISO , CPF_ISOMINI, "ISO-8859-7" },
[WIN_1251] = { CP_TID_ISO , CPF_ISOMINI, "CP1251" },
[ISO_8859_9] = { CP_TID_ISO , CPF_ISOMINI, "ISO-8859-9" },
[ISO_8859_2] = { CP_TID_ISO , CPF_ISOMINI, "ISO-8859-2" },
[WIN_1250] = { CP_TID_ISO , CPF_ISOMINI, "CP1250" },
[WIN_1252] = { CP_TID_ISO , CPF_ISOMINI, "CP1252" },
[UTF_8] = { CP_TID_ISO , NULL , "UTF-8" },
};
static const char * const name_codepages[NUM_CODEPAGES+1] =
#endif /* HAVE_LCD_BITMAP */
static int default_cp = INIT_CODEPAGE;
static int default_cp_tid = CP_TID_NONE;
static int default_cp_handle = 0;
static int volatile default_cp_table_ref = 0;
static int loaded_cp_tid = CP_TID_NONE;
static int volatile cp_table_ref = 0;
#define CP_LOADING BIT_N(sizeof(int)*8-1) /* guard against multi loaders */
/* non-default codepage table buffer (cannot be bufalloced! playback itself
may be making the load request) */
static unsigned short codepage_table[MAX_CP_TABLE_SIZE+1];
#if defined(APPLICATION) && defined(__linux__)
static const char * const name_codepages_linux[NUM_CODEPAGES+1] =
{
"ISO-8859-1",
"ISO-8859-7",
"CP1251",
"ISO-8859-9",
"ISO-8859-2",
"CP1250",
"CP1252",
"UTF-8",
"unknown"
[0 ... NUM_CODEPAGES] = "unknown",
[ISO_8859_1] = "iso8859-1",
[ISO_8859_7] = "iso8859-7",
[ISO_8859_8] = "iso8859-8",
[WIN_1251] = "cp1251",
[ISO_8859_11] = "iso8859-11",
[WIN_1256] = "cp1256",
[ISO_8859_9] = "iso8859-9",
[ISO_8859_2] = "iso8859-2",
[WIN_1250] = "cp1250",
/* iso8859-15 is closest, linux doesnt have a codepage named cp1252 */
[WIN_1252] = "iso8859-15",
[SJIS] = "cp932",
[GB_2312] = "cp936",
[KSX_1001] = "cp949",
[BIG_5] = "cp950",
[UTF_8] = "utf8",
};
#endif
static unsigned short codepage_table[MAX_CP_TABLE_SIZE];
const char *get_current_codepage_name_linux(void)
{
int cp = default_cp;
if (cp < 0 || cp>= NUM_CODEPAGES)
cp = NUM_CODEPAGES;
return name_codepages_linux[cp];
}
#endif /* defined(APPLICATION) && defined(__linux__) */
static const unsigned char utf8comp[6] =
{
0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
};
/* Load codepage file into memory */
static int load_cp_table(int cp)
static inline void cptable_tohw16(uint16_t *buf, unsigned int count)
{
int i = 0;
int table = cp_2_table[cp];
int file, tablesize;
unsigned char tmp[2];
#ifdef ROCKBOX_BIG_ENDIAN
for (unsigned int i = 0; i < count; i++)
buf[i] = letoh16(buf[i]);
#endif
(void)buf; (void)count;
}
if (table == 0 || table == loaded_cp_table)
return 1;
static int move_callback(int handle, void *current, void *new)
{
/* we don't keep a pointer but we have to stop it if this applies to a
buffer not yet swapped-in since it will likely be in use in an I/O
call */
return (handle != default_cp_handle || default_cp_table_ref != 0) ?
BUFLIB_CB_CANNOT_MOVE : BUFLIB_CB_OK;
(void)current; (void)new;
}
file = open(filename[table-1], O_RDONLY|O_BINARY);
static int alloc_and_load_cp_table(int cp, void *buf)
{
static struct buflib_callbacks ops =
{ .move_callback = move_callback };
if (file < 0) {
DEBUGF("Can't open codepage file: %s.cp\n", filename[table-1]);
/* alloc and read only if there is an associated file */
const char *filename = cp_info[cp].filename;
if (!filename)
return 0;
char path[MAX_PATH];
if (path_append(path, CODEPAGE_DIR, filename, sizeof (path))
>= sizeof (path)) {
return -1;
}
tablesize = filesize(file) / 2;
/* must be opened without a chance of reentering from FS code */
int fd = open_noiso_internal(path, O_RDONLY);
if (fd < 0)
return -1;
if (tablesize > MAX_CP_TABLE_SIZE) {
DEBUGF("Invalid codepage file: %s.cp\n", filename[table-1]);
close(file);
return 0;
}
off_t size = filesize(fd);
while (i < tablesize) {
if (!read(file, tmp, 2)) {
DEBUGF("Can't read from codepage file: %s.cp\n",
filename[table-1]);
loaded_cp_table = 0;
return 0;
if (size > 0 && size <= MAX_CP_TABLE_SIZE*2 &&
!(size % (off_t)sizeof (uint16_t))) {
/* if the buffer is provided, use that but don't alloc */
int handle = buf ? 0 : core_alloc_ex(filename, size, &ops);
if (handle > 0)
buf = core_get_data(handle);
if (buf && read(fd, buf, size) == size) {
close(fd);
cptable_tohw16(buf, size / sizeof (uint16_t));
return handle;
}
codepage_table[i++] = (tmp[1] << 8) | tmp[0];
if (handle > 0)
core_free(handle);
}
loaded_cp_table = table;
close(file);
return 1;
close(fd);
return -1;
}
/* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */
@ -205,47 +271,96 @@ unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8)
unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8,
int cp, int count)
{
unsigned short ucs, tmp;
uint16_t *table = NULL;
if (cp == -1) /* use default codepage */
cp = default_codepage;
cp_lock_enter();
if (!load_cp_table(cp)) cp = 0;
if (cp < 0 || cp >= NUM_CODEPAGES)
cp = default_cp;
int tid = cp_info[cp].tid;
while (1) {
if (tid == default_cp_tid) {
/* use default table */
if (default_cp_handle > 0) {
table = core_get_data(default_cp_handle);
default_cp_table_ref++;
}
break;
}
bool load = false;
if (tid == loaded_cp_tid) {
/* use loaded table */
if (!(cp_table_ref & CP_LOADING)) {
if (tid != CP_TID_NONE) {
table = codepage_table;
cp_table_ref++;
}
break;
}
} else if (cp_table_ref == 0) {
load = true;
cp_table_ref |= CP_LOADING;
}
/* alloc and load must be done outside the lock */
cp_lock_leave();
if (!load) {
yield();
} else if (alloc_and_load_cp_table(cp, codepage_table) < 0) {
cp = INIT_CODEPAGE; /* table may be clobbered now */
tid = cp_info[cp].tid;
}
cp_lock_enter();
if (load) {
loaded_cp_tid = tid;
cp_table_ref &= ~CP_LOADING;
}
}
cp_lock_leave();
while (count--) {
unsigned short ucs, tmp;
if (*iso < 128 || cp == UTF_8) /* Already UTF-8 */
*utf8++ = *iso++;
else {
/* cp tells us which codepage to convert from */
switch (cp) {
case ISO_8859_7: /* Greek */
case WIN_1252: /* Western European */
case WIN_1251: /* Cyrillic */
case ISO_8859_9: /* Turkish */
case ISO_8859_2: /* Latin Extended */
case WIN_1250: /* Central European */
#ifdef HAVE_LCD_BITMAP
case ISO_8859_8: /* Hebrew */
case ISO_8859_11: /* Thai */
case WIN_1256: /* Arabic */
#endif
/* tid tells us which table to use and how */
switch (tid) {
case CP_TID_ISO: /* Greek */
/* Hebrew */
/* Cyrillic */
/* Thai */
/* Arabic */
/* Turkish */
/* Latin Extended */
/* Central European */
/* Western European */
tmp = ((cp-1)*128) + (*iso++ - 128);
ucs = codepage_table[tmp];
ucs = table[tmp];
break;
#ifdef HAVE_LCD_BITMAP
case SJIS: /* Japanese */
case CP_TID_932: /* Japanese */
if (*iso > 0xA0 && *iso < 0xE0) {
tmp = *iso++ | (0xA100 - 0x8000);
ucs = codepage_table[tmp];
ucs = table[tmp];
break;
}
case GB_2312: /* Simplified Chinese */
case KSX_1001: /* Korean */
case BIG_5: /* Traditional Chinese */
case CP_TID_936: /* Simplified Chinese */
case CP_TID_949: /* Korean */
case CP_TID_950: /* Traditional Chinese */
if (count < 1 || !iso[1]) {
ucs = *iso++;
break;
@ -256,7 +371,7 @@ unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8,
tmp = *iso++ << 8;
tmp |= *iso++;
tmp -= 0x8000;
ucs = codepage_table[tmp];
ucs = table[tmp];
count--;
break;
#endif /* HAVE_LCD_BITMAP */
@ -271,6 +386,17 @@ unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8,
utf8 = utf8encode(ucs, utf8);
}
}
if (table) {
cp_lock_enter();
if (table == codepage_table) {
cp_table_ref--;
} else {
default_cp_table_ref--;
}
cp_lock_leave();
}
return utf8;
}
@ -288,7 +414,7 @@ unsigned char* utf16LEdecode(const unsigned char *utf16, unsigned char *utf8,
utf16 += 4;
count -= 2;
} else {
ucs = (utf16[0] | (utf16[1] << 8));
ucs = getle16(utf16);
utf16 += 2;
count -= 1;
}
@ -310,7 +436,7 @@ unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8,
utf16 += 4;
count -= 2;
} else {
ucs = (utf16[0] << 8) | utf16[1];
ucs = getbe16(utf16);
utf16 += 2;
count -= 1;
}
@ -400,8 +526,50 @@ const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs)
void set_codepage(int cp)
{
default_codepage = cp;
return;
if (cp < 0 || cp >= NUM_CODEPAGES)
cp = NUM_CODEPAGES;
/* load first then swap if load is successful, else just leave it; if
handle is 0 then we just free the current one; this won't happen often
thus we don't worry about reusing it and consequently avoid possible
clobbering of the existing one */
int handle = -1;
int tid = cp_info[cp].tid;
while (1) {
cp_lock_enter();
if (default_cp_tid == tid)
break;
if (handle >= 0 && default_cp_table_ref == 0) {
int hold = default_cp_handle;
default_cp_handle = handle;
handle = hold;
default_cp_tid = tid;
break;
}
/* alloc and load must be done outside the lock */
cp_lock_leave();
if (handle < 0 && (handle = alloc_and_load_cp_table(cp, NULL)) < 0)
return; /* OOM; change nothing */
yield();
}
default_cp = cp;
cp_lock_leave();
if (handle > 0)
core_free(handle);
}
int get_codepage(void)
{
return default_cp;
}
/* seek to a given char in a utf8 string and
@ -418,9 +586,16 @@ int utf8seek(const unsigned char* utf8, int offset)
return pos;
}
const char* get_codepage_name(int cp)
const char * get_codepage_name(int cp)
{
if (cp < 0 || cp>= NUM_CODEPAGES)
return name_codepages[NUM_CODEPAGES];
return name_codepages[cp];
if (cp < 0 || cp >= NUM_CODEPAGES)
cp = NUM_CODEPAGES;
return cp_info[cp].name;
}
#if 0 /* not needed just now */
void unicode_init(void)
{
cp_lock_init();
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -27,12 +27,21 @@
/* symbolic names for multiple choice configurations: */
/* CONFIG_STORAGE (note these are combineable bit-flags) */
#define STORAGE_ATA 0x01
#define STORAGE_MMC 0x02
#define STORAGE_SD 0x04
#define STORAGE_NAND 0x08
#define STORAGE_RAMDISK 0x10
#define STORAGE_HOSTFS 0x20 /* meant for APPLICATION targets (implicit for SIMULATOR) */
#define STORAGE_ATA_NUM 0
#define STORAGE_MMC_NUM 1
#define STORAGE_SD_NUM 2
#define STORAGE_NAND_NUM 3
#define STORAGE_RAMDISK_NUM 4
#define STORAGE_HOSTFS_NUM 5
#define STORAGE_NUM_TYPES 6
#define STORAGE_ATA (1 << STORAGE_ATA_NUM)
#define STORAGE_MMC (1 << STORAGE_MMC_NUM)
#define STORAGE_SD (1 << STORAGE_SD_NUM)
#define STORAGE_NAND (1 << STORAGE_NAND_NUM)
#define STORAGE_RAMDISK (1 << STORAGE_RAMDISK_NUM)
/* meant for APPLICATION targets (implicit for SIMULATOR) */
#define STORAGE_HOSTFS (1 << STORAGE_HOSTFS_NUM)
/* CONFIG_TUNER (note these are combineable bit-flags) */
#define S1A0903X01 0x01 /* Samsung */
@ -573,6 +582,8 @@ Lyre prototype 1 */
#ifdef __PCTOOL__
#undef CONFIG_CPU
#define CONFIG_CPU 0
#undef HAVE_MULTIVOLUME
#undef HAVE_MULTIDRIVE
#endif
#ifdef APPLICATION
@ -831,9 +842,11 @@ Lyre prototype 1 */
* plenty of RAM. Both features can be enabled independently. */
#if (MEMORYSIZE >= 8) && !defined(BOOTLOADER) && !defined(__PCTOOL__) \
&& !defined(APPLICATION)
#ifndef SIMULATOR
#define HAVE_DIRCACHE
#endif
#ifdef HAVE_TAGCACHE
#define HAVE_TC_RAMCACHE
//#define HAVE_TC_RAMCACHE
#endif
#endif
@ -1178,7 +1191,7 @@ Lyre prototype 1 */
/* This attribute can be used to enable to detection of plugin file handles leaks.
* When enabled, the plugin core will monitor open/close/creat and when the plugin exits
* will display an error message if the plugin leaked some file handles */
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined (SIMULATOR)
#define HAVE_PLUGIN_CHECK_OPEN_CLOSE
#endif

View File

@ -16,6 +16,9 @@
/* define this if you use an ATA controller */
#define CONFIG_STORAGE STORAGE_ATA
/* For the Gigabeat S, we mount the second partition */
#define CONFIG_DEFAULT_PARTNUM 1
/*define this if the ATA controller and method of USB access support LBA48 */
#define HAVE_LBA48

View File

@ -24,7 +24,8 @@
#include "config.h"
#include "mv.h" /* for volume definitions */
struct partinfo {
struct partinfo
{
unsigned long start; /* first sector (LBA) */
unsigned long size; /* number of sectors */
unsigned char type;
@ -35,11 +36,9 @@ struct partinfo {
#define PARTITION_TYPE_FAT16 0x06
#define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84
/* returns a pointer to an array of 8 partinfo structs */
struct partinfo* disk_init(IF_MD_NONVOID(int drive));
struct partinfo* disk_partinfo(int partition);
bool disk_init(IF_MD_NONVOID(int drive));
bool disk_partinfo(int partition, struct partinfo *info);
void disk_init_subsystem(void) INIT_ATTR; /* Initialises mutexes */
int disk_mount_all(void); /* returns the # of successful mounts */
int disk_mount(int drive);
int disk_unmount_all(void);
@ -50,4 +49,6 @@ int disk_unmount(int drive);
int disk_get_sector_multiplier(IF_MD_NONVOID(int drive));
#endif
#endif
bool disk_present(IF_MD_NONVOID(int drive));
#endif /* _DISK_H_ */

View File

@ -18,123 +18,168 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef FAT_H
#define FAT_H
#include <stdbool.h>
#include "mv.h" /* for volume definitions */
#include <sys/types.h>
#include <time.h>
#include "config.h"
#include "system.h"
#include "mv.h" /* for volume definitions */
/* This value can be overwritten by a target in config-[target].h, but
that behaviour is still experimental */
/********
**** DO NOT use these functions directly unless otherwise noted. Required
**** synchronization is done by higher-level interfaces to minimize locking
**** overhead.
****
**** Volume, drive, string, etc. parameters should also be checked by
**** callers for gross violations-- NULL strings, out-of-bounds values,
**** etc.
****/
/****************************************************************************
** Values that can be overridden by a target in config-[target].h
**/
/* if your ATA implementation can do better, go right ahead and increase this
* value */
#ifndef FAT_MAX_TRANSFER_SIZE
#define FAT_MAX_TRANSFER_SIZE 256
#endif
/* still experimental? */
/* increasing this will increase the total memory used by the cache; the
cache, as noted in disk_cache.h, has other minimum requirements that may
prevent reducing its number of entries in order to compensate */
#ifndef SECTOR_SIZE
#define SECTOR_SIZE 512
#endif
/**
****************************************************************************/
#define INVALID_SECNUM (0xfffffffeul) /* sequential, not FAT */
#define FAT_MAX_FILE_SIZE (0xfffffffful) /* 2^32-1 bytes */
#define MAX_DIRENTRIES 65536
#define MAX_DIRECTORY_SIZE (MAX_DIRENTRIES*32) /* 2MB max size */
/* these aren't I/O error conditions, so define specially; failure rc's
* shouldn't return the last digit as "0", therefore this is unambiguous */
#define FAT_RC_ENOSPC (-10)
#define FAT_SEEK_EOF (-20)
/* Number of bytes reserved for a file name (including the trailing \0).
Since names are stored in the entry as UTF-8, we won't be able to
Since names are stored in the entry as UTF-8, we won't be ble to
store all names allowed by FAT. In FAT, a name can have max 255
characters (not bytes!). Since the UTF-8 encoding of a char may take
up to 4 bytes, there will be names that we won't be able to store
completely. For such names, the short DOS name is used. */
#define FAT_FILENAME_BYTES 256
#define FAT_DIRENTRY_NAME_MAX 255
struct fat_direntry
{
unsigned char name[FAT_FILENAME_BYTES]; /* UTF-8 encoded name plus \0 */
unsigned short attr; /* Attributes */
unsigned char crttimetenth; /* Millisecond creation
time stamp (0-199) */
unsigned short crttime; /* Creation time */
unsigned short crtdate; /* Creation date */
unsigned short lstaccdate; /* Last access date */
unsigned short wrttime; /* Last write time */
unsigned short wrtdate; /* Last write date */
unsigned long filesize; /* File size in bytes */
long firstcluster; /* fstclusterhi<<16 + fstcluslo */
union {
uint8_t name[255+1+4]; /* UTF-8 name plus \0 plus parse slop */
uint16_t ucssegs[5+20][13]; /* UTF-16 segment buffer - layout saves... */
}; /* ...130 bytes (important if stacked) */
uint16_t ucsterm; /* allow one NULL-term after ucssegs */
uint8_t shortname[13]; /* DOS filename (OEM charset) */
uint8_t attr; /* file attributes */
uint8_t crttimetenth; /* millisecond creation time stamp (0-199) */
uint16_t crttime; /* creation time */
uint16_t crtdate; /* creation date */
uint16_t lstaccdate; /* last access date */
uint16_t wrttime; /* last write time */
uint16_t wrtdate; /* last write date */
uint32_t filesize; /* file size in bytes */
int32_t firstcluster; /* first FAT cluster of file, 0 if empty */
};
#define FAT_ATTR_READ_ONLY 0x01
#define FAT_ATTR_HIDDEN 0x02
#define FAT_ATTR_SYSTEM 0x04
#define FAT_ATTR_VOLUME_ID 0x08
#define FAT_ATTR_DIRECTORY 0x10
#define FAT_ATTR_ARCHIVE 0x20
#define FAT_ATTR_VOLUME 0x40 /* this is a volume, not a real directory */
/* cursor structure used for scanning directories; holds the last-returned
entry information */
struct fat_dirscan_info
{
unsigned int entry; /* short dir entry index in parent */
unsigned int entries; /* number of dir entries used */
};
#define FAT_RW_VAL (0u - 1)
/* basic FAT file information about where to find a file and who houses it */
struct fat_file
{
long firstcluster; /* first cluster in file */
long lastcluster; /* cluster of last access */
long lastsector; /* sector of last access */
long clusternum; /* current clusternum */
long sectornum; /* sector number in this cluster */
unsigned int direntry; /* short dir entry index from start of dir */
unsigned int direntries; /* number of dir entries used by this file */
long dircluster; /* first cluster of dir */
bool eof;
#ifdef HAVE_MULTIVOLUME
int volume; /* file resides on which volume */
int volume; /* file resides on which volume (first!) */
#endif
long firstcluster; /* first cluster in file */
long dircluster; /* first cluster of parent directory */
struct fat_dirscan_info e; /* entry information */
};
struct fat_dir
/* this stores what was last accessed when read or writing a file's data */
struct fat_filestr
{
unsigned char sectorcache[SECTOR_SIZE] CACHEALIGN_ATTR;
unsigned int entry;
unsigned int entrycount;
long sector;
struct fat_file file;
/* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are
* at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most
* 13 characters, that a total of 260 characters. So we keep a buffer of that size.
* Keep coherent with fat.c code. */
unsigned char longname[260 * 2];
} CACHEALIGN_ATTR;
struct fat_file *fatfilep; /* common file information */
long lastcluster; /* cluster of last access */
unsigned long lastsector; /* sector of last access */
long clusternum; /* cluster number of last access */
unsigned long sectornum; /* sector number within current cluster */
bool eof; /* end-of-file reached */
};
#ifdef HAVE_HOTSWAP
extern void fat_lock(void);
extern void fat_unlock(void);
#endif
/** File entity functions **/
int fat_create_file(struct fat_file *parent, const char *name,
uint8_t attr, struct fat_file *file,
struct fat_direntry *fatent);
bool fat_dir_is_parent(const struct fat_file *dir, const struct fat_file *file);
bool fat_file_is_same(const struct fat_file *file1, const struct fat_file *file2);
int fat_fstat(struct fat_file *file, struct fat_direntry *entry);
int fat_open(const struct fat_file *parent, long startcluster,
struct fat_file *file);
int fat_open_rootdir(IF_MV(int volume,) struct fat_file *dir);
enum fat_remove_op /* what should fat_remove(), remove? */
{
FAT_RM_DIRENTRIES = 0x1, /* remove only directory entries */
FAT_RM_DATA = 0x2, /* remove only file data */
FAT_RM_ALL = 0x3, /* remove all of above */
};
int fat_remove(struct fat_file *file, enum fat_remove_op what);
int fat_rename(struct fat_file *parent, struct fat_file *file,
const unsigned char *newname);
extern void fat_init(void);
extern int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume));
extern int fat_mount(IF_MV(int volume,) IF_MD(int drive,) long startsector);
extern int fat_unmount(int volume, bool flush);
extern void fat_size(IF_MV(int volume,) /* public for info */
unsigned long* size,
unsigned long* free);
extern void fat_recalc_free(IF_MV_NONVOID(int volume)); /* public for debug info screen */
extern int fat_create_dir(const char* name,
struct fat_dir* newdir,
struct fat_dir* dir);
extern int fat_open(IF_MV(int volume,)
long cluster,
struct fat_file* ent,
const struct fat_dir* dir);
extern int fat_create_file(const char* name,
struct fat_file* ent,
struct fat_dir* dir);
extern long fat_readwrite(struct fat_file *ent, long sectorcount,
void* buf, bool write );
extern int fat_closewrite(struct fat_file *ent, long size, int attr);
extern int fat_seek(struct fat_file *ent, unsigned long sector );
extern int fat_remove(struct fat_file *ent);
extern int fat_truncate(const struct fat_file *ent);
extern int fat_rename(struct fat_file* file,
struct fat_dir* dir,
const unsigned char* newname,
long size, int attr);
/** File stream functions **/
int fat_closewrite(struct fat_filestr *filestr, uint32_t size,
struct fat_direntry *fatentp);
void fat_filestr_init(struct fat_filestr *filestr, struct fat_file *file);
unsigned long fat_query_sectornum(const struct fat_filestr *filestr);
long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount,
void *buf, bool write);
void fat_rewind(struct fat_filestr *filestr);
int fat_seek(struct fat_filestr *filestr, unsigned long sector);
int fat_truncate(const struct fat_filestr *filestr);
extern int fat_opendir(IF_MV(int volume,)
struct fat_dir *ent, unsigned long startcluster,
const struct fat_dir *parent_dir);
extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry);
extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */
extern bool fat_ismounted(int volume);
extern void* fat_get_sector_buffer(void);
extern void fat_release_sector_buffer(void);
/** Directory stream functions **/
struct filestr_cache;
int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan,
struct filestr_cache *cachep, struct fat_direntry *entry);
void fat_rewinddir(struct fat_dirscan_info *scan);
#endif
/** Mounting and unmounting functions **/
bool fat_ismounted(IF_MV_NONVOID(int volume));
int fat_mount(IF_MV(int volume,) IF_MD(int drive,) unsigned long startsector);
int fat_unmount(IF_MV_NONVOID(int volume));
/** Debug screen stuff **/
#ifdef MAX_LOG_SECTOR_SIZE
int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume));
#endif /* MAX_LOG_SECTOR_SIZE */
unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume));
void fat_recalc_free(IF_MV_NONVOID(int volume));
bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free);
/** Misc. **/
time_t fattime_mktime(uint16_t fatdate, uint16_t fattime);
void fat_empty_fat_direntry(struct fat_direntry *entry);
void fat_init(void);
#endif /* FAT_H */

View File

@ -41,4 +41,29 @@ extern bool hostfs_removable(int drive);
extern bool hostfs_present(int drive);
#endif
/* This has to be repeated here for now for sim's sake since HAVE_HOSTFS
eats all the other stuff in storage.h. The sim probably shouldn't use
this. */
#ifdef CONFIG_STORAGE_MULTI
extern int hostfs_driver_type(int drive);
#else
# ifdef APPLICATION
# define hostfs_driver_type(drive) (STORAGE_HOSTFS_NUM)
# else /* !APPLICATION */
# if (CONFIG_STORAGE & STORAGE_ATA)
# define hostfs_driver_type(drive) (STORAGE_ATA_NUM)
# elif (CONFIG_STORAGE & STORAGE_SD)
# define hostfs_driver_type(drive) (STORAGE_SD_NUM)
# elif (CONFIG_STORAGE & STORAGE_MMC)
# define hostfs_driver_type(drive) (STORAGE_MMC_NUM)
# elif (CONFIG_STORAGE & STORAGE_NAND)
# define hostfs_driver_type(drive) (STORAGE_NAND_NUM)
# elif (CONFIG_STORAGE & STORAGE_RAMDISK)
# define hostfs_driver_type(drive) (STORAGE_RAMDISK_NUM)
# else
# error Unknown storage driver
# endif /* CONFIG_STORAGE */
# endif /* APPLICATION */
#endif /* CONFIG_STORAGE_MULTI */
#endif /* HOSTFS_H */

View File

@ -45,7 +45,17 @@ static inline void lc_close(void *handle) { (void)handle; }
#elif (CONFIG_PLATFORM & PLATFORM_HOSTED)
#ifdef APPLICATION
/* App doesn't simulate code loading from a buffer */
static inline void * lc_open_from_mem(void *addr, size_t blob_size)
{
return NULL;
(void)addr; (void)blob_size;
}
#else
extern void *lc_open_from_mem(void* addr, size_t blob_size);
#endif
extern void *lc_get_header(void *handle);
extern void lc_close(void *handle);

View File

@ -45,23 +45,44 @@
#define IF_MV(x...) x /* valist contents or empty */
#define IF_MV_NONVOID(x...) x /* valist contents or 'void' */
#define IF_MV_VOL(v) v /* volume argument or '0' */
/* how to name volumes, first char must be outside of legal file names,
a number gets appended to enumerate, if applicable */
#if (CONFIG_STORAGE & STORAGE_MMC)
#define VOL_NAMES "<MMC%d>"
#define VOL_ENUM_POS 4 /* position of %d, to avoid runtime calculation */
#elif (CONFIG_STORAGE & STORAGE_SD)
#define VOL_NAMES "<microSD%d>"
#define VOL_ENUM_POS 8 /* position of %d, to avoid runtime calculation */
#else
#define VOL_NAMES "<HD%d>"
#define VOL_ENUM_POS 3
#endif /* CONFIG_STORAGE */
#ifdef HAVE_HOTSWAP
bool volume_removable(int volume);
bool volume_present(int volume);
/* Format: "/<DEC###>/foo/bar"
* The "DEC" is pure decoration and treated as a comment. Only an unbroken
* trailing string of digits within the brackets is parsed as the volume
* number.
*
* IMPORTANT!: Adjust VOL_DEC_MAX_LEN if needed to the longest of these
*/
#define DEFAULT_VOL_DEC "Volume"
#if (CONFIG_STORAGE & STORAGE_ATA)
#define ATA_VOL_DEC "HDD"
#endif
#if (CONFIG_STORAGE & STORAGE_MMC)
#define MMC_VOL_DEC "MMC"
#endif
#if (CONFIG_STORAGE & STORAGE_SD)
#define SD_VOL_DEC "microSD"
#endif
#if (CONFIG_STORAGE & STORAGE_NAND)
#define NAND_VOL_DEC "NAND"
#endif
#if (CONFIG_STORAGE & STORAGE_RAMDISK)
#define RAMDISK_VOL_DEC "RAMDisk"
#endif
#if (CONFIG_STORAGE & STORAGE_HOSTFS)
#ifndef HOSTFS_VOL_DEC /* overridable */
#define HOSTFS_VOL_DEC DEFAULT_VOL_DEC
#endif
#endif
/* Characters that delimit a volume specifier at any root point in the path.
The tokens must be outside of legal filename characters */
#define VOL_START_TOK '<'
#define VOL_END_TOK '>'
#define VOL_DEC_MAX_LEN 7 /* biggest of all xxx_VOL_DEC defines */
#define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1)
#define VOL_NUM_MAX 100
#else /* empty definitions if no multi-volume */
#define IF_MV(x...)
@ -69,4 +90,30 @@ bool volume_present(int volume);
#define IF_MV_VOL(v) 0
#endif /* HAVE_MULTIVOLUME */
#define CHECK_VOL(volume) \
((unsigned int)IF_MV_VOL(volume) < NUM_VOLUMES)
#define CHECK_DRV(drive) \
((unsigned int)IF_MD_DRV(drive) < NUM_DRIVES)
/* Volume-centric functions (in disk.c) */
void volume_recalc_free(IF_MV_NONVOID(int volume));
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume));
void volume_size(IF_MV(int volume,) unsigned long *size, unsigned long *free);
bool volume_ismounted(IF_MV_NONVOID(int volume));
#ifdef HAVE_HOTSWAP
bool volume_removable(int volume);
bool volume_present(int volume);
#endif /* HAVE_HOTSWAP */
#ifdef HAVE_MULTIDRIVE
int volume_drive(int volume);
#else /* !HAVE_MULTIDRIVE */
static inline int volume_drive(int volume)
{
return 0;
(void)volume;
}
#endif /* HAVE_MULTIDRIVE */
#endif /* __MV_H__ */

100
firmware/export/pathfuncs.h Normal file
View File

@ -0,0 +1,100 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _PATHFUNCS_H_
#define _PATHFUNCS_H_
#include <sys/types.h>
#define __need_size_t
#include <stddef.h>
#include <stdbool.h>
#include "config.h"
/* useful char constants that could be reconfigured if desired */
#define PATH_SEPCH '/'
#define PATH_SEPSTR "/"
#define PATH_ROOTSTR "/"
#define PATH_BADSEPCH '\\'
#define PATH_DRVSEPCH ':'
/* a nicer way to check for "." and ".." than two strcmp() calls */
static inline bool is_dotdir_name(const char *name)
{
return name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]));
}
static inline bool name_is_dot(const char *name)
{
return name[0] == '.' && !name[1];
}
static inline bool name_is_dot_dot(const char *name)
{
return name[0] == '.' && name[1] == '.' && !name[2];
}
/* return a pointer to the character following path separators */
#define GOBBLE_PATH_SEPCH(p) \
({ int _c; \
const char *_p = (p); \
while ((_c = *_p) == PATH_SEPCH) \
++_p; \
_p; })
/* return a pointer to the character following a path component which may
be a separator or the terminating nul */
#define GOBBLE_PATH_COMP(p) \
({ int _c; \
const char *_p = (p); \
while ((_c = *_p) && _c != PATH_SEPCH) \
++_p; \
_p; })
/* does the character terminate a path component? */
#define IS_COMP_TERMINATOR(c) \
({ int _c = (c); \
!_c || _c == PATH_SEPCH; })
#ifdef HAVE_MULTIVOLUME
int path_strip_volume(const char *name, const char **nameptr, bool greedy);
int get_volume_name(int volume, char *name);
#endif
int path_strip_drive(const char *name, const char **nameptr, bool greedy);
size_t path_trim_whitespace(const char *name, const char **nameptr);
size_t path_basename(const char *name, const char **nameptr);
size_t path_dirname(const char *name, const char **nameptr);
size_t path_strip_trailing_separators(const char *name, const char **nameptr);
void path_correct_separators(char *dstpath, const char *path);
/* constants useable in basepath and component */
#define PA_SEP_HARD NULL /* separate even if base is empty */
#define PA_SEP_SOFT "" /* separate only if base is nonempty */
size_t path_append(char *buffer, const char *basepath, const char *component,
size_t bufsize);
ssize_t parse_path_component(const char **pathp, const char **namep);
/* return true if path begins with a root '/' component and is not NULL */
static inline bool path_is_absolute(const char *path)
{
return path && path[0] == PATH_SEPCH;
}
#endif /* _PATHFUNCS_H_ */

View File

@ -70,27 +70,6 @@
#define HOME_DIR_LEN (sizeof(HOME_DIR)-1)
#ifdef APPLICATION
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
int app_open(const char *name, int o, ...);
int app_creat(const char* name, mode_t mode);
int app_remove(const char *name);
int app_rename(const char *old, const char *new);
DIR* app_opendir(const char *_name);
int app_closedir(DIR *dir);
struct dirent* app_readdir(DIR* dir);
int app_mkdir(const char* name);
int app_rmdir(const char* name);
ssize_t app_readlink(const char *path, char *buf, size_t bufsiz);
extern void paths_init(void);
#endif
#define REC_BASE_DIR HOME_DIR
#define PLAYLIST_CATALOG_DEFAULT_DIR HOME_DIR "/Playlists"

View File

@ -93,6 +93,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; }
#define storage_removable(drive) hostfs_removable(IF_MD(drive))
#define storage_present(drive) hostfs_present(IF_MD(drive))
#endif
#define storage_driver_type(drive) hostfs_driver_type(IF_MV(drive))
#elif (CONFIG_STORAGE & STORAGE_ATA)
#define STORAGE_FUNCTION(NAME) (ata_## NAME)
#define storage_spindown ata_spindown
@ -119,6 +120,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; }
#define storage_removable(drive) ata_removable(IF_MD(drive))
#define storage_present(drive) ata_present(IF_MD(drive))
#endif
#define storage_driver_type(drive) (STORAGE_ATA_NUM)
#elif (CONFIG_STORAGE & STORAGE_SD)
#define STORAGE_FUNCTION(NAME) (sd_## NAME)
#define storage_spindown sd_spindown
@ -145,6 +147,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; }
#define storage_removable(drive) sd_removable(IF_MD(drive))
#define storage_present(drive) sd_present(IF_MD(drive))
#endif
#define storage_driver_type(drive) (STORAGE_SD_NUM)
#elif (CONFIG_STORAGE & STORAGE_MMC)
#define STORAGE_FUNCTION(NAME) (mmc_## NAME)
#define storage_spindown mmc_spindown
@ -170,6 +173,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; }
#define storage_removable(drive) mmc_removable(IF_MD(drive))
#define storage_present(drive) mmc_present(IF_MD(drive))
#endif
#define storage_driver_type(drive) (STORAGE_MMC_NUM)
#elif (CONFIG_STORAGE & STORAGE_NAND)
#define STORAGE_FUNCTION(NAME) (nand_## NAME)
#define storage_spindown nand_spindown
@ -195,6 +199,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; }
#define storage_removable(drive) nand_removable(IF_MD(drive))
#define storage_present(drive) nand_present(IF_MD(drive))
#endif
#define storage_driver_type(drive) (STORAGE_NAND_NUM)
#elif (CONFIG_STORAGE & STORAGE_RAMDISK)
#define STORAGE_FUNCTION(NAME) (ramdisk_## NAME)
#define storage_spindown ramdisk_spindown
@ -220,6 +225,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; }
#define storage_removable(drive) ramdisk_removable(IF_MD(drive))
#define storage_present(drive) ramdisk_present(IF_MD(drive))
#endif
#define storage_driver_type(drive) (STORAGE_RAMDISK_NUM)
#else
//#error No storage driver!
#endif
@ -246,6 +252,7 @@ void storage_get_info(int drive, struct storage_info *info);
bool storage_removable(int drive);
bool storage_present(int drive);
#endif
int storage_driver_type(int drive);
#endif /* NOT CONFIG_STORAGE_MULTI and NOT SIMULATOR*/

View File

@ -104,6 +104,10 @@ int get_cpu_boost_counter(void);
/* return number of elements in array a */
#define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0]))
/* is the given pointer "p" inside the said bounds of array "a"? */
#define PTR_IN_ARRAY(a, p, numelem) \
((uintptr_t)(p) - (uintptr_t)(a) < (uintptr_t)(numelem)*sizeof ((a)[0]))
/* return p incremented by specified number of bytes */
#define SKIPBYTES(p, count) ((typeof (p))((char *)(p) + (count)))
@ -188,9 +192,13 @@ enum {
#include "system-target.h"
#elif defined(HAVE_SDL) /* SDL build */
#include "system-sdl.h"
#elif defined(__PCTOOL__)
#include "system-sdl.h"
#ifdef SIMULATOR
#include "system-sim.h"
#endif
#elif defined(__PCTOOL__)
#include "system-hosted.h"
#endif
#include "bitswap.h"
#include "rbendian.h"

View File

@ -18,46 +18,71 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _DIR_H_
#define _DIR_H_
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include "config.h"
#include "fs_attr.h"
#define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02
#define ATTR_SYSTEM 0x04
#define ATTR_VOLUME_ID 0x08
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20
#define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */
#define ATTR_LINK 0x80
#ifdef HAVE_DIRCACHE
# include "dircache.h"
# define DIR DIR_CACHED
# define dirent dirent_cached
# define opendir opendir_cached
# define closedir closedir_cached
# define readdir readdir_cached
# define closedir closedir_cached
# define mkdir mkdir_cached
# define rmdir rmdir_cached
#if defined (APPLICATION)
#include "filesystem-app.h"
#elif defined(SIMULATOR) || defined(__PCTOOL__)
#include "../../uisimulator/common/filesystem-sim.h"
#else
# include "dir_uncached.h"
# define DIR DIR_UNCACHED
# define dirent dirent_uncached
# define opendir opendir_uncached
# define closedir closedir_uncached
# define readdir readdir_uncached
# define closedir closedir_uncached
# define mkdir mkdir_uncached
# define rmdir rmdir_uncached
#include "filesystem-native.h"
#endif
typedef DIR* (*opendir_func)(const char* name);
typedef int (*closedir_func)(DIR* dir);
typedef struct dirent* (*readdir_func)(DIR* dir);
#ifndef DIRFUNCTIONS_DEFINED
#ifndef opendir
#define opendir FS_PREFIX(opendir)
#endif
#ifndef readdir
#define readdir FS_PREFIX(readdir)
#endif
#ifndef readdir_r
#define readdir_r FS_PREFIX(readdir_r)
#endif
#ifndef rewinddir
#define rewinddir FS_PREFIX(rewinddir)
#endif
#ifndef closedir
#define closedir FS_PREFIX(closedir)
#endif
#ifndef mkdir
#define mkdir FS_PREFIX(mkdir)
#endif
#ifndef rmdir
#define rmdir FS_PREFIX(rmdir)
#endif
#ifndef samedir
#define samedir FS_PREFIX(samedir)
#endif
#ifndef dir_exists
#define dir_exists FS_PREFIX(dir_exists)
#endif
#endif /* !DIRFUNCTIONS_DEFINED */
#ifndef DIRENT_DEFINED
struct DIRENT
{
struct dirinfo_native info; /* platform extra info */
char d_name[MAX_PATH]; /* UTF-8 name of entry (last!) */
};
#endif /* DIRENT_DEFINED */
struct dirinfo
{
unsigned int attribute; /* attribute bits of file */
off_t size; /* binary size of file */
time_t mtime; /* local file time */
};
#ifndef DIRFUNCTIONS_DECLARED
/* TIP: set errno to zero before calling to see if anything failed */
struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry);
#endif /* !DIRFUNCTIONS_DECLARED */
#endif /* _DIR_H_ */

View File

@ -1,107 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: dir.h 13741 2007-06-30 02:08:27Z jethead71 $
*
* Copyright (C) 2002 by Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _DIR_UNCACHED_H_
#define _DIR_UNCACHED_H_
#include "config.h"
struct dirinfo {
int attribute;
long size;
unsigned short wrtdate;
unsigned short wrttime;
};
#include <stdbool.h>
#include "file.h"
#if defined(SIMULATOR) || defined(__PCTOOL__)
# define dirent_uncached sim_dirent
# define DIR_UNCACHED SIM_DIR
# define opendir_uncached sim_opendir
# define readdir_uncached sim_readdir
# define closedir_uncached sim_closedir
# define mkdir_uncached sim_mkdir
# define rmdir_uncached sim_rmdir
#elif defined(APPLICATION)
# include "rbpaths.h"
# define DIRENT_DEFINED
# define DIR_DEFINED
# define dirent_uncached dirent
# define DIR_UNCACHED DIR
# define opendir_uncached app_opendir
# define readdir_uncached app_readdir
# define closedir_uncached app_closedir
# define mkdir_uncached app_mkdir
# define rmdir_uncached app_rmdir
#endif
#ifndef DIRENT_DEFINED
struct dirent_uncached {
unsigned char d_name[MAX_PATH];
struct dirinfo info;
long startcluster;
};
#endif
#include "fat.h"
#ifndef DIR_DEFINED
typedef struct {
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
struct fat_dir fatdir CACHEALIGN_ATTR;
bool busy;
long startcluster;
struct dirent_uncached theent;
#ifdef HAVE_MULTIVOLUME
int volumecounter; /* running counter for faked volume entries */
#endif
#else
/* simulator/application: */
void *dir; /* actually a DIR* dir */
char *name;
#endif
} DIR_UNCACHED CACHEALIGN_ATTR;
#endif
#ifdef HAVE_HOTSWAP
char *get_volume_name(int volume);
#endif
#ifdef HAVE_MULTIVOLUME
int strip_volume(const char*, char*);
#endif
#ifndef DIRFUNCTIONS_DEFINED
extern DIR_UNCACHED* opendir_uncached(const char* name);
extern int closedir_uncached(DIR_UNCACHED* dir);
extern int mkdir_uncached(const char* name);
extern int rmdir_uncached(const char* name);
extern struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir);
extern int release_dirs(int volume);
#endif /* DIRFUNCTIONS_DEFINED */
#endif

View File

@ -8,6 +8,7 @@
* $Id$
*
* Copyright (C) 2005 by Miika Pekkarinen
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -21,84 +22,154 @@
#ifndef _DIRCACHE_H
#define _DIRCACHE_H
#include "config.h"
#include "dir_uncached.h"
#include <string.h> /* size_t */
#include "mv.h"
#include <string.h> /* size_t */
#include <sys/types.h> /* ssize_t */
#ifdef HAVE_DIRCACHE
#define DIRCACHE_RESERVE (1024*64)
#define DIRCACHE_LIMIT (1024*1024*6)
/****************************************************************************
** Configurable values
**/
#define DIRCACHE_APPFLAG_TAGCACHE 0x0001
#define DIRCACHE_APPFLAG_PLAYLIST 0x0002
/* Internal structures. */
struct travel_data {
struct dircache_entry *first;
struct dircache_entry *ce;
struct dircache_entry *down_entry;
#if (CONFIG_PLATFORM & PLATFORM_HOSTED)
DIR_UNCACHED *dir, *newdir;
struct dirent_uncached *entry;
#else
struct fat_dir *dir;
struct fat_dir newdir;
struct fat_direntry entry;
#if 0
/* enable dumping code */
#define DIRCACHE_DUMPSTER
#define DIRCACHE_DUMPSTER_BIN "/dircache_dump.bin"
#define DIRCACHE_DUMPSTER_CSV "/dircache_dump.csv"
#endif
int pathpos;
/* dircache builds won't search below this but will work down to this point
while below it the cache will just pass requests through to the storage;
the limiting factor is the scanning thread stack size, not the
implementation -- tune the two together */
#define DIRCACHE_MAX_DEPTH 15
#define DIRCACHE_STACK_SIZE (DEFAULT_STACK_SIZE + 0x100)
/* memory buffer constants that control allocation */
#define DIRCACHE_RESERVE (1024*64) /* 64 KB - new entry slack */
#define DIRCACHE_MIN (1024*1024*1) /* 1 MB - provision min size */
#define DIRCACHE_LIMIT (1024*1024*6) /* 6 MB - provision max size */
/* make it easy to change serialnumber size without modifying anything else;
32 bits allows 21845 builds before wrapping in a 6MB cache that is filled
exclusively with entries and nothing else (32 byte entries), making that
figure pessimistic */
typedef uint32_t dc_serial_t;
/**
****************************************************************************/
#if CONFIG_PLATFORM & PLATFORM_NATIVE
/* native dircache is lower-level than on a hosted target */
#define DIRCACHE_NATIVE
#endif
struct dircache_file
{
int idx; /* this file's cache index */
dc_serial_t serialnum; /* this file's serial number */
};
struct dirent_cached {
struct dirinfo info;
char *d_name;
long startcluster;
enum dircache_status
{
DIRCACHE_IDLE = 0, /* no volume is initialized */
DIRCACHE_SCANNING = 1, /* dircache is scanning a volume */
DIRCACHE_READY = 2, /* dircache is ready to be used */
};
typedef struct {
bool busy;
struct dirent_cached theent; /* .attribute is set to -1 on init(opendir) */
int internal_entry; /* the current entry in the directory */
DIR_UNCACHED *regulardir;
} DIR_CACHED;
/** Dircache control **/
void dircache_wait(void);
void dircache_suspend(void);
int dircache_resume(void);
int dircache_enable(void);
void dircache_disable(void);
void dircache_free_buffer(void);
void dircache_init(void) INIT_ATTR;
/** Volume mounting **/
void dircache_mount(void); /* always tries building everything it can */
void dircache_unmount(IF_MV_NONVOID(int volume));
/** File API service functions **/
/* avoid forcing #include of file_internal.h, fat.h and dir.h */
struct filestr_base;
struct file_base_info;
struct file_base_binding;
struct dirent;
struct dirscan_info;
struct dirinfo_native;
int dircache_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
struct dirent *entry);
void dircache_rewinddir_dirent(struct dirscan_info *scanp);
#ifdef DIRCACHE_NATIVE
struct fat_direntry;
int dircache_readdir_internal(struct filestr_base *stream,
struct file_base_info *infop,
struct fat_direntry *fatent);
void dircache_rewinddir_internal(struct file_base_info *info);
#endif /* DIRCACHE_NATIVE */
/** Dircache live updating **/
void dircache_get_rootinfo(struct file_base_info *infop);
void dircache_bind_file(struct file_base_binding *bindp);
void dircache_unbind_file(struct file_base_binding *bindp);
void dircache_fileop_create(struct file_base_info *dirinfop,
struct file_base_binding *bindp,
const char *basename,
const struct dirinfo_native *dinp);
void dircache_fileop_rename(struct file_base_info *dirinfop,
struct file_base_binding *bindp,
const char *basename);
void dircache_fileop_remove(struct file_base_binding *bindp);
void dircache_fileop_sync(struct file_base_binding *infop,
const struct dirinfo_native *dinp);
/** Dircache paths and files **/
ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf,
size_t size);
int dircache_get_file(const char *path, struct dircache_file *dcfilep);
/** Debug screen/info stuff **/
struct dircache_info
{
enum dircache_status status; /* current composite status value */
const char *statusdesc; /* pointer to string describing 'status' */
size_t last_size; /* cache size after last build */
size_t size; /* total size of entries (with holes) */
size_t sizeused; /* bytes of 'size' actually utilized */
size_t size_limit; /* maximum possible size */
size_t reserve; /* size of reserve area */
size_t reserve_used; /* amount of reserve used */
unsigned int entry_count; /* number of cache entries */
long build_ticks; /* total time used to build cache */
};
void dircache_get_info(struct dircache_info *info);
#ifdef DIRCACHE_DUMPSTER
void dircache_dump(void);
#endif /* DIRCACHE_DUMPSTER */
/** Misc. stuff **/
void dircache_dcfile_init(struct dircache_file *dcfilep);
#ifdef HAVE_EEPROM_SETTINGS
int dircache_load(void);
int dircache_save(void);
int dircache_build(int last_size);
void* dircache_steal_buffer(size_t *size);
bool dircache_is_enabled(void);
bool dircache_is_initializing(void);
void dircache_set_appflag(long mask);
bool dircache_get_appflag(long mask);
int dircache_get_entry_count(void);
int dircache_get_cache_size(void);
int dircache_get_reserve_used(void);
int dircache_get_build_ticks(void);
void dircache_disable(void);
void dircache_suspend(void);
bool dircache_resume(void);
int dircache_get_entry_id(const char *filename);
size_t dircache_copy_path(int index, char *buf, size_t size);
#endif /* HAVE_EEPROM_SETTINGS */
/* the next two are internal for file.c */
long _dircache_get_entry_startcluster(int id);
struct dirinfo* _dircache_get_entry_dirinfo(int id);
void dircache_init(size_t last_size) INIT_ATTR;
void dircache_bind(int fd, const char *path);
void dircache_update_filesize(int fd, long newsize, long startcluster);
void dircache_update_filetime(int fd);
void dircache_mkdir(const char *path);
void dircache_rmdir(const char *path);
void dircache_remove(const char *name);
void dircache_rename(const char *oldpath, const char *newpath);
void dircache_add_file(const char *path, long startcluster);
#endif /* HAVE_DIRCACHE */
DIR_CACHED* opendir_cached(const char* name);
struct dirent_cached* readdir_cached(DIR_CACHED* dir);
int closedir_cached(DIR_CACHED *dir);
int mkdir_cached(const char *name);
int rmdir_cached(const char* name);
#endif /* !HAVE_DIRCACHE */
#endif
#endif /* _DIRCACHE_H */

View File

@ -0,0 +1,198 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _DIRCACHE_REDIRECT_H_
#include "dir.h"
/***
** Internal redirects that depend upon whether or not dircache is made
**/
/** File binding **/
static inline void get_rootinfo_internal(struct file_base_info *infop)
{
#ifdef HAVE_DIRCACHE
dircache_get_rootinfo(infop);
#else
(void)infop;
#endif
}
static inline void fileobj_bind_file(struct file_base_binding *bindp)
{
#ifdef HAVE_DIRCACHE
dircache_bind_file(bindp);
#else
file_binding_insert_last(bindp);
#endif
}
static inline void fileobj_unbind_file(struct file_base_binding *bindp)
{
#ifdef HAVE_DIRCACHE
dircache_unbind_file(bindp);
#else
file_binding_remove(bindp);
#endif
}
/** File event handlers **/
static inline void fileop_onopen_internal(struct filestr_base *stream,
struct file_base_info *srcinfop,
unsigned int callflags)
{
fileobj_fileop_open(stream, srcinfop, callflags);
}
static inline void fileop_onclose_internal(struct filestr_base *stream)
{
fileobj_fileop_close(stream);
}
static inline void fileop_oncreate_internal(struct filestr_base *stream,
struct file_base_info *srcinfop,
unsigned int callflags,
struct file_base_info *dirinfop,
const char *basename)
{
#ifdef HAVE_DIRCACHE
dircache_dcfile_init(&srcinfop->dcfile);
#endif
fileobj_fileop_create(stream, srcinfop, callflags);
#ifdef HAVE_DIRCACHE
struct dirinfo_native din;
fill_dirinfo_native(&din);
dircache_fileop_create(dirinfop, stream->bindp, basename, &din);
#endif
(void)dirinfop; (void)basename;
}
static inline void fileop_onremove_internal(struct filestr_base *stream,
struct file_base_info *oldinfop)
{
fileobj_fileop_remove(stream, oldinfop);
#ifdef HAVE_DIRCACHE
dircache_fileop_remove(stream->bindp);
#endif
}
static inline void fileop_onrename_internal(struct filestr_base *stream,
struct file_base_info *oldinfop,
struct file_base_info *dirinfop,
const char *basename)
{
fileobj_fileop_rename(stream, oldinfop);
#ifdef HAVE_DIRCACHE
dircache_fileop_rename(dirinfop, stream->bindp, basename);
#endif
(void)dirinfop; (void)basename;
}
static inline void fileop_onsync_internal(struct filestr_base *stream)
{
fileobj_fileop_sync(stream);
#ifdef HAVE_DIRCACHE
struct dirinfo_native din;
fill_dirinfo_native(&din);
dircache_fileop_sync(stream->bindp, &din);
#endif
}
static inline void fileop_ontruncate_internal(struct filestr_base *stream)
{
fileobj_fileop_truncate(stream);
}
static inline void volume_onmount_internal(IF_MV_NONVOID(int volume))
{
#ifdef HAVE_DIRCACHE
dircache_mount();
#endif
IF_MV( (void)volume; )
}
static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume))
{
fileobj_mgr_unmount(IF_MV(volume));
#ifdef HAVE_DIRCACHE
dircache_unmount(IF_MV(volume));
#endif
}
/** Directory reading **/
static inline int readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
struct dirent *entry)
{
#ifdef HAVE_DIRCACHE
return dircache_readdir_dirent(stream, scanp, entry);
#else
return uncached_readdir_dirent(stream, scanp, entry);
#endif
}
static inline void rewinddir_dirent(struct dirscan_info *scanp)
{
#ifdef HAVE_DIRCACHE
dircache_rewinddir_dirent(scanp);
#else
uncached_rewinddir_dirent(scanp);
#endif
}
static inline int readdir_internal(struct filestr_base *stream,
struct file_base_info *infop,
struct fat_direntry *fatent)
{
#ifdef HAVE_DIRCACHE
return dircache_readdir_internal(stream, infop, fatent);
#else
return uncached_readdir_internal(stream, infop, fatent);
#endif
}
static inline void rewinddir_internal(struct file_base_info *infop)
{
#ifdef HAVE_DIRCACHE
dircache_rewinddir_internal(infop);
#else
uncached_rewinddir_internal(infop);
#endif
}
/** Misc. stuff **/
static inline struct fat_direntry *get_dir_fatent_dircache(void)
{
#ifdef HAVE_DIRCACHE
return get_dir_fatent();
#else
return NULL;
#endif
}
#endif /* _DIRCACHE_REDIRECT_H_ */

View File

@ -0,0 +1,83 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef DISK_CACHE_H
#define DISK_CACHE_H
/* This needs enough for all file handles to have a buffer in the worst case
* plus at least one reserved exclusively for the cache client and a couple
* for other file system code. The buffers are put to use by the cache if not
* taken for another purpose (meaning nothing is wasted sitting fallow).
*
* One map per volume is maintained in order to avoid collisions between
* volumes that would slow cache probing. DC_MAP_NUM_ENTRIES is the number
* for each map per volume. The buffers themselves are shared.
*/
#if MEMORYSIZE < 8
#define DC_NUM_ENTRIES 32
#define DC_MAP_NUM_ENTRIES 128
#elif MEMORYSIZE <= 32
#define DC_NUM_ENTRIES 48
#define DC_MAP_NUM_ENTRIES 128
#else /* MEMORYSIZE > 32 */
#define DC_NUM_ENTRIES 64
#define DC_MAP_NUM_ENTRIES 256
#endif /* MEMORYSIZE */
/* this _could_ be larger than a sector if that would ever be useful */
#define DC_CACHE_BUFSIZE SECTOR_SIZE
#include "mutex.h"
#include "mv.h"
static inline void dc_lock_cache(void)
{
extern struct mutex disk_cache_mutex;
mutex_lock(&disk_cache_mutex);
}
static inline void dc_unlock_cache(void)
{
extern struct mutex disk_cache_mutex;
mutex_unlock(&disk_cache_mutex);
}
void * dc_cache_probe(IF_MV(int volume,) unsigned long secnum,
unsigned int *flags);
void dc_dirty_buf(void *buf);
void dc_discard_buf(void *buf);
void dc_commit_all(IF_MV_NONVOID(int volume));
void dc_discard_all(IF_MV_NONVOID(int volume));
void dc_init(void) INIT_ATTR;
/* in addition to filling, writeback is implemented by the client */
extern void dc_writeback_callback(IF_MV(int volume, ) unsigned long sector,
void *buf);
/** These synchronize and can be called by anyone **/
/* expropriate a buffer from the cache of DC_CACHE_BUFSIZE bytes */
void * dc_get_buffer(void);
/* return buffer to the cache by buffer */
void dc_release_buffer(void *buf);
#endif /* DISK_CACHE_H */

View File

@ -18,81 +18,88 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _FILE_H_
#define _FILE_H_
#include <sys/types.h>
#include "config.h"
#include "gcc_extensions.h"
#include <stdbool.h>
#include <fcntl.h>
#ifdef WIN32
/* this has SEEK_SET et al */
#include <stdio.h>
#endif
#include "config.h"
#include "gcc_extensions.h"
#undef MAX_PATH /* this avoids problems when building simulator */
#define MAX_PATH 260
#define MAX_OPEN_FILES 11
#if !defined(PLUGIN) && !defined(CODEC)
#if defined(APPLICATION) && !defined(__PCTOOL__)
#include "rbpaths.h"
# define open(x, ...) app_open(x, __VA_ARGS__)
# define creat(x,m) app_creat(x, m)
# define remove(x) app_remove(x)
# define rename(x,y) app_rename(x,y)
# define readlink(x,y,z) app_readlink(x,y,z)
# if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA))
/* SDL overrides a few more */
# define read(x,y,z) sim_read(x,y,z)
# define write(x,y,z) sim_write(x,y,z)
# endif
#elif defined(SIMULATOR) || defined(DBTOOL)
# define open(x, ...) sim_open(x, __VA_ARGS__)
# define creat(x,m) sim_creat(x,m)
# define remove(x) sim_remove(x)
# define rename(x,y) sim_rename(x,y)
# define fsync(x) sim_fsync(x)
# define ftruncate(x,y) sim_ftruncate(x,y)
# define lseek(x,y,z) sim_lseek(x,y,z)
# define read(x,y,z) sim_read(x,y,z)
# define write(x,y,z) sim_write(x,y,z)
# define close(x) sim_close(x)
/* readlink() not used in the sim yet */
extern int sim_open(const char *name, int o, ...);
extern int sim_creat(const char *name, mode_t mode);
enum relate_result
{
/* < 0 == failure */
RELATE_DIFFERENT = 0, /* the two paths are different objects */
RELATE_SAME, /* the two paths are the same object */
RELATE_PREFIX, /* the path2 contains path1 as a prefix */
};
#if defined(APPLICATION)
#include "filesystem-app.h"
#elif defined(SIMULATOR) || defined(__PCTOOL__)
#include "../../uisimulator/common/filesystem-sim.h"
#else
#include "filesystem-native.h"
#endif
typedef int (*open_func)(const char* pathname, int flags, ...);
typedef ssize_t (*read_func)(int fd, void *buf, size_t count);
typedef int (*creat_func)(const char *pathname, mode_t mode);
typedef ssize_t (*write_func)(int fd, const void *buf, size_t count);
typedef void (*qsort_func)(void *base, size_t nmemb, size_t size,
int(*_compar)(const void *, const void *));
#ifndef FILEFUNCTIONS_DECLARED
int fdprintf(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
#endif /* FILEFUNCTIONS_DECLARED */
extern int file_open(const char* pathname, int flags);
extern int close(int fd);
extern int fsync(int fd);
extern ssize_t read(int fd, void *buf, size_t count);
extern off_t lseek(int fildes, off_t offset, int whence);
extern int file_creat(const char *pathname);
#if ((CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(__PCTOOL__)) || \
defined(TEST_FAT)
#define creat(x, y) file_creat(x)
#ifndef FILEFUNCTIONS_DEFINED
#ifndef open
#define open FS_PREFIX(open)
#endif
#ifndef creat
#define creat FS_PREFIX(creat)
#endif
#ifndef close
#define close FS_PREFIX(close)
#endif
#ifndef ftruncate
#define ftruncate FS_PREFIX(ftruncate)
#endif
#ifndef fsync
#define fsync FS_PREFIX(fsync)
#endif
#ifndef lseek
#define lseek FS_PREFIX(lseek)
#endif
#ifndef read
#define read FS_PREFIX(read)
#endif
#ifndef write
#define write FS_PREFIX(write)
#endif
#ifndef remove
#define remove FS_PREFIX(remove)
#endif
#ifndef rename
#define rename FS_PREFIX(rename)
#endif
#ifndef filesize
#define filesize FS_PREFIX(filesize)
#endif
#ifndef fsamefile
#define fsamefile FS_PREFIX(fsamefile)
#endif
#ifndef file_exists
#define file_exists FS_PREFIX(file_exists)
#endif
#ifndef relate
#define relate FS_PREFIX(relate)
#endif
#ifndef readlink
#define readlink FS_PREFIX(readlink)
#endif
#endif /* FILEFUNCTIONS_DEFINED */
#if !defined(CODEC) && !defined(PLUGIN)
#define open(x, y, ...) file_open(x,y)
#endif
#endif
extern ssize_t write(int fd, const void *buf, size_t count);
extern int remove(const char* pathname);
extern int rename(const char* path, const char* newname);
extern int ftruncate(int fd, off_t length);
extern off_t filesize(int fd);
extern int release_files(int volume);
int fdprintf (int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
#endif /* !CODEC && !PLUGIN */
#endif
#endif /* _FILE_H_ */

View File

@ -0,0 +1,371 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _FILE_INTERNAL_H_
#define _FILE_INTERNAL_H_
#include <sys/types.h>
#include <stdlib.h>
#include "mv.h"
#include "linked_list.h"
#include "mutex.h"
#include "mrsw_lock.h"
#include "fs_attr.h"
#include "fat.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
#endif
/** Tuneable parameters **/
/* limits for number of open descriptors - if you increase these values, make
certain that the disk cache has enough available buffers */
#define MAX_OPEN_FILES 11
#define MAX_OPEN_DIRS 12
#define MAX_OPEN_HANDLES (MAX_OPEN_FILES+MAX_OPEN_DIRS)
/* internal functions open streams as well; make sure they don't fail if all
user descs are busy; this needs to be at least the greatest quantity needed
at once by all internal functions */
#ifdef HAVE_DIRCACHE
#define AUX_FILEOBJS 3
#else
#define AUX_FILEOBJS 2
#endif
/* number of components statically allocated to handle the vast majority
of path depths; should maybe be tuned for >= 90th percentile but for now,
imma just guessing based on something like:
root + 'Music' + 'Artist' + 'Album' + 'Disc N' + filename */
#define STATIC_PATHCOMP_NUM 6
#define MAX_NAME 255
/* unsigned value that will also hold the off_t range we need without
overflow */
#define file_size_t uint32_t
#ifdef __USE_FILE_OFFSET64
/* if we want, we can deal with files up to 2^32-1 bytes-- the full FAT16/32
range */
#define FILE_SIZE_MAX (0xffffffffu)
#else
/* file contents and size will be preserved by the APIs so long as ftruncate()
isn't used; bytes passed 2^31-1 will not accessible nor will writes succeed
that would extend the file beyond the max for a 32-bit off_t */
#define FILE_SIZE_MAX (0x7fffffffu)
#endif
/* if file is "large(ish)", then get rid of the contents now rather than
lazily when the file is synced or closed in order to free-up space */
#define O_TRUNC_THRESH 65536
/* default attributes when creating new files and directories */
#define ATTR_NEW_FILE (ATTR_ARCHIVE)
#define ATTR_NEW_DIRECTORY (ATTR_DIRECTORY)
#define ATTR_MOUNT_POINT (ATTR_VOLUME | ATTR_DIRECTORY)
/** File sector cache **/
enum filestr_cache_flags
{
FSC_DIRTY = 0x1, /* buffer is dirty (needs writeback) */
FSC_NEW = 0x2, /* buffer is new (never yet written) */
};
struct filestr_cache
{
uint8_t *buffer; /* buffer to hold sector */
unsigned long sector; /* file sector that is in buffer */
unsigned int flags; /* FSC_* bits */
};
void file_cache_init(struct filestr_cache *cachep);
void file_cache_reset(struct filestr_cache *cachep);
void file_cache_alloc(struct filestr_cache *cachep);
void file_cache_free(struct filestr_cache *cachep);
/** Common bitflags used throughout **/
/* bitflags used by open files and descriptors */
enum fildes_and_obj_flags
{
/* used in descriptor and common */
FDO_BUSY = 0x0001, /* descriptor/object is in use */
/* only used in individual stream descriptor */
FD_WRITE = 0x0002, /* descriptor has write mode */
FD_WRONLY = 0x0004, /* descriptor is write mode only */
FD_APPEND = 0x0008, /* descriptor is append mode */
/* only used as common flags */
FO_DIRECTORY = 0x0010, /* fileobj is a directory */
FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */
FO_REMOVED = 0x0040, /* fileobj was deleted while open */
FO_SINGLE = 0x0080, /* fileobj has only one stream open */
FDO_MASK = 0x00ff,
/* bitflags that instruct various 'open' functions how to behave */
FF_FILE = 0x0000, /* expect file; accept file only */
FF_DIR = 0x0100, /* expect dir; accept dir only */
FF_ANYTYPE = 0x0200, /* succeed if either file or dir */
FF_TYPEMASK = 0x0300, /* mask of typeflags */
FF_CREAT = 0x0400, /* create if file doesn't exist */
FF_EXCL = 0x0800, /* fail if creating and file exists */
FF_CHECKPREFIX = 0x1000, /* detect if file is prefix of path */
FF_NOISO = 0x2000, /* do not decode ISO filenames to UTF-8 */
FF_MASK = 0x3f00,
/* special values used in isolation */
FV_NONEXIST = 0x8000, /* closed but not freed (unmounted) */
FV_OPENSYSROOT = 0xc001, /* open sysroot, volume 0 not mounted */
};
/** Common data structures used throughout **/
/* basic file information about its location */
struct file_base_info
{
union {
#ifdef HAVE_MULTIVOLUME
int volume; /* file's volume (overlaps fatfile.volume) */
#endif
#if CONFIG_PLATFORM & PLATFORM_NATIVE
struct fat_file fatfile; /* FS driver file info */
#endif
};
#ifdef HAVE_DIRCACHE
struct dircache_file dcfile; /* dircache file info */
#endif
};
#define BASEINFO_VOL(infop) \
IF_MV_VOL((infop)->volume)
/* open files binding item */
struct file_base_binding
{
struct ll_node node; /* list item node (first!) */
struct file_base_info info; /* basic file info */
};
#define BASEBINDING_VOL(bindp) \
BASEINFO_VOL(&(bindp)->info)
/* directory scanning position info */
struct dirscan_info
{
#if CONFIG_PLATFORM & PLATFORM_NATIVE
struct fat_dirscan_info fatscan; /* FS driver scan info */
#endif
#ifdef HAVE_DIRCACHE
struct dircache_file dcscan; /* dircache scan info */
#endif
};
/* describes the file as an open stream */
struct filestr_base
{
struct ll_node node; /* list item node (first!) */
uint16_t flags; /* FD_* bits of this stream */
uint16_t unused; /* not used */
struct filestr_cache cache; /* stream-local cache */
struct filestr_cache *cachep; /* the cache in use (local or shared) */
struct file_base_info *infop; /* base file information */
struct fat_filestr fatstr; /* FS driver information */
struct file_base_binding *bindp; /* common binding for file/dir */
struct mutex *mtx; /* serialization for this stream */
};
void filestr_base_init(struct filestr_base *stream);
void filestr_base_destroy(struct filestr_base *stream);
void filestr_alloc_cache(struct filestr_base *stream);
void filestr_free_cache(struct filestr_base *stream);
void filestr_assign_cache(struct filestr_base *stream,
struct filestr_cache *cachep);
void filestr_copy_cache(struct filestr_base *stream,
struct filestr_cache *cachep);
void filestr_discard_cache(struct filestr_base *stream);
/* allocates a cache buffer if needed and returns the cache pointer */
static inline struct filestr_cache *
filestr_get_cache(struct filestr_base *stream)
{
struct filestr_cache *cachep = stream->cachep;
if (!cachep->buffer)
filestr_alloc_cache(stream);
return cachep;
}
static inline void filestr_lock(struct filestr_base *stream)
{
mutex_lock(stream->mtx);
}
static inline void filestr_unlock(struct filestr_base *stream)
{
mutex_unlock(stream->mtx);
}
/* stream lock doesn't have to be used if getting RW lock writer access */
#define FILESTR_WRITER 0
#define FILESTR_READER 1
#define FILESTR_LOCK(type, stream) \
({ if (FILESTR_##type) filestr_lock(stream); })
#define FILESTR_UNLOCK(type, stream) \
({ if (FILESTR_##type) filestr_unlock(stream); })
#define ATTR_PREFIX (0x8000) /* out of the way of all ATTR_* bits */
/* structure to return detailed information about what you opened */
struct path_component_info
{
const char *name; /* pointer to name within 'path' */
size_t length; /* length of component within 'path' */
file_size_t filesize; /* size of the opened file (0 if dir) */
unsigned int attr; /* attributes of this component */
struct file_base_info *prefixp; /* base info to check as prefix (IN) */
struct file_base_info parentinfo; /* parent directory info of file */
};
int open_stream_internal(const char *path, unsigned int callflags,
struct filestr_base *stream,
struct path_component_info *compinfo);
int close_stream_internal(struct filestr_base *stream);
int create_stream_internal(struct file_base_info *parentinfop,
const char *basename, size_t length,
unsigned int attr, unsigned int callflags,
struct filestr_base *stream);
int remove_stream_internal(const char *path, struct filestr_base *stream,
unsigned int callflags);
int test_stream_exists_internal(const char *path, unsigned int callflags);
int open_noiso_internal(const char *path, int oflag); /* file.c */
struct dirent;
int uncached_readdir_dirent(struct filestr_base *stream,
struct dirscan_info *scanp,
struct dirent *entry);
void uncached_rewinddir_dirent(struct dirscan_info *scanp);
int uncached_readdir_internal(struct filestr_base *stream,
struct file_base_info *infop,
struct fat_direntry *fatent);
void uncached_rewinddir_internal(struct file_base_info *infop);
int test_dir_empty_internal(struct filestr_base *stream);
struct dirinfo_internal
{
unsigned int attr;
file_size_t size;
uint16_t wrtdate;
uint16_t wrttime;
};
/** Synchronization used throughout **/
/* acquire the filesystem lock as READER */
static inline void file_internal_lock_READER(void)
{
extern struct mrsw_lock file_internal_mrsw;
mrsw_read_acquire(&file_internal_mrsw);
}
/* release the filesystem lock as READER */
static inline void file_internal_unlock_READER(void)
{
extern struct mrsw_lock file_internal_mrsw;
mrsw_read_release(&file_internal_mrsw);
}
/* acquire the filesystem lock as WRITER */
static inline void file_internal_lock_WRITER(void)
{
extern struct mrsw_lock file_internal_mrsw;
mrsw_write_acquire(&file_internal_mrsw);
}
/* release the filesystem lock as WRITER */
static inline void file_internal_unlock_WRITER(void)
{
extern struct mrsw_lock file_internal_mrsw;
mrsw_write_release(&file_internal_mrsw);
}
#define ERRNO 0 /* maintain errno value */
#define RC 0 /* maintain rc value */
/* NOTES: if _errno is a non-constant expression, it must set an error
* number and not return the ERRNO constant which will merely set
* errno to zero, not preserve the current value; if you must set
* errno to zero, set it explicitly, not in the macro
*
* if _rc is constant-expression evaluation to 'RC', then rc will
* NOT be altered; i.e. if you must set rc to zero, set it explicitly,
* not in the macro
*/
/* set errno and rc and proceed to the "file_error:" label */
#define FILE_ERROR(_errno, _rc) \
({ __builtin_constant_p(_errno) ? \
({ if ((_errno) != ERRNO) errno = (_errno); }) : \
({ errno = (_errno); }); \
__builtin_constant_p(_rc) ? \
({ if ((_rc) != RC) rc = (_rc); }) : \
({ rc = (_rc); }); \
goto file_error; })
/* set errno and return a value at the point of invocation */
#define FILE_ERROR_RETURN(_errno, _rc...) \
({ __builtin_constant_p(_errno) ? \
({ if ((_errno) != ERRNO) errno = (_errno); }) : \
({ errno = (_errno); }); \
return _rc; })
/** Misc. stuff **/
/* iterate through all the volumes if volume < 0, else just the given volume */
#define FOR_EACH_VOLUME(volume, i) \
for (int i = (IF_MV_VOL(volume) >= 0 ? IF_MV_VOL(volume) : 0), \
_end = (IF_MV_VOL(volume) >= 0 ? i : NUM_VOLUMES-1); \
i <= _end; i++)
/* return a pointer to the static struct fat_direntry */
static inline struct fat_direntry *get_dir_fatent(void)
{
extern struct fat_direntry dir_fatent;
return &dir_fatent;
}
void iso_decode_d_name(char *d_name);
#ifdef HAVE_DIRCACHE
void empty_dirent(struct dirent *entry);
void fill_dirinfo_native(struct dirinfo_native *din);
#endif /* HAVE_DIRCACHE */
void filesystem_init(void) INIT_ATTR;
#endif /* _FILE_INTERNAL_H_ */

View File

@ -0,0 +1,56 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2014 by Michael Sevakis
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _FILEOBJ_MGR_H_
#define _FILEOBJ_MGR_H_
#include "file_internal.h"
void file_binding_insert_first(struct file_base_binding *bindp);
void file_binding_insert_last(struct file_base_binding *bindp);
void file_binding_remove(struct file_base_binding *bindp);
void file_binding_remove_next(struct file_base_binding *prevp,
struct file_base_binding *bindp);
void fileobj_fileop_open(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags);
void fileobj_fileop_close(struct filestr_base *stream);
void fileobj_fileop_create(struct filestr_base *stream,
const struct file_base_info *srcinfop,
unsigned int callflags);
void fileobj_fileop_rename(struct filestr_base *stream,
const struct file_base_info *oldinfop);
void fileobj_fileop_remove(struct filestr_base *stream,
const struct file_base_info *oldinfop);
void fileobj_fileop_sync(struct filestr_base *stream);
void fileobj_fileop_truncate(struct filestr_base *stream);
extern void ftruncate_internal_callback(struct filestr_base *stream,
struct filestr_base *s);
file_size_t * fileobj_get_sizep(const struct filestr_base *stream);
unsigned int fileobj_get_flags(const struct filestr_base *stream);
void fileobj_change_flags(struct filestr_base *stream,
unsigned int flags, unsigned int mask);
void fileobj_mgr_unmount(IF_MV_NONVOID(int volume));
void fileobj_mgr_init(void) INIT_ATTR;
#endif /* _FILEOBJ_MGR_H_ */

View File

@ -0,0 +1,107 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _FILESYSTEM_NATIVE_H_
#define _FILESYSTEM_NATIVE_H_
#if defined(PLUGIN) || defined(CODEC)
#define FILEFUNCTIONS_DECLARED
#define FILEFUNCTIONS_DEFINED
#define DIRFUNCTIONS_DECLARED
#define DIRFUNCTIONS_DEFINED
#endif /* PLUGIN || CODEC */
#define FS_PREFIX(_x_) _x_
#endif /* _FILESYSTEM_NATIVE_H_ */
#ifdef _FILE_H_
#ifndef _FILESYSTEM_NATIVE__FILE_H_
#define _FILESYSTEM_NATIVE__FILE_H_
#ifdef RB_FILESYSTEM_OS
#define FILEFUNCTIONS_DEFINED
#endif
#ifndef FILEFUNCTIONS_DECLARED
#define __OPEN_MODE_ARG
#define __CREAT_MODE_ARG
int open(const char *name, int oflag);
int creat(const char *name);
int close(int fildes);
int ftruncate(int fildes, off_t length);
int fsync(int fildes);
off_t lseek(int fildes, off_t offset, int whence);
ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);
int remove(const char *path);
int rename(const char *old, const char *new);
off_t filesize(int fildes);
int fsamefile(int fildes1, int fildes2);
int relate(const char *path1, const char *path2);
bool file_exists(const char *path);
#endif /* !FILEFUNCTIONS_DECLARED */
#if !defined(RB_FILESYSTEM_OS) && !defined (FILEFUNCTIONS_DEFINED)
#define open(path, oflag, ...) open(path, oflag)
#define creat(path, mode) creat(path)
#endif /* FILEFUNCTIONS_DEFINED */
#endif /* _FILESYSTEM_NATIVE__FILE_H_ */
#endif /* _FILE_H_ */
#ifdef _DIR_H_
#ifndef _FILESYSTEM_NATIVE__DIR_H_
#define _FILESYSTEM_NATIVE__DIR_H_
#define DIRENT dirent
struct dirent;
struct dirinfo_native
{
unsigned int attr;
off_t size;
uint16_t wrtdate;
uint16_t wrttime;
};
typedef struct {} DIR;
#ifndef DIRFUNCTIONS_DECLARED
#define __MKDIR_MODE_ARG
#ifdef RB_FILESYSTEM_OS
#define DIRFUNCTIONS_DEFINED
#endif
DIR * opendir(const char *dirname);
struct dirent * readdir(DIR *dirp);
int readdir_r(DIR *dirp, struct dirent *entry,
struct dirent **result);
void rewinddir(DIR *dirp);
int closedir(DIR *dirp);
int mkdir(const char *path);
int rmdir(const char *path);
int samedir(DIR *dirp1, DIR *dirp2);
bool dir_exists(const char *dirname);
#endif /* !DIRFUNCTIONS_DECLARED */
#endif /* _FILESYSTEM_NATIVE__DIR_H_ */
#endif /* _DIR_H_ */

View File

@ -0,0 +1,39 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 by Kévin Ferrare
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef _FS_ATTR_H_
#define _FS_ATTR_H_
#include <stdbool.h>
#undef MAX_PATH /* this avoids problems when building simulator */
#define MAX_PATH 260
/* also used by fat.c so values must not change */
#define ATTR_READ_ONLY 0x01
#define ATTR_HIDDEN 0x02
#define ATTR_SYSTEM 0x04
#define ATTR_VOLUME_ID 0x08
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20
#define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */
#define ATTR_LINK 0x80
#endif /* _FS_ATTR_H_ */

View File

@ -18,5 +18,4 @@
*
****************************************************************************/
const char *rb_strerror(int8_t errno);
int load_firmware(unsigned char* buf, const char* firmware, int buffer_size);

View File

@ -51,7 +51,8 @@ enum codepages {
KSX_1001, /* Korean */
BIG_5, /* Trad. Chinese */
UTF_8, /* Unicode */
NUM_CODEPAGES
NUM_CODEPAGES,
INIT_CODEPAGE = ISO_8859_1,
};
#else /* !HAVE_LCD_BITMAP, reduced support */
@ -65,7 +66,8 @@ enum codepages {
WIN_1250, /* Central European */
WIN_1252, /* Western European */
UTF_8, /* Unicode */
NUM_CODEPAGES
NUM_CODEPAGES,
INIT_CODEPAGE = ISO_8859_1,
};
#endif
@ -78,9 +80,19 @@ unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8, in
unsigned long utf8length(const unsigned char *utf8);
const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs);
void set_codepage(int cp);
int get_codepage(void);
int utf8seek(const unsigned char* utf8, int offset);
const char* get_codepage_name(int cp);
#if defined(APPLICATION) && defined(__linux__)
#ifdef APPLICATION
#if defined(__linux__)
const char *get_current_codepage_name_linux(void);
#endif
#endif /* APPLICATION */
#if 0 /* not needed just now */
void unicode_init(void);
#else
#define unicode_init() do {} while (0)
#endif
#endif /* _RBUNICODE_H_ */

View File

@ -31,21 +31,12 @@ struct mutex
struct blocker blocker; /* priority inheritance info
for waiters and owner*/
IF_COP( struct corelock cl; ) /* multiprocessor sync */
#ifdef HAVE_PRIORITY_SCHEDULING
bool no_preempt;
#endif
};
extern void mutex_init(struct mutex *m);
extern void mutex_lock(struct mutex *m);
extern void mutex_unlock(struct mutex *m);
#ifdef HAVE_PRIORITY_SCHEDULING
/* Deprecated temporary function to disable mutex preempting a thread on
* unlock - firmware/drivers/fat.c and a couple places in apps/buffering.c -
* reliance on it is a bug! */
static inline void mutex_set_preempt(struct mutex *m, bool preempt)
{ m->no_preempt = !preempt; }
#else
#ifndef HAVE_PRIORITY_SCHEDULING
/* Deprecated but needed for now - firmware/drivers/ata_mmc.c */
static inline bool mutex_test(const struct mutex *m)
{ return m->blocker.thread != NULL; }

View File

@ -33,9 +33,6 @@ void mutex_init(struct mutex *m)
wait_queue_init(&m->queue);
m->recursion = 0;
blocker_init(&m->blocker);
#ifdef HAVE_PRIORITY_SCHEDULING
m->no_preempt = false;
#endif
corelock_init(&m->cl);
}
@ -115,7 +112,7 @@ void mutex_unlock(struct mutex *m)
corelock_unlock(&m->cl);
#ifdef HAVE_PRIORITY_SCHEDULING
if((result & THREAD_SWITCH) && !m->no_preempt)
if(result & THREAD_SWITCH)
switch_thread();
#endif
(void)result;

View File

@ -88,6 +88,7 @@ extern int * __errno(void);
#define ELBIN 75 /* Inode is remote (not really error) */
#define EDOTDOT 76 /* Cross mount point (not really error) */
#define EBADMSG 77 /* Trying to read unreadable message */
#define EOVERFLOW 78 /* Value too large to be stored in data type */
#define ENOTUNIQ 80 /* Given log. name not unique */
#define EBADFD 81 /* f.d. invalid for this operation */
#define EREMCHG 82 /* Remote address changed */

View File

@ -23,18 +23,20 @@
#define __FCNTL_H__
#ifndef O_RDONLY
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 4
#define O_APPEND 8
#define O_TRUNC 0x10
#define O_RDONLY 0x0000 /* open for reading only */
#define O_WRONLY 0x0001 /* open for writing only */
#define O_RDWR 0x0002 /* open for reading and writing */
#define O_ACCMODE 0x0003 /* mask for above modes */
#define O_APPEND 0x0008 /* set append mode */
#define O_CREAT 0x0200 /* create if nonexistent */
#define O_TRUNC 0x0400 /* truncate to zero length */
#define O_EXCL 0x0800 /* error if already exists */
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#define SEEK_SET 0 /* set file offset to offset */
#define SEEK_CUR 1 /* set file offset to current plus offset */
#define SEEK_END 2 /* set file offset to EOF plus offset */
#endif
#endif /* __FCNTL_H__ */

View File

@ -23,7 +23,6 @@
#include <time.h>
#include "config.h"
#if CONFIG_RTC
/* mktime() code taken from lynx-2.8.5 source, written
by Philippe De Muyter <phdm@macqel.be> */
time_t mktime(struct tm *t)
@ -58,4 +57,3 @@ time_t mktime(struct tm *t)
result += t->tm_sec;
return(result);
}
#endif

View File

@ -190,6 +190,15 @@ int storage_num_drives(void)
return num_drives;
}
int storage_driver_type(int drive)
{
if (drive >= num_drives)
return -1;
unsigned int bit = (storage_drivers[drive] & DRIVER_MASK)>>DRIVER_OFFSET;
return bit ? find_first_set_bit(bit) : -1;
}
int storage_init(void)
{
int rc=0;

View File

@ -449,21 +449,12 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
{
int microsd_init = 1;
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
/* We now have exclusive control of fat cache and ata */
disk_unmount(SD_SLOT_AS3525); /* release "by force" */
disk_unmount(SD_SLOT_AS3525); /* release "by force", ensure file
descriptors aren't leaked and any busy
ones are invalid if mounting */
mutex_lock(&sd_mtx); /* lock-out card activity */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
@ -471,29 +462,32 @@ static void sd_thread(void)
if (ev.id == SYS_HOTSWAP_INSERTED)
{
success = 0;
sd_enable(true);
init_pl180_controller(SD_SLOT_AS3525);
microsd_init = sd_init_card(SD_SLOT_AS3525);
if (microsd_init < 0) /* initialisation failed */
panicf("microSD init failed : %d", microsd_init);
microsd_init = disk_mount(SD_SLOT_AS3525); /* 0 if fail */
int rc = sd_init_card(SD_SLOT_AS3525);
sd_enable(false);
if (rc >= 0)
success = 2;
else /* initialisation failed */
panicf("microSD init failed : %d", rc);
}
mutex_unlock(&sd_mtx);
if (success > 1)
success = disk_mount(SD_SLOT_AS3525); /* 0 if fail */
/*
* Mount succeeded, or this was an EXTRACTED event,
* in both cases notify the system about the changed filesystems
*/
if (microsd_init)
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
sd_enable(false);
}
break;
#endif
#endif /* HAVE_HOTSWAP */
case SYS_TIMEOUT:
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
{

View File

@ -598,21 +598,13 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
{
int changed = 1;
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
/* We now have exclusive control of fat cache and ata */
disk_unmount(SD_SLOT_AS3525); /* release "by force" */
mutex_lock(&sd_mtx); /* lock-out card activity */
disk_unmount(SD_SLOT_AS3525); /* release "by force", ensure file
descriptors aren't leaked and any busy
ones are invalid if mounting */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
card_info[SD_SLOT_AS3525].initialized = 0;
@ -620,24 +612,25 @@ static void sd_thread(void)
if (ev.id == SYS_HOTSWAP_INSERTED)
{
sd_enable(true);
changed = (sd_init_card(SD_SLOT_AS3525) == 0) && disk_mount(SD_SLOT_AS3525); /* 0 if fail */
success = sd_init_card(SD_SLOT_AS3525) == 0 ? 2 : 0;
sd_enable(false);
}
mutex_unlock(&sd_mtx);
if (success > 1)
success = disk_mount(SD_SLOT_AS3525); /* 0 if fail */
/*
* Mount succeeded, or this was an EXTRACTED event,
* in both cases notify the system about the changed filesystems
*/
if (changed)
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
sd_enable(false);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
}
break;
#endif
#endif /* HAVE_HOTSWAP */
case SYS_TIMEOUT:
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
{

View File

@ -766,14 +766,7 @@ static void sdmmc_thread(void)
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
{
int microsd_init = 1;
/* lock-out FAT activity first -
* prevent deadlocking via disk_mount that
* would cause a reverse-order attempt with
* another thread */
#ifdef HAVE_HOTSWAP
fat_lock();
#endif
int microsd_init = ev.id == SYS_HOTSWAP_INSERTED ? 0 : 1;
/* We now have exclusive control of fat cache and sd.
* Release "by force", ensure file
@ -785,35 +778,37 @@ static void sdmmc_thread(void)
/* Skip non-removable drivers */
if(!sdmmc_removable(drive))
continue;
/* lock-out card activity - direct calls
* into driver that bypass the fat cache */
mutex_lock(&mutex[drive]);
disk_unmount(sd_first_drive + sd_drive);
mutex_lock(&mutex[drive]); /* lock-out card activity */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
SDMMC_INFO(sd_map[sd_drive]).initialized = 0;
int rc = -1;
if(ev.id == SYS_HOTSWAP_INSERTED)
{
microsd_init = init_drive(drive);
if(microsd_init < 0) /* initialisation failed */
panicf("%s init failed : %d", SDMMC_CONF(sd_map[sd_drive]).name, microsd_init);
microsd_init = disk_mount(sd_first_drive + sd_drive); /* 0 if fail */
rc = init_drive(drive);
if(rc < 0) /* initialisation failed */
panicf("%s init failed : %d", SDMMC_CONF(sd_map[sd_drive]).name, rc);
}
/*
* Mount succeeded, or this was an EXTRACTED event,
* in both cases notify the system about the changed filesystems
*/
if(microsd_init)
queue_broadcast(SYS_FS_CHANGED, 0);
/* unlock card */
mutex_unlock(&mutex[drive]);
if (rc >= 0)
microsd_init += disk_mount(sd_first_drive + sd_drive); /* 0 if fail */
}
/* Access is now safe */
#ifdef HAVE_HOTSWAP
fat_unlock();
#endif
/*
* One or more mounts succeeded, or this was an EXTRACTED event,
* in both cases notify the system about the changed filesystems
*/
if(microsd_init)
queue_broadcast(SYS_FS_CHANGED, 0);
break;
}
#endif

View File

@ -19,7 +19,6 @@
*
****************************************************************************/
#include "config.h" /* for HAVE_MULTIDRIVE */
#include "fat.h"
#include "sdmmc.h"
#include "gcc_extensions.h"
#ifdef HAVE_HOTSWAP
@ -1125,35 +1124,28 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
/* We now have exclusive control of fat cache and ata */
disk_unmount(sd_first_drive+1); /* release "by force" */
disk_unmount(sd_first_drive+1); /* release "by force", ensure file
descriptors aren't leaked and any busy
ones are invalid if mounting */
mutex_lock(&sd_mtx); /* lock-out card activity */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
card_info[1].initialized = 0;
sd_status[1].retry = 0;
if (ev.id == SYS_HOTSWAP_INSERTED)
disk_mount(sd_first_drive+1);
queue_broadcast(SYS_FS_CHANGED, 0);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
if (ev.id == SYS_HOTSWAP_INSERTED)
success = disk_mount(sd_first_drive+1); /* 0 if fail */
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
break;
#endif
#endif /* HAVE_HOTSWAP */
case SYS_TIMEOUT:
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
{

View File

@ -22,7 +22,6 @@
****************************************************************************/
#include "config.h" /* for HAVE_MULTIVOLUME */
#include "fat.h"
#include "thread.h"
#include "gcc_extensions.h"
#include "led.h"
@ -331,50 +330,45 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
{
int microsd_init = 1;
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
/* We now have exclusive control of fat cache and ata */
disk_unmount(sd_first_drive); /* release "by force" */
mutex_lock(&sd_mtx); /* lock-out card activity */
disk_unmount(sd_first_drive); /* release "by force", ensure file
descriptors aren't leaked and any busy
ones are invalid if mounting */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
card_info.initialized = 0;
if (ev.id == SYS_HOTSWAP_INSERTED)
{
success = 0;
sd_enable(true);
microsd_init = sd_init_card(sd_first_drive);
if (microsd_init < 0) /* initialisation failed */
panicf("microSD init failed : %d", microsd_init);
microsd_init = disk_mount(sd_first_drive); /* 0 if fail */
int rc = sd_init_card(sd_first_drive);
sd_enable(false);
if (rc >= 0)
success = 2;
else /* initialisation failed */
panicf("microSD init failed : %d", rc);
}
/* Access is now safe */
mutex_unlock(&sd_mtx);
if (success > 1)
success = disk_mount(sd_first_drive); /* 0 if fail */
/*
* Mount succeeded, or this was an EXTRACTED event,
* in both cases notify the system about the changed filesystems
*/
if (microsd_init)
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
sd_enable(false);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
}
break;
#endif
#endif /* HAVE_HOTSWAP */
case SYS_TIMEOUT:
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
{

View File

@ -34,7 +34,6 @@
#ifdef HAVE_HOTSWAP
#include "sdmmc.h"
#include "disk.h"
#include "fat.h"
#endif
#include "dma-target.h"
#include "system-target.h"
@ -585,48 +584,29 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
{
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
/* We now have exclusive control of fat cache and ata */
disk_unmount(0); /* release "by force" */
disk_unmount(0); /* release "by force", ensure file
descriptors aren't leaked and any busy
ones are invalid if mounting */
mutex_lock(&sd_mtx); /* lock-out card activity */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
card_info[0].initialized = 0;
/* Access is now safe */
mutex_unlock(&sd_mtx);
if (ev.id == SYS_HOTSWAP_INSERTED)
{
/* FIXME: once sd_enabled is implement properly,
* reinitializing the controllers might be needed */
sd_enable(true);
if (success < 0) /* initialisation failed */
panicf("SD init failed : %d", success);
success = disk_mount(0); /* 0 if fail */
}
/* notify the system about the changed filesystems
*/
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
sd_enable(false);
}
break;
#endif
#endif /* HAVE_HOTSWAP */
}
}
}

View File

@ -32,7 +32,7 @@
#include "s5l8702.h"
#include "led.h"
#include "ata_idle_notify.h"
#include "fat.h"
#include "disk_cache.h"
#include "splash.h"
@ -68,6 +68,7 @@ static struct semaphore mmc_wakeup;
static struct semaphore mmc_comp_wakeup;
static int spinup_time = 0;
static int dma_mode = 0;
static char aligned_buffer[SECTOR_SIZE] __attribute__((aligned(0x10)));
#ifdef ATA_HAVE_BBT
@ -857,8 +858,25 @@ int ata_bbt_translate(uint64_t sector, uint32_t count, uint64_t* phys, uint32_t*
static int ata_rw_sectors(uint64_t sector, uint32_t count, void* buffer, bool write)
{
if (((uint32_t)buffer) & 0xf)
panicf("ATA: Misaligned data buffer at %08X (sector %lu, count %lu)",
(unsigned int)buffer, (long unsigned int)sector, (long unsigned int)count);
{
while (count)
{
if (write)
memcpy(aligned_buffer, buffer, SECTOR_SIZE);
PASS_RC(ata_rw_sectors(sector, 1, aligned_buffer, write), 0, 0);
if (!write)
memcpy(buffer, aligned_buffer, SECTOR_SIZE);
buffer += SECTOR_SIZE;
sector++;
count--;
}
return 0;
}
#ifdef ATA_HAVE_BBT
if (sector + count > ata_virtual_sectors) RET_ERR(0);
if (ata_bbt)
@ -1117,14 +1135,13 @@ int ata_init(void)
-- Michael Sparmann (theseven), 2011-10-22 */
if (!ceata)
{
unsigned char* sector = fat_get_sector_buffer();
unsigned char* sector = aligned_buffer;
ata_rw_sectors(0, 1, sector, false);
if (sector[510] == 0xaa && sector[511] == 0x55)
{
ata_swap = true;
splashf(5000, "Wrong HDD endianness, please update your emCORE version!");
}
fat_release_sector_buffer();
}
create_thread(ata_thread, ata_stack,

View File

@ -28,7 +28,6 @@
#include "led.h"
#include "thread.h"
#include "disk.h"
#include "fat.h"
#include "ata_idle_notify.h"
#include "usb.h"
@ -657,35 +656,30 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
/* We now have exclusive control of fat cache and ata */
/* Release "by force", ensure file descriptors aren't leaked and
any busy ones are invalid if mounting */
/* Release "by force" */
disk_unmount(sd_first_drive + CARD_NUM_SLOT);
mutex_lock(&sd_mtx); /* lock-out card activity */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
card_info[CARD_NUM_SLOT].initialized = 0;
sd_status[CARD_NUM_SLOT].retry = 0;
if (ev.id == SYS_HOTSWAP_INSERTED)
disk_mount(sd_first_drive + CARD_NUM_SLOT);
queue_broadcast(SYS_FS_CHANGED, 0);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
if (ev.id == SYS_HOTSWAP_INSERTED)
success = disk_mount(sd_first_drive + CARD_NUM_SLOT);
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
break;
#endif
#endif /* HAVE_HOTSWAP */
case SYS_TIMEOUT:
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
{

View File

@ -592,48 +592,29 @@ static void sd_thread(void)
{
#ifdef HAVE_HOTSWAP
case SYS_HOTSWAP_INSERTED:
case SYS_HOTSWAP_EXTRACTED:
{
case SYS_HOTSWAP_EXTRACTED:;
int success = 1;
fat_lock(); /* lock-out FAT activity first -
prevent deadlocking via disk_mount that
would cause a reverse-order attempt with
another thread */
mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
into driver that bypass the fat cache */
/* We now have exclusive control of fat cache and ata */
disk_unmount(0); /* release "by force" */
disk_unmount(0); /* release "by force", ensure file
descriptors aren't leaked and any busy
ones are invalid if mounting */
mutex_lock(&sd_mtx); /* lock-out card activity */
/* Force card init for new card, re-init for re-inserted one or
* clear if the last attempt to init failed with an error. */
card_info[0].initialized = 0;
if (ev.id == SYS_HOTSWAP_INSERTED)
{
/* FIXME: once sd_enabled is implement properly,
* reinitializing the controllers might be needed */
sd_enable(true);
if (success < 0) /* initialisation failed */
panicf("SD init failed : %d", success);
success = disk_mount(0); /* 0 if fail */
}
mutex_unlock(&sd_mtx);
/* notify the system about the changed filesystems
*/
if (ev.id == SYS_HOTSWAP_INSERTED)
success = disk_mount(0); /* 0 if fail */
/* notify the system about the changed filesystems */
if (success)
queue_broadcast(SYS_FS_CHANGED, 0);
/* Access is now safe */
mutex_unlock(&sd_mtx);
fat_unlock();
sd_enable(false);
}
break;
#endif
#endif /* HAVE_HOTSWAP */
case SYS_TIMEOUT:
if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
{

Some files were not shown because too many files have changed in this diff Show More