diff options
130 files changed, 14689 insertions, 7316 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c index 3234e77d9b..567f98ac29 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c | |||
@@ -41,7 +41,7 @@ | |||
41 | #include "list.h" | 41 | #include "list.h" |
42 | #include "plugin.h" | 42 | #include "plugin.h" |
43 | #include "file.h" | 43 | #include "file.h" |
44 | #include "filefuncs.h" | 44 | #include "pathfuncs.h" |
45 | 45 | ||
46 | #define MAX_BOOKMARKS 10 | 46 | #define MAX_BOOKMARKS 10 |
47 | #define MAX_BOOKMARK_SIZE 350 | 47 | #define MAX_BOOKMARK_SIZE 350 |
@@ -1090,10 +1090,10 @@ static bool generate_bookmark_file_name(const char *in) | |||
1090 | { | 1090 | { |
1091 | #ifdef HAVE_MULTIVOLUME | 1091 | #ifdef HAVE_MULTIVOLUME |
1092 | /* The "root" of an extra volume need special handling too. */ | 1092 | /* The "root" of an extra volume need special handling too. */ |
1093 | bool volume_root = (strip_volume(in, global_bookmark_file_name) && | 1093 | const char *filename; |
1094 | !strcmp("/", global_bookmark_file_name)); | 1094 | path_strip_volume(in, &filename, true); |
1095 | bool volume_root = *filename == '\0'; | ||
1095 | #endif | 1096 | #endif |
1096 | |||
1097 | strcpy(global_bookmark_file_name, in); | 1097 | strcpy(global_bookmark_file_name, in); |
1098 | if(global_bookmark_file_name[len-1] == '/') | 1098 | if(global_bookmark_file_name[len-1] == '/') |
1099 | len--; | 1099 | len--; |
diff --git a/apps/codecs.c b/apps/codecs.c index e84cdf88aa..cabc9ba993 100644 --- a/apps/codecs.c +++ b/apps/codecs.c | |||
@@ -128,7 +128,7 @@ struct codec_api ci = { | |||
128 | logf, | 128 | logf, |
129 | #endif | 129 | #endif |
130 | 130 | ||
131 | (qsort_func)qsort, | 131 | (void *)qsort, |
132 | 132 | ||
133 | #ifdef RB_PROFILE | 133 | #ifdef RB_PROFILE |
134 | profile_thread, | 134 | profile_thread, |
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 61698f5025..75e23b3945 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -528,14 +528,19 @@ static const char* dbg_partitions_getname(int selected_item, void *data, | |||
528 | { | 528 | { |
529 | (void)data; | 529 | (void)data; |
530 | int partition = selected_item/2; | 530 | int partition = selected_item/2; |
531 | struct partinfo* p = disk_partinfo(partition); | 531 | |
532 | struct partinfo p; | ||
533 | if (!disk_partinfo(partition, &p)) | ||
534 | return buffer; | ||
535 | |||
532 | if (selected_item%2) | 536 | if (selected_item%2) |
533 | { | 537 | { |
534 | snprintf(buffer, buffer_len, " T:%x %ld MB", p->type, p->size / ( 2048 / ( SECTOR_SIZE / 512 ))); | 538 | snprintf(buffer, buffer_len, " T:%x %ld MB", p.type, |
539 | p.size / ( 2048 / ( SECTOR_SIZE / 512 ))); | ||
535 | } | 540 | } |
536 | else | 541 | else |
537 | { | 542 | { |
538 | snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p->start); | 543 | snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p.start); |
539 | } | 544 | } |
540 | return buffer; | 545 | return buffer; |
541 | } | 546 | } |
@@ -1377,7 +1382,7 @@ static int disk_callback(int btn, struct gui_synclist *lists) | |||
1377 | simplelist_addline( | 1382 | simplelist_addline( |
1378 | "Size: %s", buf); | 1383 | "Size: %s", buf); |
1379 | unsigned long free; | 1384 | unsigned long free; |
1380 | fat_size( IF_MV(0,) NULL, &free ); | 1385 | volume_size( IF_MV(0,) NULL, &free ); |
1381 | simplelist_addline( | 1386 | simplelist_addline( |
1382 | "Free: %ld MB", free / 1024); | 1387 | "Free: %ld MB", free / 1024); |
1383 | simplelist_addline( | 1388 | simplelist_addline( |
@@ -1469,7 +1474,7 @@ static int disk_callback(int btn, struct gui_synclist *lists) | |||
1469 | "No timing info"); | 1474 | "No timing info"); |
1470 | } | 1475 | } |
1471 | simplelist_addline( | 1476 | simplelist_addline( |
1472 | "Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0))); | 1477 | "Cluster size: %d bytes", volume_get_cluster_size(IF_MV(0))); |
1473 | #ifdef HAVE_ATA_DMA | 1478 | #ifdef HAVE_ATA_DMA |
1474 | i = ata_get_dma_mode(); | 1479 | i = ata_get_dma_mode(); |
1475 | if (i == 0) { | 1480 | if (i == 0) { |
@@ -1496,11 +1501,11 @@ static int disk_callback(int btn, struct gui_synclist *lists) | |||
1496 | simplelist_addline( | 1501 | simplelist_addline( |
1497 | "Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024); | 1502 | "Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024); |
1498 | unsigned long free; | 1503 | unsigned long free; |
1499 | fat_size( IF_MV(0,) NULL, &free ); | 1504 | volume_size( IF_MV(0,) NULL, &free ); |
1500 | simplelist_addline( | 1505 | simplelist_addline( |
1501 | "Free: %ld MB", free / 1024); | 1506 | "Free: %ld MB", free / 1024); |
1502 | simplelist_addline( | 1507 | simplelist_addline( |
1503 | "Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0))); | 1508 | "Cluster size: %d bytes", volume_get_cluster_size(IF_MV(0))); |
1504 | return btn; | 1509 | return btn; |
1505 | } | 1510 | } |
1506 | #endif | 1511 | #endif |
@@ -1542,29 +1547,64 @@ static bool dbg_disk_info(void) | |||
1542 | #ifdef HAVE_DIRCACHE | 1547 | #ifdef HAVE_DIRCACHE |
1543 | static int dircache_callback(int btn, struct gui_synclist *lists) | 1548 | static int dircache_callback(int btn, struct gui_synclist *lists) |
1544 | { | 1549 | { |
1545 | (void)lists; | 1550 | struct dircache_info info; |
1551 | dircache_get_info(&info); | ||
1552 | |||
1553 | if (global_settings.dircache) | ||
1554 | { | ||
1555 | switch (btn) | ||
1556 | { | ||
1557 | case ACTION_STD_CONTEXT: | ||
1558 | splash(HZ/2, "Rebuilding cache"); | ||
1559 | dircache_suspend(); | ||
1560 | *(int *)lists->data = dircache_resume(); | ||
1561 | case ACTION_UNKNOWN: | ||
1562 | btn = ACTION_NONE; | ||
1563 | break; | ||
1564 | #ifdef DIRCACHE_DUMPSTER | ||
1565 | case ACTION_STD_OK: | ||
1566 | splash(0, "Dumping cache"); | ||
1567 | dircache_dump(); | ||
1568 | btn = ACTION_NONE; | ||
1569 | break; | ||
1570 | #endif /* DIRCACHE_DUMPSTER */ | ||
1571 | case ACTION_STD_CANCEL: | ||
1572 | if (*(int *)lists->data > 0 && info.status == DIRCACHE_SCANNING) | ||
1573 | { | ||
1574 | splash(HZ, str(LANG_SCANNING_DISK)); | ||
1575 | btn = ACTION_NONE; | ||
1576 | } | ||
1577 | break; | ||
1578 | } | ||
1579 | } | ||
1580 | |||
1546 | simplelist_set_line_count(0); | 1581 | simplelist_set_line_count(0); |
1547 | simplelist_addline("Cache initialized: %s", | 1582 | |
1548 | dircache_is_enabled() ? "Yes" : "No"); | 1583 | simplelist_addline("Cache status: %s", info.statusdesc); |
1549 | simplelist_addline("Cache size: %d B", | 1584 | simplelist_addline("Last size: %lu B", info.last_size); |
1550 | dircache_get_cache_size()); | 1585 | simplelist_addline("Size: %lu B", info.size); |
1551 | simplelist_addline("Last size: %d B", | 1586 | unsigned int utilized = info.size ? 1000ull*info.sizeused / info.size : 0; |
1552 | global_status.dircache_size); | 1587 | simplelist_addline("Used: %lu B (%u.%u%%)", info.sizeused, |
1553 | simplelist_addline("Limit: %d B", | 1588 | utilized / 10, utilized % 10); |
1554 | DIRCACHE_LIMIT); | 1589 | simplelist_addline("Limit: %lu B", info.size_limit); |
1555 | simplelist_addline("Reserve: %d/%d B", | 1590 | simplelist_addline("Reserve: %lu/%lu B", info.reserve_used, info.reserve); |
1556 | dircache_get_reserve_used(), DIRCACHE_RESERVE); | 1591 | long ticks = ALIGN_UP(info.build_ticks, HZ / 10); |
1557 | simplelist_addline("Scanning took: %d s", | 1592 | simplelist_addline("Scanning took: %ld.%ld s", |
1558 | dircache_get_build_ticks() / HZ); | 1593 | ticks / HZ, (ticks*10 / HZ) % 10); |
1559 | simplelist_addline("Entry count: %d", | 1594 | simplelist_addline("Entry count: %u", info.entry_count); |
1560 | dircache_get_entry_count()); | 1595 | |
1596 | if (btn == ACTION_NONE) | ||
1597 | btn = ACTION_REDRAW; | ||
1598 | |||
1561 | return btn; | 1599 | return btn; |
1600 | (void)lists; | ||
1562 | } | 1601 | } |
1563 | 1602 | ||
1564 | static bool dbg_dircache_info(void) | 1603 | static bool dbg_dircache_info(void) |
1565 | { | 1604 | { |
1566 | struct simplelist_info info; | 1605 | struct simplelist_info info; |
1567 | simplelist_info_init(&info, "Dircache Info", 7, NULL); | 1606 | int syncbuild = 0; |
1607 | simplelist_info_init(&info, "Dircache Info", 8, &syncbuild); | ||
1568 | info.action_callback = dircache_callback; | 1608 | info.action_callback = dircache_callback; |
1569 | info.hide_selection = true; | 1609 | info.hide_selection = true; |
1570 | info.scroll_all = true; | 1610 | info.scroll_all = true; |
diff --git a/apps/filetree.c b/apps/filetree.c index 319b5f4a77..64283b274b 100644 --- a/apps/filetree.c +++ b/apps/filetree.c | |||
@@ -376,9 +376,7 @@ int ft_load(struct tree_context* c, const char* tempdir) | |||
376 | ++files_in_dir; | 376 | ++files_in_dir; |
377 | 377 | ||
378 | dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used; | 378 | dptr->name = core_get_data(c->cache.name_buffer_handle)+name_buffer_used; |
379 | dptr->time_write = | 379 | dptr->time_write = info.mtime; |
380 | (long)info.wrtdate<<16 | | ||
381 | (long)info.wrttime; /* in one # */ | ||
382 | strcpy(dptr->name, (char *)entry->d_name); | 380 | strcpy(dptr->name, (char *)entry->d_name); |
383 | name_buffer_used += len + 1; | 381 | name_buffer_used += len + 1; |
384 | 382 | ||
diff --git a/apps/main.c b/apps/main.c index 6c6f0d6aba..9098180fb8 100644 --- a/apps/main.c +++ b/apps/main.c | |||
@@ -19,11 +19,12 @@ | |||
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "config.h" | 21 | #include "config.h" |
22 | #include "system.h" | ||
22 | 23 | ||
23 | #include "gcc_extensions.h" | 24 | #include "gcc_extensions.h" |
24 | #include "storage.h" | 25 | #include "storage.h" |
25 | #include "disk.h" | 26 | #include "disk.h" |
26 | #include "fat.h" | 27 | #include "file_internal.h" |
27 | #include "lcd.h" | 28 | #include "lcd.h" |
28 | #include "rtc.h" | 29 | #include "rtc.h" |
29 | #include "debug.h" | 30 | #include "debug.h" |
@@ -34,7 +35,6 @@ | |||
34 | #include "filetypes.h" | 35 | #include "filetypes.h" |
35 | #include "panic.h" | 36 | #include "panic.h" |
36 | #include "menu.h" | 37 | #include "menu.h" |
37 | #include "system.h" | ||
38 | #include "usb.h" | 38 | #include "usb.h" |
39 | #include "powermgmt.h" | 39 | #include "powermgmt.h" |
40 | #include "adc.h" | 40 | #include "adc.h" |
@@ -203,80 +203,53 @@ int main(void) | |||
203 | root_menu(); | 203 | root_menu(); |
204 | } | 204 | } |
205 | 205 | ||
206 | static int init_dircache(bool preinit) INIT_ATTR; | ||
207 | static int init_dircache(bool preinit) | ||
208 | { | ||
209 | #ifdef HAVE_DIRCACHE | 206 | #ifdef HAVE_DIRCACHE |
210 | int result = 0; | 207 | static int INIT_ATTR init_dircache(bool preinit) |
211 | bool clear = false; | 208 | { |
212 | |||
213 | if (preinit) | 209 | if (preinit) |
214 | dircache_init(); | 210 | dircache_init(MAX(global_status.dircache_size, 0)); |
215 | 211 | ||
216 | if (!global_settings.dircache) | 212 | if (!global_settings.dircache) |
217 | return 0; | 213 | return -1; |
214 | |||
215 | int result = -1; | ||
218 | 216 | ||
219 | # ifdef HAVE_EEPROM_SETTINGS | 217 | #ifdef HAVE_EEPROM_SETTINGS |
220 | if (firmware_settings.initialized && firmware_settings.disk_clean | 218 | if (firmware_settings.initialized && |
221 | && preinit) | 219 | firmware_settings.disk_clean && |
220 | preinit) | ||
222 | { | 221 | { |
223 | result = dircache_load(); | 222 | result = dircache_load(); |
224 | |||
225 | if (result < 0) | 223 | if (result < 0) |
226 | { | ||
227 | firmware_settings.disk_clean = false; | 224 | firmware_settings.disk_clean = false; |
228 | if (global_status.dircache_size <= 0) | ||
229 | { | ||
230 | /* This will be in default language, settings are not | ||
231 | applied yet. Not really any easy way to fix that. */ | ||
232 | splash(0, str(LANG_SCANNING_DISK)); | ||
233 | clear = true; | ||
234 | } | ||
235 | |||
236 | dircache_build(global_status.dircache_size); | ||
237 | } | ||
238 | } | 225 | } |
239 | else | 226 | else |
240 | # endif | 227 | #endif /* HAVE_EEPROM_SETTINGS */ |
228 | if (!preinit) | ||
241 | { | 229 | { |
242 | if (preinit) | 230 | result = dircache_enable(); |
243 | return -1; | 231 | if (result != 0) |
244 | |||
245 | if (!dircache_is_enabled() | ||
246 | && !dircache_is_initializing()) | ||
247 | { | 232 | { |
248 | if (global_status.dircache_size <= 0) | 233 | if (result > 0) |
249 | { | 234 | { |
235 | /* Print "Scanning disk..." to the display. */ | ||
250 | splash(0, str(LANG_SCANNING_DISK)); | 236 | splash(0, str(LANG_SCANNING_DISK)); |
251 | clear = true; | 237 | dircache_wait(); |
238 | backlight_on(); | ||
239 | show_logo(); | ||
252 | } | 240 | } |
253 | result = dircache_build(global_status.dircache_size); | ||
254 | } | ||
255 | 241 | ||
256 | if (result < 0) | 242 | struct dircache_info info; |
257 | { | 243 | dircache_get_info(&info); |
258 | /* Initialization of dircache failed. Manual action is | 244 | global_status.dircache_size = info.size; |
259 | * necessary to enable dircache again. | 245 | status_save(); |
260 | */ | ||
261 | splashf(0, "Dircache failed, disabled. Result: %d", result); | ||
262 | global_settings.dircache = false; | ||
263 | } | 246 | } |
264 | } | 247 | /* else don't wait or already enabled by load */ |
265 | |||
266 | if (clear) | ||
267 | { | ||
268 | backlight_on(); | ||
269 | show_logo(); | ||
270 | global_status.dircache_size = dircache_get_cache_size(); | ||
271 | status_save(); | ||
272 | } | 248 | } |
273 | 249 | ||
274 | return result; | 250 | return result; |
275 | #else | ||
276 | (void)preinit; | ||
277 | return 0; | ||
278 | #endif | ||
279 | } | 251 | } |
252 | #endif /* HAVE_DIRCACHE */ | ||
280 | 253 | ||
281 | #ifdef HAVE_TAGCACHE | 254 | #ifdef HAVE_TAGCACHE |
282 | static void init_tagcache(void) INIT_ATTR; | 255 | static void init_tagcache(void) INIT_ATTR; |
@@ -363,6 +336,7 @@ static void init(void) | |||
363 | button_init(); | 336 | button_init(); |
364 | powermgmt_init(); | 337 | powermgmt_init(); |
365 | backlight_init(); | 338 | backlight_init(); |
339 | unicode_init(); | ||
366 | #ifdef SIMULATOR | 340 | #ifdef SIMULATOR |
367 | sim_tasks_init(); | 341 | sim_tasks_init(); |
368 | #endif | 342 | #endif |
@@ -392,8 +366,10 @@ static void init(void) | |||
392 | settings_reset(); | 366 | settings_reset(); |
393 | settings_load(SETTINGS_ALL); | 367 | settings_load(SETTINGS_ALL); |
394 | settings_apply(true); | 368 | settings_apply(true); |
369 | #ifdef HAVE_DIRCACHE | ||
395 | init_dircache(true); | 370 | init_dircache(true); |
396 | init_dircache(false); | 371 | init_dircache(false); |
372 | #endif | ||
397 | #ifdef HAVE_TAGCACHE | 373 | #ifdef HAVE_TAGCACHE |
398 | init_tagcache(); | 374 | init_tagcache(); |
399 | #endif | 375 | #endif |
@@ -429,6 +405,8 @@ static void init(void) | |||
429 | 405 | ||
430 | #else | 406 | #else |
431 | 407 | ||
408 | #include "errno.h" | ||
409 | |||
432 | static void init(void) INIT_ATTR; | 410 | static void init(void) INIT_ATTR; |
433 | static void init(void) | 411 | static void init(void) |
434 | { | 412 | { |
@@ -443,6 +421,9 @@ static void init(void) | |||
443 | core_allocator_init(); | 421 | core_allocator_init(); |
444 | kernel_init(); | 422 | kernel_init(); |
445 | 423 | ||
424 | /* early early early! */ | ||
425 | filesystem_init(); | ||
426 | |||
446 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 427 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
447 | set_cpu_frequency(CPUFREQ_NORMAL); | 428 | set_cpu_frequency(CPUFREQ_NORMAL); |
448 | #ifdef CPU_COLDFIRE | 429 | #ifdef CPU_COLDFIRE |
@@ -462,6 +443,7 @@ static void init(void) | |||
462 | /* current_tick should be ticking by now */ | 443 | /* current_tick should be ticking by now */ |
463 | CHART("ticking"); | 444 | CHART("ticking"); |
464 | 445 | ||
446 | unicode_init(); | ||
465 | lcd_init(); | 447 | lcd_init(); |
466 | #ifdef HAVE_REMOTE_LCD | 448 | #ifdef HAVE_REMOTE_LCD |
467 | lcd_remote_init(); | 449 | lcd_remote_init(); |
@@ -558,8 +540,6 @@ static void init(void) | |||
558 | } | 540 | } |
559 | #endif | 541 | #endif |
560 | 542 | ||
561 | |||
562 | disk_init_subsystem(); | ||
563 | CHART(">storage_init"); | 543 | CHART(">storage_init"); |
564 | rc = storage_init(); | 544 | rc = storage_init(); |
565 | CHART("<storage_init"); | 545 | CHART("<storage_init"); |
@@ -661,22 +641,24 @@ static void init(void) | |||
661 | CHART("<settings_load(ALL)"); | 641 | CHART("<settings_load(ALL)"); |
662 | } | 642 | } |
663 | 643 | ||
644 | #ifdef HAVE_DIRCACHE | ||
664 | CHART(">init_dircache(true)"); | 645 | CHART(">init_dircache(true)"); |
665 | rc = init_dircache(true); | 646 | rc = init_dircache(true); |
666 | CHART("<init_dircache(true)"); | 647 | CHART("<init_dircache(true)"); |
667 | if (rc < 0) | ||
668 | { | ||
669 | #ifdef HAVE_TAGCACHE | 648 | #ifdef HAVE_TAGCACHE |
649 | if (rc < 0) | ||
670 | remove(TAGCACHE_STATEFILE); | 650 | remove(TAGCACHE_STATEFILE); |
671 | #endif | 651 | #endif /* HAVE_TAGCACHE */ |
672 | } | 652 | #endif /* HAVE_DIRCACHE */ |
673 | 653 | ||
674 | CHART(">settings_apply(true)"); | 654 | CHART(">settings_apply(true)"); |
675 | settings_apply(true); | 655 | settings_apply(true); |
676 | CHART("<settings_apply(true)"); | 656 | CHART("<settings_apply(true)"); |
657 | #ifdef HAVE_DIRCACHE | ||
677 | CHART(">init_dircache(false)"); | 658 | CHART(">init_dircache(false)"); |
678 | init_dircache(false); | 659 | init_dircache(false); |
679 | CHART("<init_dircache(false)"); | 660 | CHART("<init_dircache(false)"); |
661 | #endif | ||
680 | #ifdef HAVE_TAGCACHE | 662 | #ifdef HAVE_TAGCACHE |
681 | CHART(">init_tagcache"); | 663 | CHART(">init_tagcache"); |
682 | init_tagcache(); | 664 | init_tagcache(); |
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c index 3e1443d02e..948dcede00 100644 --- a/apps/menus/display_menu.c +++ b/apps/menus/display_menu.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #endif | 43 | #endif |
44 | #include "viewport.h" | 44 | #include "viewport.h" |
45 | #include "statusbar.h" /* statusbar_vals enum*/ | 45 | #include "statusbar.h" /* statusbar_vals enum*/ |
46 | #include "rbunicode.h" | ||
46 | 47 | ||
47 | #ifdef HAVE_BACKLIGHT | 48 | #ifdef HAVE_BACKLIGHT |
48 | static int filterfirstkeypress_callback(int action,const struct menu_item_ex *this_item) | 49 | 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, | |||
524 | &touchscreen_menu_calibrate, &touchscreen_menu_reset_calibration); | 525 | &touchscreen_menu_calibrate, &touchscreen_menu_reset_calibration); |
525 | #endif | 526 | #endif |
526 | 527 | ||
528 | static int codepage_callback(int action, const struct menu_item_ex *this_item) | ||
529 | { | ||
530 | static int old_codepage; | ||
531 | int new_codepage = global_settings.default_codepage; | ||
532 | (void)this_item; | ||
533 | switch (action) | ||
534 | { | ||
535 | case ACTION_ENTER_MENUITEM: | ||
536 | old_codepage = new_codepage; | ||
537 | break; | ||
538 | case ACTION_EXIT_MENUITEM: | ||
539 | if (new_codepage != old_codepage) | ||
540 | set_codepage(new_codepage); | ||
541 | break; | ||
542 | } | ||
543 | return action; | ||
544 | } | ||
527 | 545 | ||
528 | MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, NULL); | 546 | MENUITEM_SETTING(codepage_setting, &global_settings.default_codepage, codepage_callback); |
529 | 547 | ||
530 | 548 | ||
531 | MAKE_MENU(display_menu, ID2P(LANG_DISPLAY), | 549 | MAKE_MENU(display_menu, ID2P(LANG_DISPLAY), |
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c index 6a1295996c..8764101f73 100644 --- a/apps/menus/main_menu.c +++ b/apps/menus/main_menu.c | |||
@@ -48,6 +48,7 @@ | |||
48 | #include "time.h" | 48 | #include "time.h" |
49 | #include "wps.h" | 49 | #include "wps.h" |
50 | #include "skin_buffer.h" | 50 | #include "skin_buffer.h" |
51 | #include "disk.h" | ||
51 | 52 | ||
52 | static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG}; | 53 | static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG}; |
53 | 54 | ||
@@ -160,14 +161,14 @@ static const char* info_getname(int selected_item, void *data, | |||
160 | #endif | 161 | #endif |
161 | if (info->new_data) | 162 | if (info->new_data) |
162 | { | 163 | { |
163 | fat_size(IF_MV(0,) &info->size, &info->free); | 164 | volume_size(IF_MV(0,) &info->size, &info->free); |
164 | #ifdef HAVE_MULTIVOLUME | 165 | #ifdef HAVE_MULTIVOLUME |
165 | #ifndef APPLICATION | 166 | #ifndef APPLICATION |
166 | if (fat_ismounted(1)) | 167 | volume_size(1, &info->size2, &info->free2); |
167 | fat_size(1, &info->size2, &info->free2); | 168 | #else |
168 | else | 169 | info->size2 = 0; |
169 | #endif | 170 | #endif |
170 | info->size2 = 0; | 171 | |
171 | #endif | 172 | #endif |
172 | info->new_data = false; | 173 | info->new_data = false; |
173 | } | 174 | } |
@@ -347,12 +348,7 @@ static int info_action_callback(int action, struct gui_synclist *lists) | |||
347 | info->new_data = true; | 348 | info->new_data = true; |
348 | splash(0, ID2P(LANG_SCANNING_DISK)); | 349 | splash(0, ID2P(LANG_SCANNING_DISK)); |
349 | for (i = 0; i < NUM_VOLUMES; i++) | 350 | for (i = 0; i < NUM_VOLUMES; i++) |
350 | { | 351 | volume_recalc_free(IF_MV(i)); |
351 | #ifdef HAVE_HOTSWAP | ||
352 | if (fat_ismounted(i)) | ||
353 | #endif | ||
354 | fat_recalc_free(IF_MV(i)); | ||
355 | } | ||
356 | #else | 352 | #else |
357 | (void) lists; | 353 | (void) lists; |
358 | #endif | 354 | #endif |
diff --git a/apps/menus/settings_menu.c b/apps/menus/settings_menu.c index 95423a20fa..0d2a7febf1 100644 --- a/apps/menus/settings_menu.c +++ b/apps/menus/settings_menu.c | |||
@@ -209,12 +209,12 @@ static int dircache_callback(int action,const struct menu_item_ex *this_item) | |||
209 | switch (action) | 209 | switch (action) |
210 | { | 210 | { |
211 | case ACTION_EXIT_MENUITEM: /* on exit */ | 211 | case ACTION_EXIT_MENUITEM: /* on exit */ |
212 | if (global_settings.dircache && !dircache_is_enabled()) | 212 | if (global_settings.dircache) |
213 | { | 213 | { |
214 | if (dircache_build(0) < 0) | 214 | if (dircache_enable() < 0) |
215 | splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); | 215 | splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); |
216 | } | 216 | } |
217 | else if (!global_settings.dircache && dircache_is_enabled()) | 217 | else |
218 | { | 218 | { |
219 | dircache_disable(); | 219 | dircache_disable(); |
220 | } | 220 | } |
diff --git a/apps/misc.c b/apps/misc.c index f847023c31..b6eaafb599 100644 --- a/apps/misc.c +++ b/apps/misc.c | |||
@@ -28,9 +28,12 @@ | |||
28 | #include "misc.h" | 28 | #include "misc.h" |
29 | #include "system.h" | 29 | #include "system.h" |
30 | #include "lcd.h" | 30 | #include "lcd.h" |
31 | #ifdef HAVE_DIRCACHE | ||
32 | #include "dircache.h" | ||
33 | #endif | ||
31 | #include "file.h" | 34 | #include "file.h" |
32 | #ifndef __PCTOOL__ | 35 | #ifndef __PCTOOL__ |
33 | #include "filefuncs.h" | 36 | #include "pathfuncs.h" |
34 | #include "lang.h" | 37 | #include "lang.h" |
35 | #include "dir.h" | 38 | #include "dir.h" |
36 | #ifdef HAVE_REMOTE_LCD | 39 | #ifdef HAVE_REMOTE_LCD |
@@ -744,8 +747,7 @@ int show_logo( void ) | |||
744 | */ | 747 | */ |
745 | void check_bootfile(bool do_rolo) | 748 | void check_bootfile(bool do_rolo) |
746 | { | 749 | { |
747 | static unsigned short wrtdate = 0; | 750 | static time_t mtime = 0; |
748 | static unsigned short wrttime = 0; | ||
749 | DIR* dir = NULL; | 751 | DIR* dir = NULL; |
750 | struct dirent* entry = NULL; | 752 | struct dirent* entry = NULL; |
751 | 753 | ||
@@ -761,10 +763,9 @@ void check_bootfile(bool do_rolo) | |||
761 | { | 763 | { |
762 | struct dirinfo info = dir_get_info(dir, entry); | 764 | struct dirinfo info = dir_get_info(dir, entry); |
763 | /* found the bootfile */ | 765 | /* found the bootfile */ |
764 | if(wrtdate && do_rolo) | 766 | if(mtime && do_rolo) |
765 | { | 767 | { |
766 | if((info.wrtdate != wrtdate) || | 768 | if(info.mtime != mtime) |
767 | (info.wrttime != wrttime)) | ||
768 | { | 769 | { |
769 | static const char *lines[] = { ID2P(LANG_BOOT_CHANGED), | 770 | static const char *lines[] = { ID2P(LANG_BOOT_CHANGED), |
770 | ID2P(LANG_REBOOT_NOW) }; | 771 | ID2P(LANG_REBOOT_NOW) }; |
@@ -777,8 +778,7 @@ void check_bootfile(bool do_rolo) | |||
777 | } | 778 | } |
778 | } | 779 | } |
779 | } | 780 | } |
780 | wrtdate = info.wrtdate; | 781 | mtime = info.mtime; |
781 | wrttime = info.wrttime; | ||
782 | } | 782 | } |
783 | } | 783 | } |
784 | closedir(dir); | 784 | closedir(dir); |
diff --git a/apps/onplay.c b/apps/onplay.c index 7c5f517090..091680e949 100644 --- a/apps/onplay.c +++ b/apps/onplay.c | |||
@@ -62,16 +62,13 @@ | |||
62 | #include "statusbar-skinned.h" | 62 | #include "statusbar-skinned.h" |
63 | #include "pitchscreen.h" | 63 | #include "pitchscreen.h" |
64 | #include "viewport.h" | 64 | #include "viewport.h" |
65 | #include "filefuncs.h" | 65 | #include "pathfuncs.h" |
66 | #include "shortcuts.h" | 66 | #include "shortcuts.h" |
67 | 67 | ||
68 | static int context; | 68 | static int context; |
69 | static const char* selected_file = NULL; | 69 | static const char *selected_file = NULL; |
70 | static int selected_file_attr = 0; | 70 | static int selected_file_attr = 0; |
71 | static int onplay_result = ONPLAY_OK; | 71 | static int onplay_result = ONPLAY_OK; |
72 | static char clipboard_selection[MAX_PATH]; | ||
73 | static int clipboard_selection_attr = 0; | ||
74 | static bool clipboard_is_copy = false; | ||
75 | 72 | ||
76 | /* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */ | 73 | /* redefine MAKE_MENU so the MENU_EXITAFTERTHISMENU flag can be added easily */ |
77 | #define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \ | 74 | #define MAKE_ONPLAYMENU( name, str, callback, icon, ... ) \ |
@@ -82,6 +79,63 @@ static bool clipboard_is_copy = false; | |||
82 | MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ | 79 | MENU_ITEM_COUNT(sizeof( name##_)/sizeof(*name##_)), \ |
83 | { (void*)name##_},{.callback_and_desc = & name##__}}; | 80 | { (void*)name##_},{.callback_and_desc = & name##__}}; |
84 | 81 | ||
82 | /* Used for directory move, copy and delete */ | ||
83 | struct dirrecurse_params | ||
84 | { | ||
85 | char path[MAX_PATH]; /* Buffer for full path */ | ||
86 | size_t append; /* Append position in 'path' for stack push */ | ||
87 | }; | ||
88 | |||
89 | enum clipboard_op_flags | ||
90 | { | ||
91 | PASTE_CUT = 0x00, /* Is a move (cut) operation (default) */ | ||
92 | PASTE_COPY = 0x01, /* Is a copy operation */ | ||
93 | PASTE_OVERWRITE = 0x02, /* Overwrite destination */ | ||
94 | PASTE_EXDEV = 0x04, /* Actually copy/move across volumes */ | ||
95 | }; | ||
96 | |||
97 | /* result codec of various onplay operations */ | ||
98 | enum onplay_result_code | ||
99 | { | ||
100 | /* Anything < 0 is failure */ | ||
101 | OPRC_SUCCESS = 0, /* All operations completed successfully */ | ||
102 | OPRC_NOOP = 1, /* Operation didn't need to do anything */ | ||
103 | OPRC_CANCELLED = 2, /* Operation was cancelled by user */ | ||
104 | OPRC_NOOVERWRT = 3, | ||
105 | }; | ||
106 | |||
107 | static struct clipboard | ||
108 | { | ||
109 | char path[MAX_PATH]; /* Clipped file's path */ | ||
110 | unsigned int attr; /* Clipped file's attributes */ | ||
111 | unsigned int flags; /* Operation type flags */ | ||
112 | } clipboard; | ||
113 | |||
114 | /* Empty the clipboard */ | ||
115 | static void clipboard_clear_selection(struct clipboard *clip) | ||
116 | { | ||
117 | clip->path[0] = '\0'; | ||
118 | clip->attr = 0; | ||
119 | clip->flags = 0; | ||
120 | } | ||
121 | |||
122 | /* Store the selection in the clipboard */ | ||
123 | static bool clipboard_clip(struct clipboard *clip, const char *path, | ||
124 | unsigned int attr, unsigned int flags) | ||
125 | { | ||
126 | /* if it fits it clips */ | ||
127 | if (strlcpy(clip->path, path, sizeof (clip->path)) | ||
128 | < sizeof (clip->path)) { | ||
129 | clip->attr = attr; | ||
130 | clip->flags = flags; | ||
131 | return true; | ||
132 | } | ||
133 | else { | ||
134 | clipboard_clear_selection(clip); | ||
135 | return false; | ||
136 | } | ||
137 | } | ||
138 | |||
85 | /* ----------------------------------------------------------------------- */ | 139 | /* ----------------------------------------------------------------------- */ |
86 | /* Displays the bookmark menu options for the user to decide. This is an */ | 140 | /* Displays the bookmark menu options for the user to decide. This is an */ |
87 | /* interface function. */ | 141 | /* interface function. */ |
@@ -492,438 +546,578 @@ static void draw_slider(void) | |||
492 | #define draw_slider() | 546 | #define draw_slider() |
493 | #endif | 547 | #endif |
494 | 548 | ||
495 | /* helper function to remove a non-empty directory */ | 549 | static void clear_display(bool update) |
496 | static int remove_dir(char* dirname, int len) | ||
497 | { | 550 | { |
498 | int result = 0; | 551 | struct viewport vp; |
499 | DIR* dir; | ||
500 | int dirlen = strlen(dirname); | ||
501 | 552 | ||
502 | dir = opendir(dirname); | 553 | FOR_NB_SCREENS(i) |
503 | if (!dir) | 554 | { |
555 | struct screen * screen = &screens[i]; | ||
556 | viewport_set_defaults(&vp, screen->screen_type); | ||
557 | screen->set_viewport(&vp); | ||
558 | screen->clear_viewport(); | ||
559 | if (update) { | ||
560 | screen->update_viewport(); | ||
561 | } | ||
562 | screen->set_viewport(NULL); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | static void splash_path(const char *path) | ||
567 | { | ||
568 | clear_display(false); | ||
569 | path_basename(path, &path); | ||
570 | splash(0, path); | ||
571 | draw_slider(); | ||
572 | } | ||
573 | |||
574 | /* Splashes the path and checks the keys */ | ||
575 | static bool poll_cancel_action(const char *path) | ||
576 | { | ||
577 | splash_path(path); | ||
578 | return ACTION_STD_CANCEL == get_action(CONTEXT_STD, TIMEOUT_NOBLOCK); | ||
579 | } | ||
580 | |||
581 | static int confirm_overwrite(void) | ||
582 | { | ||
583 | static const char *lines[] = { ID2P(LANG_REALLY_OVERWRITE) }; | ||
584 | static const struct text_message message = { lines, 1 }; | ||
585 | return gui_syncyesno_run(&message, NULL, NULL); | ||
586 | } | ||
587 | |||
588 | static int confirm_delete(const char *file) | ||
589 | { | ||
590 | const char *lines[] = { ID2P(LANG_REALLY_DELETE), file }; | ||
591 | const char *yes_lines[] = { ID2P(LANG_DELETING), file }; | ||
592 | const struct text_message message = { lines, 2 }; | ||
593 | const struct text_message yes_message = { yes_lines, 2 }; | ||
594 | return gui_syncyesno_run(&message, &yes_message, NULL); | ||
595 | } | ||
596 | |||
597 | static bool check_new_name(const char *basename) | ||
598 | { | ||
599 | /* at least prevent escapes out of the base directory from keyboard- | ||
600 | entered filenames; the file code should reject other invalidities */ | ||
601 | return *basename != '\0' && !strchr(basename, PATH_SEPCH) && | ||
602 | !is_dotdir_name(basename); | ||
603 | } | ||
604 | |||
605 | static void splash_cancelled(void) | ||
606 | { | ||
607 | clear_display(true); | ||
608 | splash(HZ, ID2P(LANG_CANCEL)); | ||
609 | } | ||
610 | |||
611 | static void splash_failed(int lang_what) | ||
612 | { | ||
613 | cond_talk_ids_fq(lang_what, LANG_FAILED); | ||
614 | clear_display(true); | ||
615 | splashf(HZ*2, "%s %s", str(lang_what), str(LANG_FAILED)); | ||
616 | } | ||
617 | |||
618 | /* helper function to remove a non-empty directory */ | ||
619 | static int remove_dir(struct dirrecurse_params *parm) | ||
620 | { | ||
621 | DIR *dir = opendir(parm->path); | ||
622 | if (!dir) { | ||
504 | return -1; /* open error */ | 623 | return -1; /* open error */ |
624 | } | ||
505 | 625 | ||
506 | while(true) | 626 | size_t append = parm->append; |
507 | { | 627 | int rc = OPRC_SUCCESS; |
508 | struct dirent* entry; | 628 | |
509 | /* walk through the directory content */ | 629 | /* walk through the directory content */ |
510 | entry = readdir(dir); | 630 | while (rc == OPRC_SUCCESS) { |
511 | if (!entry) | 631 | errno = 0; /* distinguish failure from eod */ |
632 | struct dirent *entry = readdir(dir); | ||
633 | if (!entry) { | ||
634 | if (errno) { | ||
635 | rc = -1; | ||
636 | } | ||
512 | break; | 637 | break; |
638 | } | ||
639 | |||
513 | struct dirinfo info = dir_get_info(dir, entry); | 640 | struct dirinfo info = dir_get_info(dir, entry); |
514 | dirname[dirlen] ='\0'; | 641 | if ((info.attribute & ATTR_DIRECTORY) && |
515 | /* inform the user which dir we're deleting */ | 642 | is_dotdir_name(entry->d_name)) { |
516 | splash(0, dirname); | 643 | continue; /* skip these */ |
644 | } | ||
517 | 645 | ||
518 | /* append name to current directory */ | 646 | /* append name to current directory */ |
519 | snprintf(dirname+dirlen, len-dirlen, "/%s", entry->d_name); | 647 | parm->append = append + path_append(&parm->path[append], |
520 | if (info.attribute & ATTR_DIRECTORY) | 648 | PA_SEP_HARD, entry->d_name, |
521 | { /* remove a subdirectory */ | 649 | sizeof (parm->path) - append); |
522 | if (!strcmp((char *)entry->d_name, ".") || | 650 | if (parm->append >= sizeof (parm->path)) { |
523 | !strcmp((char *)entry->d_name, "..")) | 651 | rc = -1; |
524 | continue; /* skip these */ | 652 | break; /* no space left in buffer */ |
525 | |||
526 | result = remove_dir(dirname, len); /* recursion */ | ||
527 | if (result) | ||
528 | break; /* or better continue, delete what we can? */ | ||
529 | } | ||
530 | else | ||
531 | { /* remove a file */ | ||
532 | draw_slider(); | ||
533 | result = remove(dirname); | ||
534 | } | 653 | } |
535 | if(ACTION_STD_CANCEL == get_action(CONTEXT_STD,TIMEOUT_NOBLOCK)) | 654 | |
536 | { | 655 | if (info.attribute & ATTR_DIRECTORY) { |
537 | splash(HZ, ID2P(LANG_CANCEL)); | 656 | /* remove a subdirectory */ |
538 | result = -1; | 657 | rc = remove_dir(parm); |
539 | break; | 658 | } else { |
659 | /* remove a file */ | ||
660 | if (poll_cancel_action(parm->path)) { | ||
661 | rc = OPRC_CANCELLED; | ||
662 | break; | ||
663 | } | ||
664 | |||
665 | rc = remove(parm->path); | ||
540 | } | 666 | } |
667 | |||
668 | /* Remove basename we added above */ | ||
669 | parm->path[append] = '\0'; | ||
541 | } | 670 | } |
542 | closedir(dir); | ||
543 | 671 | ||
544 | if (!result) | 672 | closedir(dir); |
545 | { /* remove the now empty directory */ | ||
546 | dirname[dirlen] = '\0'; /* terminate to original length */ | ||
547 | 673 | ||
548 | result = rmdir(dirname); | 674 | if (rc == 0) { |
675 | /* remove the now empty directory */ | ||
676 | if (poll_cancel_action(parm->path)) { | ||
677 | rc = OPRC_CANCELLED; | ||
678 | } else { | ||
679 | rc = rmdir(parm->path); | ||
680 | } | ||
549 | } | 681 | } |
550 | 682 | ||
551 | return result; | 683 | return rc; |
552 | } | 684 | } |
553 | 685 | ||
554 | |||
555 | /* share code for file and directory deletion, saves space */ | 686 | /* share code for file and directory deletion, saves space */ |
556 | static bool delete_file_dir(void) | 687 | static int delete_file_dir(void) |
557 | { | 688 | { |
558 | char file_to_delete[MAX_PATH]; | 689 | if (confirm_delete(selected_file) != YESNO_YES) { |
559 | strcpy(file_to_delete, selected_file); | 690 | return 1; |
691 | } | ||
560 | 692 | ||
561 | const char *lines[]={ | 693 | clear_display(true); |
562 | ID2P(LANG_REALLY_DELETE), | 694 | splash(HZ/2, str(LANG_DELETING)); |
563 | file_to_delete | ||
564 | }; | ||
565 | const char *yes_lines[]={ | ||
566 | ID2P(LANG_DELETING), | ||
567 | file_to_delete | ||
568 | }; | ||
569 | 695 | ||
570 | const struct text_message message={lines, 2}; | 696 | int rc = -1; |
571 | const struct text_message yes_message={yes_lines, 2}; | ||
572 | 697 | ||
573 | if(gui_syncyesno_run(&message, &yes_message, NULL)!=YESNO_YES) | 698 | if (selected_file_attr & ATTR_DIRECTORY) { /* true if directory */ |
574 | return false; | 699 | struct dirrecurse_params parm; |
700 | parm.append = strlcpy(parm.path, selected_file, sizeof (parm.path)); | ||
575 | 701 | ||
576 | splash(0, str(LANG_DELETING)); | 702 | if (parm.append < sizeof (parm.path)) { |
703 | cpu_boost(true); | ||
704 | rc = remove_dir(&parm); | ||
705 | cpu_boost(false); | ||
706 | } | ||
707 | } else { | ||
708 | rc = remove(selected_file); | ||
709 | } | ||
577 | 710 | ||
578 | int res; | 711 | if (rc < OPRC_SUCCESS) { |
579 | if (selected_file_attr & ATTR_DIRECTORY) /* true if directory */ | 712 | splash_failed(LANG_DELETE); |
580 | { | 713 | } else if (rc == OPRC_CANCELLED) { |
581 | char pathname[MAX_PATH]; /* space to go deep */ | 714 | splash_cancelled(); |
582 | cpu_boost(true); | ||
583 | strlcpy(pathname, file_to_delete, sizeof(pathname)); | ||
584 | res = remove_dir(pathname, sizeof(pathname)); | ||
585 | cpu_boost(false); | ||
586 | } | 715 | } |
587 | else | ||
588 | res = remove(file_to_delete); | ||
589 | 716 | ||
590 | if (!res) | 717 | if (rc != OPRC_NOOP) { |
718 | /* Could have failed after some but not all needed changes; reload */ | ||
591 | onplay_result = ONPLAY_RELOAD_DIR; | 719 | onplay_result = ONPLAY_RELOAD_DIR; |
720 | } | ||
592 | 721 | ||
593 | return (res == 0); | 722 | return 1; |
594 | } | 723 | } |
595 | 724 | ||
596 | static bool rename_file(void) | 725 | static int rename_file(void) |
597 | { | 726 | { |
727 | int rc = -1; | ||
598 | char newname[MAX_PATH]; | 728 | char newname[MAX_PATH]; |
599 | char* ptr = strrchr(selected_file, '/') + 1; | 729 | const char *oldbase, *selection = selected_file; |
600 | int pathlen = (ptr - selected_file); | 730 | |
601 | strlcpy(newname, selected_file, sizeof(newname)); | 731 | path_basename(selection, &oldbase); |
602 | if (!kbd_input(newname + pathlen, (sizeof newname)-pathlen)) { | 732 | size_t pathlen = oldbase - selection; |
603 | if (!strlen(newname + pathlen) || | 733 | char *newbase = newname + pathlen; |
604 | (rename(selected_file, newname) < 0)) { | 734 | |
605 | cond_talk_ids_fq(LANG_RENAME, LANG_FAILED); | 735 | if (strlcpy(newname, selection, sizeof (newname)) >= sizeof (newname)) { |
606 | splashf(HZ*2, "%s %s", str(LANG_RENAME), str(LANG_FAILED)); | 736 | /* Too long */ |
737 | } else if (kbd_input(newbase, sizeof (newname) - pathlen) < 0) { | ||
738 | rc = OPRC_CANCELLED; | ||
739 | } else if (!strcmp(oldbase, newbase)) { | ||
740 | rc = OPRC_NOOP; /* No change at all */ | ||
741 | } else if (check_new_name(newbase)) { | ||
742 | switch (relate(selection, newname)) | ||
743 | { | ||
744 | case RELATE_DIFFERENT: | ||
745 | if (file_exists(newname)) { | ||
746 | break; /* don't overwrite */ | ||
747 | } | ||
748 | /* Fall-through */ | ||
749 | case RELATE_SAME: | ||
750 | rc = rename(selection, newname); | ||
751 | break; | ||
752 | case RELATE_PREFIX: | ||
753 | default: | ||
754 | break; | ||
607 | } | 755 | } |
608 | else | ||
609 | onplay_result = ONPLAY_RELOAD_DIR; | ||
610 | } | 756 | } |
611 | 757 | ||
612 | return false; | 758 | if (rc < OPRC_SUCCESS) { |
759 | splash_failed(LANG_RENAME); | ||
760 | } else if (rc == OPRC_CANCELLED) { | ||
761 | /* splash_cancelled(); kbd_input() splashes it */ | ||
762 | } else if (rc == OPRC_SUCCESS) { | ||
763 | onplay_result = ONPLAY_RELOAD_DIR; | ||
764 | } | ||
765 | |||
766 | return 1; | ||
613 | } | 767 | } |
614 | 768 | ||
615 | static bool create_dir(void) | 769 | static int create_dir(void) |
616 | { | 770 | { |
771 | int rc = -1; | ||
617 | char dirname[MAX_PATH]; | 772 | char dirname[MAX_PATH]; |
618 | char *cwd; | 773 | size_t pathlen = path_append(dirname, getcwd(NULL, 0), PA_SEP_HARD, |
619 | int rc; | 774 | sizeof (dirname)); |
620 | int pathlen; | 775 | char *basename = dirname + pathlen; |
621 | 776 | ||
622 | cwd = getcwd(NULL, 0); | 777 | if (pathlen >= sizeof (dirname)) { |
623 | memset(dirname, 0, sizeof dirname); | 778 | /* Too long */ |
624 | 779 | } else if (kbd_input(basename, sizeof (dirname) - pathlen) < 0) { | |
625 | snprintf(dirname, sizeof dirname, "%s/", cwd[1] ? cwd : ""); | 780 | rc = OPRC_CANCELLED; |
626 | 781 | } else if (check_new_name(basename)) { | |
627 | pathlen = strlen(dirname); | 782 | rc = mkdir(dirname); |
628 | rc = kbd_input(dirname + pathlen, (sizeof dirname)-pathlen); | 783 | } |
629 | if (rc < 0) | ||
630 | return false; | ||
631 | 784 | ||
632 | rc = mkdir(dirname); | 785 | if (rc < OPRC_SUCCESS) { |
633 | if (rc < 0) { | 786 | splash_failed(LANG_CREATE_DIR); |
634 | cond_talk_ids_fq(LANG_CREATE_DIR, LANG_FAILED); | 787 | } else if (rc == OPRC_CANCELLED) { |
635 | splashf(HZ, (unsigned char *)"%s %s", str(LANG_CREATE_DIR), | 788 | /* splash_cancelled(); kbd_input() splashes it */ |
636 | str(LANG_FAILED)); | 789 | } else if (rc == OPRC_SUCCESS) { |
637 | } else { | ||
638 | onplay_result = ONPLAY_RELOAD_DIR; | 790 | onplay_result = ONPLAY_RELOAD_DIR; |
639 | } | 791 | } |
640 | 792 | ||
641 | return true; | 793 | return 1; |
642 | } | 794 | } |
643 | 795 | ||
644 | /* Store the current selection in the clipboard */ | 796 | /* Paste a file */ |
645 | static bool clipboard_clip(bool copy) | 797 | static int clipboard_pastefile(const char *src, const char *target, |
798 | unsigned int flags) | ||
646 | { | 799 | { |
647 | clipboard_selection[0] = 0; | 800 | int rc = -1; |
648 | strlcpy(clipboard_selection, selected_file, sizeof(clipboard_selection)); | 801 | |
649 | clipboard_selection_attr = selected_file_attr; | 802 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { |
650 | clipboard_is_copy = copy; | 803 | if ((flags & PASTE_OVERWRITE) || !file_exists(target)) { |
651 | 804 | /* Rename and possibly overwrite the file */ | |
652 | return true; | 805 | if (poll_cancel_action(src)) { |
653 | } | 806 | rc = OPRC_CANCELLED; |
807 | } else { | ||
808 | rc = rename(src, target); | ||
809 | } | ||
654 | 810 | ||
655 | static bool clipboard_cut(void) | 811 | #ifdef HAVE_MULTIVOLUME |
656 | { | 812 | if (rc < 0 && errno == EXDEV) { |
657 | return clipboard_clip(false); | 813 | /* Failed because cross volume rename doesn't work; force |
658 | } | 814 | a move instead */ |
815 | flags |= PASTE_EXDEV; | ||
816 | break; | ||
817 | } | ||
818 | #endif /* HAVE_MULTIVOLUME */ | ||
819 | } | ||
659 | 820 | ||
660 | static bool clipboard_copy(void) | 821 | return rc; |
661 | { | 822 | } |
662 | return clipboard_clip(true); | ||
663 | } | ||
664 | 823 | ||
665 | /* Paste a file to a new directory. Will overwrite always. */ | 824 | /* See if we can get the plugin buffer for the file copy buffer */ |
666 | static bool clipboard_pastefile(const char *src, const char *target, bool copy) | ||
667 | { | ||
668 | int src_fd, target_fd; | ||
669 | size_t buffersize; | 825 | size_t buffersize; |
670 | ssize_t size, bytesread, byteswritten; | 826 | char *buffer = (char *) plugin_get_buffer(&buffersize); |
671 | char *buffer; | 827 | if (buffer == NULL || buffersize < 512) { |
672 | bool result = false; | 828 | /* Not large enough, try for a disk sector worth of stack |
673 | 829 | instead */ | |
674 | if (copy) { | 830 | buffersize = 512; |
675 | /* See if we can get the plugin buffer for the file copy buffer */ | 831 | buffer = (char *)alloca(buffersize); |
676 | buffer = (char *) plugin_get_buffer(&buffersize); | 832 | } |
677 | if (buffer == NULL || buffersize < 512) { | ||
678 | /* Not large enough, try for a disk sector worth of stack | ||
679 | instead */ | ||
680 | buffersize = 512; | ||
681 | buffer = (char *) __builtin_alloca(buffersize); | ||
682 | } | ||
683 | 833 | ||
684 | if (buffer == NULL) { | 834 | if (buffer == NULL) { |
685 | return false; | 835 | return -1; |
686 | } | 836 | } |
687 | 837 | ||
688 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector | 838 | buffersize &= ~0x1ff; /* Round buffer size to multiple of sector |
689 | size */ | 839 | size */ |
690 | 840 | ||
691 | src_fd = open(src, O_RDONLY); | 841 | int src_fd = open(src, O_RDONLY); |
842 | if (src_fd >= 0) { | ||
843 | int oflag = O_WRONLY|O_CREAT; | ||
692 | 844 | ||
693 | if (src_fd >= 0) { | 845 | if (!(flags & PASTE_OVERWRITE)) { |
694 | target_fd = creat(target, 0666); | 846 | oflag |= O_EXCL; |
847 | } | ||
695 | 848 | ||
696 | if (target_fd >= 0) { | 849 | int target_fd = open(target, oflag, 0666); |
697 | result = true; | 850 | if (target_fd >= 0) { |
851 | off_t total_size = 0; | ||
852 | off_t next_cancel_test = 0; /* No excessive button polling */ | ||
698 | 853 | ||
699 | size = filesize(src_fd); | 854 | rc = OPRC_SUCCESS; |
700 | 855 | ||
701 | if (size == -1) { | 856 | while (rc == OPRC_SUCCESS) { |
702 | result = false; | 857 | if (total_size >= next_cancel_test) { |
858 | next_cancel_test = total_size + 0x10000; | ||
859 | if (poll_cancel_action(src)) { | ||
860 | rc = OPRC_CANCELLED; | ||
861 | break; | ||
862 | } | ||
703 | } | 863 | } |
704 | 864 | ||
705 | while(size > 0) { | 865 | ssize_t bytesread = read(src_fd, buffer, buffersize); |
706 | bytesread = read(src_fd, buffer, buffersize); | 866 | if (bytesread <= 0) { |
707 | 867 | if (bytesread < 0) { | |
708 | if (bytesread == -1) { | 868 | rc = -1; |
709 | result = false; | ||
710 | break; | ||
711 | } | 869 | } |
870 | /* else eof on buffer boundary; nothing to write */ | ||
871 | break; | ||
872 | } | ||
712 | 873 | ||
713 | size -= bytesread; | 874 | ssize_t byteswritten = write(target_fd, buffer, bytesread); |
714 | 875 | if (byteswritten < bytesread) { | |
715 | while(bytesread > 0) { | 876 | /* Some I/O error */ |
716 | byteswritten = write(target_fd, buffer, bytesread); | 877 | rc = -1; |
717 | 878 | break; | |
718 | if (byteswritten < 0) { | ||
719 | result = false; | ||
720 | size = 0; | ||
721 | break; | ||
722 | } | ||
723 | |||
724 | bytesread -= byteswritten; | ||
725 | draw_slider(); | ||
726 | } | ||
727 | } | 879 | } |
728 | 880 | ||
729 | close(target_fd); | 881 | total_size += byteswritten; |
730 | 882 | ||
731 | /* Copy failed. Cleanup. */ | 883 | if (bytesread < (ssize_t)buffersize) { |
732 | if (!result) { | 884 | /* EOF with trailing bytes */ |
733 | remove(target); | 885 | break; |
734 | } | 886 | } |
735 | } | 887 | } |
736 | 888 | ||
737 | close(src_fd); | 889 | if (rc == OPRC_SUCCESS) { |
738 | } | 890 | /* If overwriting, set the correct length if original was |
739 | } else { | 891 | longer */ |
740 | result = rename(src, target) == 0; | 892 | rc = ftruncate(target_fd, total_size); |
741 | #ifdef HAVE_MULTIVOLUME | 893 | } |
742 | if (!result) { | 894 | |
743 | if (errno == EXDEV) { | 895 | close(target_fd); |
744 | /* Failed because cross volume rename doesn't work. Copy | 896 | |
745 | instead */ | 897 | if (rc != OPRC_SUCCESS) { |
746 | result = clipboard_pastefile(src, target, true); | 898 | /* Copy failed. Cleanup. */ |
747 | 899 | remove(target); | |
748 | if (result) { | ||
749 | result = remove(src) == 0; | ||
750 | } | ||
751 | } | 900 | } |
752 | } | 901 | } |
753 | #endif | 902 | |
903 | close(src_fd); | ||
754 | } | 904 | } |
755 | 905 | ||
756 | return result; | 906 | if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) { |
907 | /* Remove the source file */ | ||
908 | rc = remove(src); | ||
909 | } | ||
910 | |||
911 | return rc; | ||
757 | } | 912 | } |
758 | 913 | ||
759 | /* Paste a directory to a new location. Designed to be called by | 914 | /* Paste a directory */ |
760 | clipboard_paste */ | 915 | static int clipboard_pastedirectory(struct dirrecurse_params *src, |
761 | static bool clipboard_pastedirectory(char *src, int srclen, char *target, | 916 | struct dirrecurse_params *target, |
762 | int targetlen, bool copy) | 917 | unsigned int flags) |
763 | { | 918 | { |
764 | DIR *srcdir; | 919 | int rc = -1; |
765 | int srcdirlen = strlen(src); | 920 | |
766 | int targetdirlen = strlen(target); | 921 | while (!(flags & (PASTE_COPY | PASTE_EXDEV))) { |
767 | bool result = true; | 922 | if ((flags & PASTE_OVERWRITE) || !file_exists(target->path)) { |
768 | 923 | /* Just try to move the directory */ | |
769 | if (!file_exists(target)) { | 924 | if (poll_cancel_action(src->path)) { |
770 | if (!copy) { | 925 | rc = OPRC_CANCELLED; |
771 | /* Just move the directory */ | 926 | } else { |
772 | result = rename(src, target) == 0; | 927 | rc = rename(src->path, target->path); |
928 | } | ||
773 | 929 | ||
774 | #ifdef HAVE_MULTIVOLUME | 930 | if (rc < 0) { |
775 | if (!result && errno == EXDEV) { | 931 | int errnum = errno; |
776 | /* Try a copy as we're going across devices */ | 932 | if (errnum == ENOTEMPTY && (flags & PASTE_OVERWRITE)) { |
777 | result = clipboard_pastedirectory(src, srclen, target, | 933 | /* Directory is not empty thus rename() will not do a quick |
778 | targetlen, true); | 934 | overwrite */ |
779 | 935 | break; | |
780 | /* If it worked, remove the source directory */ | ||
781 | if (result) { | ||
782 | remove_dir(src, srclen); | ||
783 | } | 936 | } |
937 | #ifdef HAVE_MULTIVOLUME | ||
938 | else if (errnum == EXDEV) { | ||
939 | /* Failed because cross volume rename doesn't work; force | ||
940 | a move instead */ | ||
941 | flags |= PASTE_EXDEV; | ||
942 | break; | ||
943 | } | ||
944 | #endif /* HAVE_MULTIVOLUME */ | ||
784 | } | 945 | } |
785 | #endif | ||
786 | return result; | ||
787 | } else { | ||
788 | /* Make a directory to copy things to */ | ||
789 | result = mkdir(target) == 0; | ||
790 | } | 946 | } |
791 | } | ||
792 | 947 | ||
793 | /* Check if something went wrong already */ | 948 | return rc; |
794 | if (!result) { | ||
795 | return result; | ||
796 | } | 949 | } |
797 | 950 | ||
798 | srcdir = opendir(src); | 951 | DIR *srcdir = opendir(src->path); |
799 | if (!srcdir) { | 952 | |
800 | return false; | 953 | if (srcdir) { |
954 | /* Make a directory to copy things to */ | ||
955 | rc = mkdir(target->path); | ||
956 | if (rc < 0 && errno == EEXIST && (flags & PASTE_OVERWRITE)) { | ||
957 | /* Exists and overwrite was approved */ | ||
958 | rc = OPRC_SUCCESS; | ||
959 | } | ||
801 | } | 960 | } |
802 | 961 | ||
803 | /* This loop will exit as soon as there's a problem */ | 962 | size_t srcap = src->append, targetap = target->append; |
804 | while(result) | 963 | |
805 | { | 964 | /* Walk through the directory content; this loop will exit as soon as |
806 | struct dirent* entry; | 965 | there's a problem */ |
807 | /* walk through the directory content */ | 966 | while (rc == OPRC_SUCCESS) { |
808 | entry = readdir(srcdir); | 967 | errno = 0; /* Distinguish failure from eod */ |
809 | if (!entry) | 968 | struct dirent *entry = readdir(srcdir); |
969 | if (!entry) { | ||
970 | if (errno) { | ||
971 | rc = -1; | ||
972 | } | ||
810 | break; | 973 | break; |
974 | } | ||
811 | 975 | ||
812 | struct dirinfo info = dir_get_info(srcdir, entry); | 976 | struct dirinfo info = dir_get_info(srcdir, entry); |
813 | /* append name to current directory */ | 977 | if ((info.attribute & ATTR_DIRECTORY) && |
814 | snprintf(src+srcdirlen, srclen-srcdirlen, "/%s", entry->d_name); | 978 | is_dotdir_name(entry->d_name)) { |
815 | snprintf(target+targetdirlen, targetlen-targetdirlen, "/%s", | 979 | continue; /* Skip these */ |
816 | entry->d_name); | 980 | } |
817 | 981 | ||
818 | DEBUGF("Copy %s to %s\n", src, target); | 982 | /* Append names to current directories */ |
983 | src->append = srcap + | ||
984 | path_append(&src->path[srcap], PA_SEP_HARD, entry->d_name, | ||
985 | sizeof(src->path) - srcap); | ||
819 | 986 | ||
820 | if (info.attribute & ATTR_DIRECTORY) | 987 | target->append = targetap + |
821 | { /* copy/move a subdirectory */ | 988 | path_append(&target->path[targetap], PA_SEP_HARD, entry->d_name, |
822 | if (!strcmp((char *)entry->d_name, ".") || | 989 | sizeof (target->path) - targetap); |
823 | !strcmp((char *)entry->d_name, "..")) | ||
824 | continue; /* skip these */ | ||
825 | 990 | ||
826 | result = clipboard_pastedirectory(src, srclen, target, targetlen, | 991 | if (src->append >= sizeof (src->path) || |
827 | copy); /* recursion */ | 992 | target->append >= sizeof (target->path)) { |
993 | rc = -1; /* No space left in buffer */ | ||
994 | break; | ||
828 | } | 995 | } |
829 | else | 996 | |
830 | { /* copy/move a file */ | 997 | if (poll_cancel_action(src->path)) { |
831 | draw_slider(); | 998 | rc = OPRC_CANCELLED; |
832 | result = clipboard_pastefile(src, target, copy); | 999 | break; |
1000 | } | ||
1001 | |||
1002 | DEBUGF("Copy %s to %s\n", src->path, target->path); | ||
1003 | |||
1004 | if (info.attribute & ATTR_DIRECTORY) { | ||
1005 | /* Copy/move a subdirectory */ | ||
1006 | rc = clipboard_pastedirectory(src, target, flags); /* recursion */ | ||
1007 | } else { | ||
1008 | /* Copy/move a file */ | ||
1009 | rc = clipboard_pastefile(src->path, target->path, flags); | ||
833 | } | 1010 | } |
1011 | |||
1012 | /* Remove basenames we added above */ | ||
1013 | src->path[srcap] = target->path[targetap] = '\0'; | ||
1014 | } | ||
1015 | |||
1016 | if (rc == OPRC_SUCCESS && !(flags & PASTE_COPY)) { | ||
1017 | /* Remove the now empty directory */ | ||
1018 | rc = rmdir(src->path); | ||
834 | } | 1019 | } |
835 | 1020 | ||
836 | closedir(srcdir); | 1021 | closedir(srcdir); |
1022 | return rc; | ||
1023 | } | ||
837 | 1024 | ||
838 | if (result) { | 1025 | static bool clipboard_cut(void) |
839 | src[srcdirlen] = '\0'; /* terminate to original length */ | 1026 | { |
840 | target[targetdirlen] = '\0'; /* terminate to original length */ | 1027 | return clipboard_clip(&clipboard, selected_file, selected_file_attr, |
841 | } | 1028 | PASTE_CUT); |
1029 | } | ||
842 | 1030 | ||
843 | return result; | 1031 | static bool clipboard_copy(void) |
1032 | { | ||
1033 | return clipboard_clip(&clipboard, selected_file, selected_file_attr, | ||
1034 | PASTE_COPY); | ||
844 | } | 1035 | } |
845 | 1036 | ||
846 | /* Paste the clipboard to the current directory */ | 1037 | /* Paste the clipboard to the current directory */ |
847 | static bool clipboard_paste(void) | 1038 | static int clipboard_paste(void) |
848 | { | 1039 | { |
849 | char target[MAX_PATH]; | 1040 | if (!clipboard.path[0]) |
850 | char *cwd, *nameptr; | 1041 | return 1; |
851 | bool success; | ||
852 | 1042 | ||
853 | static const char *lines[]={ID2P(LANG_REALLY_OVERWRITE)}; | 1043 | int rc = -1; |
854 | static const struct text_message message={lines, 1}; | ||
855 | 1044 | ||
856 | /* Get the name of the current directory */ | 1045 | struct dirrecurse_params src, target; |
857 | cwd = getcwd(NULL, 0); | 1046 | unsigned int flags = clipboard.flags; |
858 | 1047 | ||
859 | /* Figure out the name of the selection */ | 1048 | /* Figure out the name of the selection */ |
860 | nameptr = strrchr(clipboard_selection, '/'); | 1049 | const char *nameptr; |
1050 | path_basename(clipboard.path, &nameptr); | ||
861 | 1051 | ||
862 | /* Final target is current directory plus name of selection */ | 1052 | /* Final target is current directory plus name of selection */ |
863 | snprintf(target, sizeof(target), "%s%s", cwd[1] ? cwd : "", nameptr); | 1053 | target.append = path_append(target.path, getcwd(NULL, 0), |
864 | 1054 | nameptr, sizeof (target.path)); | |
865 | /* If the target existed but they choose not to overwite, exit */ | ||
866 | if (file_exists(target) && | ||
867 | (gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO)) { | ||
868 | return false; | ||
869 | } | ||
870 | 1055 | ||
871 | if (clipboard_is_copy) { | 1056 | switch (target.append < sizeof (target.path) ? |
872 | splash(0, ID2P(LANG_COPYING)); | 1057 | relate(clipboard.path, target.path) : -1) |
873 | } | ||
874 | else | ||
875 | { | 1058 | { |
876 | splash(0, ID2P(LANG_MOVING)); | 1059 | case RELATE_SAME: |
877 | } | 1060 | rc = OPRC_NOOP; |
1061 | break; | ||
1062 | |||
1063 | case RELATE_DIFFERENT: | ||
1064 | if (file_exists(target.path)) { | ||
1065 | /* If user chooses not to overwrite, cancel */ | ||
1066 | if (confirm_overwrite() == YESNO_NO) { | ||
1067 | rc = OPRC_NOOVERWRT; | ||
1068 | break; | ||
1069 | } | ||
878 | 1070 | ||
879 | /* Now figure out what we're doing */ | 1071 | flags |= PASTE_OVERWRITE; |
880 | cpu_boost(true); | ||
881 | if (clipboard_selection_attr & ATTR_DIRECTORY) { | ||
882 | /* Recursion. Set up external stack */ | ||
883 | char srcpath[MAX_PATH]; | ||
884 | char targetpath[MAX_PATH]; | ||
885 | if (!strncmp(clipboard_selection, target, strlen(clipboard_selection))) | ||
886 | { | ||
887 | /* Do not allow the user to paste a directory into a dir they are | ||
888 | copying */ | ||
889 | success = 0; | ||
890 | } | 1072 | } |
891 | else | ||
892 | { | ||
893 | strlcpy(srcpath, clipboard_selection, sizeof(srcpath)); | ||
894 | strlcpy(targetpath, target, sizeof(targetpath)); | ||
895 | 1073 | ||
896 | success = clipboard_pastedirectory(srcpath, sizeof(srcpath), | 1074 | clear_display(true); |
897 | target, sizeof(targetpath), clipboard_is_copy); | 1075 | splash(HZ/2, (flags & PASTE_COPY) ? ID2P(LANG_COPYING) : |
1076 | ID2P(LANG_MOVING)); | ||
898 | 1077 | ||
899 | if (success && !clipboard_is_copy) | 1078 | /* Now figure out what we're doing */ |
900 | { | 1079 | cpu_boost(true); |
901 | strlcpy(srcpath, clipboard_selection, sizeof(srcpath)); | 1080 | |
902 | remove_dir(srcpath, sizeof(srcpath)); | 1081 | if (clipboard.attr & ATTR_DIRECTORY) { |
1082 | /* Copy or move a subdirectory */ | ||
1083 | src.append = strlcpy(src.path, clipboard.path, | ||
1084 | sizeof (src.path)); | ||
1085 | if (src.append < sizeof (src.path)) { | ||
1086 | rc = clipboard_pastedirectory(&src, &target, flags); | ||
903 | } | 1087 | } |
1088 | } else { | ||
1089 | /* Copy or move a file */ | ||
1090 | rc = clipboard_pastefile(clipboard.path, target.path, flags); | ||
904 | } | 1091 | } |
905 | } else { | 1092 | |
906 | success = clipboard_pastefile(clipboard_selection, target, | 1093 | cpu_boost(false); |
907 | clipboard_is_copy); | 1094 | break; |
1095 | |||
1096 | case RELATE_PREFIX: | ||
1097 | default: /* Some other relation / failure */ | ||
1098 | break; | ||
908 | } | 1099 | } |
909 | cpu_boost(false); | ||
910 | 1100 | ||
911 | /* Did it work? */ | 1101 | clear_display(true); |
912 | if (success) { | ||
913 | /* Reset everything */ | ||
914 | clipboard_selection[0] = 0; | ||
915 | clipboard_selection_attr = 0; | ||
916 | clipboard_is_copy = false; | ||
917 | 1102 | ||
918 | /* Force reload of the current directory */ | 1103 | switch (rc) |
1104 | { | ||
1105 | case OPRC_CANCELLED: | ||
1106 | splash_cancelled(); | ||
1107 | case OPRC_SUCCESS: | ||
919 | onplay_result = ONPLAY_RELOAD_DIR; | 1108 | onplay_result = ONPLAY_RELOAD_DIR; |
920 | } else { | 1109 | case OPRC_NOOP: |
921 | cond_talk_ids_fq(LANG_PASTE, LANG_FAILED); | 1110 | clipboard_clear_selection(&clipboard); |
922 | splashf(HZ, (unsigned char *)"%s %s", str(LANG_PASTE), | 1111 | case OPRC_NOOVERWRT: |
923 | str(LANG_FAILED)); | 1112 | break; |
1113 | default: | ||
1114 | if (rc < OPRC_SUCCESS) { | ||
1115 | splash_failed(LANG_PASTE); | ||
1116 | onplay_result = ONPLAY_RELOAD_DIR; | ||
1117 | } | ||
924 | } | 1118 | } |
925 | 1119 | ||
926 | return true; | 1120 | return 1; |
927 | } | 1121 | } |
928 | 1122 | ||
929 | #ifdef HAVE_TAGCACHE | 1123 | #ifdef HAVE_TAGCACHE |
@@ -1094,15 +1288,12 @@ static int clipboard_callback(int action,const struct menu_item_ex *this_item) | |||
1094 | { | 1288 | { |
1095 | case ACTION_REQUEST_MENUITEM: | 1289 | case ACTION_REQUEST_MENUITEM: |
1096 | #ifdef HAVE_MULTIVOLUME | 1290 | #ifdef HAVE_MULTIVOLUME |
1097 | if ((selected_file_attr & FAT_ATTR_VOLUME) && | ||
1098 | (this_item == &rename_file_item || | ||
1099 | this_item == &delete_dir_item || | ||
1100 | this_item == &clipboard_cut_item) ) | ||
1101 | return ACTION_EXIT_MENUITEM; | ||
1102 | /* no rename+delete for volumes */ | 1291 | /* no rename+delete for volumes */ |
1103 | if ((selected_file_attr & ATTR_VOLUME) && | 1292 | if ((selected_file_attr & ATTR_VOLUME) && |
1104 | (this_item == &delete_file_item || | 1293 | (this_item == &rename_file_item || |
1105 | this_item == &list_viewers_item)) | 1294 | this_item == &delete_dir_item || |
1295 | this_item == &clipboard_cut_item || | ||
1296 | this_item == &list_viewers_item)) | ||
1106 | return ACTION_EXIT_MENUITEM; | 1297 | return ACTION_EXIT_MENUITEM; |
1107 | #endif | 1298 | #endif |
1108 | #ifdef HAVE_TAGCACHE | 1299 | #ifdef HAVE_TAGCACHE |
@@ -1117,7 +1308,7 @@ static int clipboard_callback(int action,const struct menu_item_ex *this_item) | |||
1117 | #endif | 1308 | #endif |
1118 | if (this_item == &clipboard_paste_item) | 1309 | if (this_item == &clipboard_paste_item) |
1119 | { /* visible if there is something to paste */ | 1310 | { /* visible if there is something to paste */ |
1120 | return (clipboard_selection[0] != 0) ? | 1311 | return (clipboard.path[0] != 0) ? |
1121 | action : ACTION_EXIT_MENUITEM; | 1312 | action : ACTION_EXIT_MENUITEM; |
1122 | } | 1313 | } |
1123 | else if (this_item == &create_dir_item) | 1314 | else if (this_item == &create_dir_item) |
@@ -1232,8 +1423,7 @@ static bool delete_item(void) | |||
1232 | { | 1423 | { |
1233 | #ifdef HAVE_MULTIVOLUME | 1424 | #ifdef HAVE_MULTIVOLUME |
1234 | /* no delete for volumes */ | 1425 | /* no delete for volumes */ |
1235 | if ((selected_file_attr & FAT_ATTR_VOLUME) || | 1426 | if (selected_file_attr & ATTR_VOLUME) |
1236 | (selected_file_attr & ATTR_VOLUME)) | ||
1237 | return false; | 1427 | return false; |
1238 | #endif | 1428 | #endif |
1239 | return delete_file_dir(); | 1429 | return delete_file_dir(); |
diff --git a/apps/playlist.c b/apps/playlist.c index 43aa97790b..db93344ef1 100755 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -86,7 +86,7 @@ | |||
86 | #include "screens.h" | 86 | #include "screens.h" |
87 | #include "core_alloc.h" | 87 | #include "core_alloc.h" |
88 | #include "misc.h" | 88 | #include "misc.h" |
89 | #include "filefuncs.h" | 89 | #include "pathfuncs.h" |
90 | #include "button.h" | 90 | #include "button.h" |
91 | #include "filetree.h" | 91 | #include "filetree.h" |
92 | #include "abrepeat.h" | 92 | #include "abrepeat.h" |
@@ -107,6 +107,8 @@ | |||
107 | #include "panic.h" | 107 | #include "panic.h" |
108 | #include "logdiskf.h" | 108 | #include "logdiskf.h" |
109 | 109 | ||
110 | #undef HAVE_DIRCACHE | ||
111 | |||
110 | #define PLAYLIST_CONTROL_FILE_VERSION 2 | 112 | #define PLAYLIST_CONTROL_FILE_VERSION 2 |
111 | 113 | ||
112 | /* | 114 | /* |
@@ -180,8 +182,8 @@ static int get_next_directory(char *dir); | |||
180 | static int get_next_dir(char *dir, bool is_forward); | 182 | static int get_next_dir(char *dir, bool is_forward); |
181 | static int get_previous_directory(char *dir); | 183 | static int get_previous_directory(char *dir); |
182 | static int check_subdir_for_music(char *dir, const char *subdir, bool recurse); | 184 | static int check_subdir_for_music(char *dir, const char *subdir, bool recurse); |
183 | static int format_track_path(char *dest, char *src, int buf_length, int max, | 185 | static ssize_t format_track_path(char *dest, char *src, int buf_length, |
184 | const char *dir); | 186 | const char *dir); |
185 | static void display_playlist_count(int count, const unsigned char *fmt, | 187 | static void display_playlist_count(int count, const unsigned char *fmt, |
186 | bool final); | 188 | bool final); |
187 | static void display_buffer_full(void); | 189 | static void display_buffer_full(void); |
@@ -526,7 +528,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist, | |||
526 | int result = 0; | 528 | int result = 0; |
527 | /* get emergency buffer so we don't fail horribly */ | 529 | /* get emergency buffer so we don't fail horribly */ |
528 | if (!buflen) | 530 | if (!buflen) |
529 | buffer = __builtin_alloca((buflen = 64)); | 531 | buffer = alloca((buflen = 64)); |
530 | 532 | ||
531 | if(-1 == playlist->fd) | 533 | if(-1 == playlist->fd) |
532 | playlist->fd = open_utf8(playlist->filename, O_RDONLY); | 534 | playlist->fd = open_utf8(playlist->filename, O_RDONLY); |
@@ -1429,7 +1431,7 @@ static int get_filename(struct playlist_info* playlist, int index, int seek, | |||
1429 | 1431 | ||
1430 | strlcpy(dir_buf, playlist->filename, playlist->dirlen); | 1432 | strlcpy(dir_buf, playlist->filename, playlist->dirlen); |
1431 | 1433 | ||
1432 | return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf)); | 1434 | return format_track_path(buf, tmp_buf, buf_length, dir_buf); |
1433 | } | 1435 | } |
1434 | 1436 | ||
1435 | static int get_next_directory(char *dir){ | 1437 | static int get_next_directory(char *dir){ |
@@ -1629,7 +1631,7 @@ static int get_next_dir(char *dir, bool is_forward) | |||
1629 | static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) | 1631 | static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) |
1630 | { | 1632 | { |
1631 | int result = -1; | 1633 | int result = -1; |
1632 | int dirlen = strlen(dir); | 1634 | size_t dirlen = strlen(dir); |
1633 | int num_files = 0; | 1635 | int num_files = 0; |
1634 | int i; | 1636 | int i; |
1635 | struct entry *files; | 1637 | struct entry *files; |
@@ -1637,12 +1639,11 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) | |||
1637 | bool has_subdir = false; | 1639 | bool has_subdir = false; |
1638 | struct tree_context* tc = tree_get_context(); | 1640 | struct tree_context* tc = tree_get_context(); |
1639 | 1641 | ||
1640 | snprintf( | 1642 | if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >= |
1641 | dir + dirlen, MAX_PATH - dirlen, | 1643 | MAX_PATH - dirlen) |
1642 | /* only add a trailing slash if we need one */ | 1644 | { |
1643 | dirlen && dir[dirlen - 1] == '/' ? "%s" : "/%s", | 1645 | return 0; |
1644 | subdir | 1646 | } |
1645 | ); | ||
1646 | 1647 | ||
1647 | if (ft_load(tc, dir) < 0) | 1648 | if (ft_load(tc, dir) < 0) |
1648 | { | 1649 | { |
@@ -1695,7 +1696,7 @@ static int check_subdir_for_music(char *dir, const char *subdir, bool recurse) | |||
1695 | } | 1696 | } |
1696 | else | 1697 | else |
1697 | { | 1698 | { |
1698 | strcpy(dir, "/"); | 1699 | strcpy(dir, PATH_ROOTSTR); |
1699 | } | 1700 | } |
1700 | 1701 | ||
1701 | /* we now need to reload our current directory */ | 1702 | /* 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) | |||
1708 | /* | 1709 | /* |
1709 | * Returns absolute path of track | 1710 | * Returns absolute path of track |
1710 | */ | 1711 | */ |
1711 | static int format_track_path(char *dest, char *src, int buf_length, int max, | 1712 | static ssize_t format_track_path(char *dest, char *src, int buf_length, |
1712 | const char *dir) | 1713 | const char *dir) |
1713 | { | 1714 | { |
1714 | int i = 0; | 1715 | size_t len; |
1715 | int j; | ||
1716 | char *temp_ptr; | ||
1717 | |||
1718 | /* Look for the end of the string */ | ||
1719 | while((i < max) && | ||
1720 | (src[i] != '\n') && | ||
1721 | (src[i] != '\r') && | ||
1722 | (src[i] != '\0')) | ||
1723 | i++; | ||
1724 | |||
1725 | /* Now work back killing white space */ | ||
1726 | while((i > 0) && | ||
1727 | ((src[i-1] == ' ') || | ||
1728 | (src[i-1] == '\t'))) | ||
1729 | i--; | ||
1730 | 1716 | ||
1731 | /* Zero-terminate the file name */ | 1717 | /* strip whitespace at beginning and end */ |
1732 | src[i]=0; | 1718 | len = path_trim_whitespace(src, (const char **)&src); |
1719 | src[len] = '\0'; | ||
1733 | 1720 | ||
1734 | /* replace backslashes with forward slashes */ | 1721 | /* replace backslashes with forward slashes */ |
1735 | for ( j=0; j<i; j++ ) | 1722 | path_correct_separators(src, src); |
1736 | if ( src[j] == '\\' ) | ||
1737 | src[j] = '/'; | ||
1738 | 1723 | ||
1739 | if('/' == src[0]) | 1724 | /* handle DOS style drive letter and parse non-greedily so that: |
1740 | { | 1725 | * 1) "c:/foo" becomes "/foo" and the result is absolute |
1741 | strlcpy(dest, src, buf_length); | 1726 | * 2) "c:foo becomes "foo" and the result is relative |
1742 | } | 1727 | * This is how Windows seems to handle it except drive letters are of no |
1743 | else | 1728 | * meaning here. */ |
1744 | { | 1729 | path_strip_drive(src, (const char **)&src, false); |
1745 | /* handle dos style drive letter */ | ||
1746 | if (':' == src[1]) | ||
1747 | strlcpy(dest, &src[2], buf_length); | ||
1748 | else if (!strncmp(src, "../", 3)) | ||
1749 | { | ||
1750 | /* handle relative paths */ | ||
1751 | i=3; | ||
1752 | while(!strncmp(&src[i], "../", 3)) | ||
1753 | i += 3; | ||
1754 | for (j=0; j<i/3; j++) { | ||
1755 | temp_ptr = strrchr(dir, '/'); | ||
1756 | if (temp_ptr) | ||
1757 | *temp_ptr = '\0'; | ||
1758 | else | ||
1759 | break; | ||
1760 | } | ||
1761 | snprintf(dest, buf_length, "%s/%s", dir, &src[i]); | ||
1762 | } | ||
1763 | else if ( '.' == src[0] && '/' == src[1] ) { | ||
1764 | snprintf(dest, buf_length, "%s/%s", dir, &src[2]); | ||
1765 | } | ||
1766 | else { | ||
1767 | snprintf(dest, buf_length, "%s/%s", dir, src); | ||
1768 | } | ||
1769 | } | ||
1770 | #ifdef HAVE_MULTIVOLUME | ||
1771 | 1730 | ||
1772 | char vol_string[VOL_ENUM_POS + 8]; | 1731 | /* prepends directory only if src is relative */ |
1773 | snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, 1); | 1732 | len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length); |
1733 | if (len >= (size_t)buf_length) | ||
1734 | return -1; /* buffer too small */ | ||
1774 | 1735 | ||
1775 | /*check if the playlist is on a external card, and correct path if needed */ | 1736 | return len; |
1776 | if(strstr(dir, vol_string) && (strstr(dest, vol_string) == NULL)){ | ||
1777 | char temp[buf_length]; | ||
1778 | strlcpy(temp, dest, buf_length); | ||
1779 | snprintf(dest, buf_length, "%s%s", vol_string, temp); | ||
1780 | } | ||
1781 | #endif | ||
1782 | |||
1783 | return 0; | ||
1784 | } | 1737 | } |
1785 | 1738 | ||
1786 | /* | 1739 | /* |
@@ -3113,8 +3066,7 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam | |||
3113 | { | 3066 | { |
3114 | int fd; | 3067 | int fd; |
3115 | int max; | 3068 | int max; |
3116 | char *temp_ptr; | 3069 | char *dir; |
3117 | const char *dir; | ||
3118 | unsigned char *count_str; | 3070 | unsigned char *count_str; |
3119 | char temp_buf[MAX_PATH+1]; | 3071 | char temp_buf[MAX_PATH+1]; |
3120 | char trackname[MAX_PATH+1]; | 3072 | char trackname[MAX_PATH+1]; |
@@ -3139,13 +3091,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam | |||
3139 | } | 3091 | } |
3140 | 3092 | ||
3141 | /* we need the directory name for formatting purposes */ | 3093 | /* we need the directory name for formatting purposes */ |
3142 | dir = filename; | 3094 | size_t dirlen = path_dirname(filename, (const char **)&dir); |
3143 | 3095 | dir = strmemdupa(dir, dirlen); | |
3144 | temp_ptr = strrchr(filename+1,'/'); | ||
3145 | if (temp_ptr) | ||
3146 | *temp_ptr = 0; | ||
3147 | else | ||
3148 | dir = "/"; | ||
3149 | 3096 | ||
3150 | if (queue) | 3097 | if (queue) |
3151 | count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); | 3098 | count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT); |
@@ -3183,8 +3130,8 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam | |||
3183 | 3130 | ||
3184 | /* we need to format so that relative paths are correctly | 3131 | /* we need to format so that relative paths are correctly |
3185 | handled */ | 3132 | handled */ |
3186 | if (format_track_path(trackname, temp_buf, sizeof(trackname), max, | 3133 | if (format_track_path(trackname, temp_buf, sizeof(trackname), |
3187 | dir) < 0) | 3134 | dir) < 0) |
3188 | { | 3135 | { |
3189 | result = -1; | 3136 | result = -1; |
3190 | break; | 3137 | break; |
@@ -3223,9 +3170,6 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam | |||
3223 | 3170 | ||
3224 | close(fd); | 3171 | close(fd); |
3225 | 3172 | ||
3226 | if (temp_ptr) | ||
3227 | *temp_ptr = '/'; | ||
3228 | |||
3229 | sync_control(playlist, false); | 3173 | sync_control(playlist, false); |
3230 | 3174 | ||
3231 | cpu_boost(false); | 3175 | cpu_boost(false); |
@@ -3537,9 +3481,9 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3537 | char path[MAX_PATH+1]; | 3481 | char path[MAX_PATH+1]; |
3538 | char tmp_buf[MAX_PATH+1]; | 3482 | char tmp_buf[MAX_PATH+1]; |
3539 | int result = 0; | 3483 | int result = 0; |
3540 | bool overwrite_current = false; | ||
3541 | int *seek_buf; | 3484 | int *seek_buf; |
3542 | bool reparse; | 3485 | bool reparse; |
3486 | ssize_t pathlen; | ||
3543 | 3487 | ||
3544 | ALIGN_BUFFER(temp_buffer, temp_buffer_size, sizeof(int)); | 3488 | ALIGN_BUFFER(temp_buffer, temp_buffer_size, sizeof(int)); |
3545 | seek_buf = temp_buffer; | 3489 | seek_buf = temp_buffer; |
@@ -3557,22 +3501,18 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3557 | return -1; | 3501 | return -1; |
3558 | 3502 | ||
3559 | /* use current working directory as base for pathname */ | 3503 | /* use current working directory as base for pathname */ |
3560 | if (format_track_path(path, filename, sizeof(tmp_buf), | 3504 | pathlen = format_track_path(path, filename, sizeof(path), PATH_ROOTSTR); |
3561 | strlen(filename)+1, "/") < 0) | 3505 | if (pathlen < 0) |
3506 | return -1; | ||
3507 | |||
3508 | /* Use temporary pathname and overwrite/rename later */ | ||
3509 | if (strlcat(path, "_temp", sizeof(path)) >= sizeof (path)) | ||
3562 | return -1; | 3510 | return -1; |
3563 | 3511 | ||
3564 | /* can ignore volatile here, because core_get_data() is called later */ | 3512 | /* can ignore volatile here, because core_get_data() is called later */ |
3565 | char* old_buffer = (char*)playlist->buffer; | 3513 | char* old_buffer = (char*)playlist->buffer; |
3566 | size_t old_buffer_size = playlist->buffer_size; | 3514 | size_t old_buffer_size = playlist->buffer_size; |
3567 | 3515 | ||
3568 | if (!strncmp(playlist->filename, path, strlen(path))) | ||
3569 | { | ||
3570 | /* Attempting to overwrite current playlist file. | ||
3571 | * use temporary pathname and overwrite later */ | ||
3572 | strlcat(path, "_temp", sizeof(path)); | ||
3573 | overwrite_current = true; | ||
3574 | } | ||
3575 | |||
3576 | if (is_m3u8(path)) | 3516 | if (is_m3u8(path)) |
3577 | { | 3517 | { |
3578 | fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC); | 3518 | fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC); |
@@ -3621,8 +3561,8 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3621 | break; | 3561 | break; |
3622 | } | 3562 | } |
3623 | 3563 | ||
3624 | if (overwrite_current && !reparse) | 3564 | if (!reparse) |
3625 | seek_buf[count] = lseek(fd, 0, SEEK_CUR); | 3565 | seek_buf[count] = filesize(fd); |
3626 | 3566 | ||
3627 | if (fdprintf(fd, "%s\n", tmp_buf) < 0) | 3567 | if (fdprintf(fd, "%s\n", tmp_buf) < 0) |
3628 | { | 3568 | { |
@@ -3647,57 +3587,61 @@ int playlist_save(struct playlist_info* playlist, char *filename, | |||
3647 | index = (index+1)%playlist->amount; | 3587 | index = (index+1)%playlist->amount; |
3648 | } | 3588 | } |
3649 | 3589 | ||
3650 | display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true); | ||
3651 | |||
3652 | close(fd); | 3590 | close(fd); |
3591 | fd = -1; | ||
3653 | 3592 | ||
3654 | if (overwrite_current && result >= 0) | 3593 | display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true); |
3594 | |||
3595 | if (result >= 0) | ||
3655 | { | 3596 | { |
3656 | result = -1; | 3597 | strmemcpy(tmp_buf, path, pathlen); /* remove "_temp" */ |
3657 | 3598 | ||
3658 | mutex_lock(playlist->control_mutex); | 3599 | mutex_lock(playlist->control_mutex); |
3659 | 3600 | ||
3660 | /* Replace the current playlist with the new one and update indices */ | 3601 | if (!rename(path, tmp_buf)) |
3661 | close(playlist->fd); | ||
3662 | playlist->fd = -1; | ||
3663 | if (remove(playlist->filename) >= 0) | ||
3664 | { | 3602 | { |
3665 | if (rename(path, playlist->filename) >= 0) | 3603 | fd = open_utf8(tmp_buf, O_RDONLY); |
3604 | if (fsamefile(fd, playlist->fd) > 0) | ||
3666 | { | 3605 | { |
3667 | playlist->fd = open_utf8(playlist->filename, O_RDONLY); | 3606 | /* Replace the current playlist with the new one and update |
3668 | if (playlist->fd >= 0) | 3607 | indices */ |
3608 | close(playlist->fd); | ||
3609 | playlist->fd = fd; | ||
3610 | fd = -1; | ||
3611 | |||
3612 | if (!reparse) | ||
3669 | { | 3613 | { |
3670 | if (!reparse) | 3614 | index = playlist->first_index; |
3615 | for (i=0, count=0; i<playlist->amount; i++) | ||
3671 | { | 3616 | { |
3672 | index = playlist->first_index; | 3617 | if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK)) |
3673 | for (i=0, count=0; i<playlist->amount; i++) | ||
3674 | { | 3618 | { |
3675 | if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK)) | 3619 | playlist->indices[index] = seek_buf[count]; |
3676 | { | 3620 | count++; |
3677 | playlist->indices[index] = seek_buf[count]; | ||
3678 | count++; | ||
3679 | } | ||
3680 | index = (index+1)%playlist->amount; | ||
3681 | } | 3621 | } |
3622 | index = (index+1)%playlist->amount; | ||
3682 | } | 3623 | } |
3683 | else | ||
3684 | { | ||
3685 | NOTEF("reparsing current playlist (slow)"); | ||
3686 | playlist->amount = 0; | ||
3687 | add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size); | ||
3688 | } | ||
3689 | /* we need to recreate control because inserted tracks are | ||
3690 | now part of the playlist and shuffle has been | ||
3691 | invalidated */ | ||
3692 | result = recreate_control(playlist); | ||
3693 | } | 3624 | } |
3694 | } | 3625 | else |
3695 | } | 3626 | { |
3627 | NOTEF("reparsing current playlist (slow)"); | ||
3628 | playlist->amount = 0; | ||
3629 | add_indices_to_playlist(playlist, temp_buffer, | ||
3630 | temp_buffer_size); | ||
3631 | } | ||
3696 | 3632 | ||
3697 | mutex_unlock(playlist->control_mutex); | 3633 | /* we need to recreate control because inserted tracks are |
3634 | now part of the playlist and shuffle has been invalidated */ | ||
3635 | result = recreate_control(playlist); | ||
3636 | } | ||
3637 | } | ||
3698 | 3638 | ||
3639 | mutex_unlock(playlist->control_mutex); | ||
3699 | } | 3640 | } |
3700 | 3641 | ||
3642 | if (fd >= 0) | ||
3643 | close(fd); | ||
3644 | |||
3701 | cpu_boost(false); | 3645 | cpu_boost(false); |
3702 | 3646 | ||
3703 | reset_old_buffer: | 3647 | reset_old_buffer: |
@@ -3759,8 +3703,12 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
3759 | if (recurse) | 3703 | if (recurse) |
3760 | { | 3704 | { |
3761 | /* recursively add directories */ | 3705 | /* recursively add directories */ |
3762 | snprintf(buf, sizeof(buf), "%s/%s", | 3706 | if (path_append(buf, dirname, files[i].name, sizeof(buf)) |
3763 | dirname[1]? dirname: "", files[i].name); | 3707 | >= sizeof(buf)) |
3708 | { | ||
3709 | continue; | ||
3710 | } | ||
3711 | |||
3764 | result = playlist_directory_tracksearch(buf, recurse, | 3712 | result = playlist_directory_tracksearch(buf, recurse, |
3765 | callback, context); | 3713 | callback, context); |
3766 | if (result < 0) | 3714 | if (result < 0) |
@@ -3785,8 +3733,11 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
3785 | } | 3733 | } |
3786 | else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) | 3734 | else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO) |
3787 | { | 3735 | { |
3788 | snprintf(buf, sizeof(buf), "%s/%s", | 3736 | if (path_append(buf, dirname, files[i].name, sizeof(buf)) |
3789 | dirname[1]? dirname: "", files[i].name); | 3737 | >= sizeof(buf)) |
3738 | { | ||
3739 | continue; | ||
3740 | } | ||
3790 | 3741 | ||
3791 | if (callback(buf, context) != 0) | 3742 | if (callback(buf, context) != 0) |
3792 | { | 3743 | { |
diff --git a/apps/playlist_catalog.c b/apps/playlist_catalog.c index 3687681b66..5741d11258 100644 --- a/apps/playlist_catalog.c +++ b/apps/playlist_catalog.c | |||
@@ -32,7 +32,7 @@ | |||
32 | #include "lang.h" | 32 | #include "lang.h" |
33 | #include "list.h" | 33 | #include "list.h" |
34 | #include "misc.h" | 34 | #include "misc.h" |
35 | #include "filefuncs.h" | 35 | #include "pathfuncs.h" |
36 | #include "onplay.h" | 36 | #include "onplay.h" |
37 | #include "playlist.h" | 37 | #include "playlist.h" |
38 | #include "settings.h" | 38 | #include "settings.h" |
diff --git a/apps/plugin.c b/apps/plugin.c index 8edc773239..8a6c577f69 100644 --- a/apps/plugin.c +++ b/apps/plugin.c | |||
@@ -18,6 +18,8 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #define DIRFUNCTIONS_DEFINED | ||
22 | #define FILEFUNCTIONS_DEFINED | ||
21 | #include "plugin.h" | 23 | #include "plugin.h" |
22 | #include <ctype.h> | 24 | #include <ctype.h> |
23 | #include <string.h> | 25 | #include <string.h> |
@@ -40,8 +42,9 @@ | |||
40 | #include "pcmbuf.h" | 42 | #include "pcmbuf.h" |
41 | #include "errno.h" | 43 | #include "errno.h" |
42 | #include "diacritic.h" | 44 | #include "diacritic.h" |
43 | #include "filefuncs.h" | 45 | #include "pathfuncs.h" |
44 | #include "load_code.h" | 46 | #include "load_code.h" |
47 | #include "file.h" | ||
45 | 48 | ||
46 | #if CONFIG_CHARGING | 49 | #if CONFIG_CHARGING |
47 | #include "power.h" | 50 | #include "power.h" |
@@ -58,80 +61,119 @@ | |||
58 | #include "usbstack/usb_hid.h" | 61 | #include "usbstack/usb_hid.h" |
59 | #endif | 62 | #endif |
60 | 63 | ||
61 | #if defined (SIMULATOR) | 64 | #define WRAPPER(_x_) _x_ ## _wrapper |
62 | #define PREFIX(_x_) sim_ ## _x_ | 65 | |
63 | #elif defined (APPLICATION) | 66 | #if (CONFIG_PLATFORM & PLATFORM_HOSTED) |
64 | #define PREFIX(_x_) app_ ## _x_ | 67 | static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE]; |
68 | void sim_lcd_ex_init(unsigned long (*getpixel)(int, int)); | ||
69 | void sim_lcd_ex_update_rect(int x, int y, int width, int height); | ||
65 | #else | 70 | #else |
66 | #define PREFIX(_x_) _x_ | 71 | extern unsigned char pluginbuf[]; |
72 | #include "bitswap.h" | ||
67 | #endif | 73 | #endif |
68 | 74 | ||
69 | #if defined (APPLICATION) | 75 | /* for actual plugins only, not for codecs */ |
70 | /* For symmetry reasons (we want app_ and sim_ to behave similarly), some | 76 | static int plugin_size = 0; |
71 | * wrappers are needed */ | 77 | static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */ |
72 | static int app_close(int fd) | 78 | static char current_plugin[MAX_PATH]; |
79 | /* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */ | ||
80 | static void *current_plugin_handle; | ||
81 | |||
82 | char *plugin_get_current_filename(void); | ||
83 | |||
84 | static void* plugin_get_audio_buffer(size_t *buffer_size); | ||
85 | static void plugin_release_audio_buffer(void); | ||
86 | static void plugin_tsr(bool (*exit_callback)(bool)); | ||
87 | |||
88 | |||
89 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | ||
90 | /* File handle leak prophylaxis */ | ||
91 | #include "bitarray.h" | ||
92 | #include "file_internal.h" /* for MAX_OPEN_FILES */ | ||
93 | |||
94 | #define PCOC_WRAPPER(_x_) WRAPPER(_x_) | ||
95 | |||
96 | BITARRAY_TYPE_DECLARE(plugin_check_open_close_bitmap_t, open_files_bitmap, | ||
97 | MAX_OPEN_FILES) | ||
98 | |||
99 | static plugin_check_open_close_bitmap_t open_files_bitmap; | ||
100 | |||
101 | static void plugin_check_open_close__enter(void) | ||
73 | { | 102 | { |
74 | return close(fd); | 103 | if (!current_plugin_handle) |
104 | open_files_bitmap_clear(&open_files_bitmap); | ||
75 | } | 105 | } |
76 | 106 | ||
77 | static ssize_t app_read(int fd, void *buf, size_t count) | 107 | static void plugin_check_open_close__open(int fildes) |
78 | { | 108 | { |
79 | return read(fd,buf,count); | 109 | if (fildes >= 0) |
110 | open_files_bitmap_set_bit(&open_files_bitmap, fildes); | ||
80 | } | 111 | } |
81 | 112 | ||
82 | static off_t app_lseek(int fd, off_t offset, int whence) | 113 | static void plugin_check_open_close__close(int fildes) |
83 | { | 114 | { |
84 | return lseek(fd,offset,whence); | 115 | if (fildes < 0) |
116 | return; | ||
117 | |||
118 | if (!open_files_bitmap_test_bit(&open_files_bitmap, fildes)) | ||
119 | { | ||
120 | logf("double close from plugin"); | ||
121 | } | ||
122 | |||
123 | open_files_bitmap_clear_bit(&open_files_bitmap, fildes); | ||
85 | } | 124 | } |
86 | 125 | ||
87 | static ssize_t app_write(int fd, const void *buf, size_t count) | 126 | static int WRAPPER(open)(const char *path, int oflag, ...) |
88 | { | 127 | { |
89 | return write(fd,buf,count); | 128 | int fildes = FS_PREFIX(open)(path, oflag __OPEN_MODE_ARG); |
129 | plugin_check_open_close__open(fildes); | ||
130 | return fildes; | ||
90 | } | 131 | } |
91 | 132 | ||
92 | static int app_ftruncate(int fd, off_t length) | 133 | static int WRAPPER(creat)(const char *path, mode_t mode) |
93 | { | 134 | { |
94 | return ftruncate(fd,length); | 135 | int fildes = FS_PREFIX(creat)(path __CREAT_MODE_ARG); |
136 | plugin_check_open_close__open(fildes); | ||
137 | return fildes; | ||
138 | (void)mode; | ||
95 | } | 139 | } |
96 | #endif | ||
97 | 140 | ||
98 | #if defined(HAVE_PLUGIN_CHECK_OPEN_CLOSE) && (MAX_OPEN_FILES>32) | 141 | static int WRAPPER(close)(int fildes) |
99 | #warning "MAX_OPEN_FILES>32, disabling plugin file open/close checking" | 142 | { |
100 | #undef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 143 | int rc = FS_PREFIX(close)(fildes); |
101 | #endif | 144 | if (rc >= 0) |
145 | plugin_check_open_close__close(fildes); | ||
102 | 146 | ||
103 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 147 | return rc; |
104 | static unsigned int open_files; | 148 | } |
105 | #endif | ||
106 | 149 | ||
107 | #if (CONFIG_PLATFORM & PLATFORM_HOSTED) | 150 | static void plugin_check_open_close__exit(void) |
108 | static unsigned char pluginbuf[PLUGIN_BUFFER_SIZE]; | 151 | { |
109 | void sim_lcd_ex_init(unsigned long (*getpixel)(int, int)); | 152 | if (current_plugin_handle) |
110 | void sim_lcd_ex_update_rect(int x, int y, int width, int height); | 153 | return; |
111 | #else | ||
112 | extern unsigned char pluginbuf[]; | ||
113 | #include "bitswap.h" | ||
114 | #endif | ||
115 | 154 | ||
116 | /* for actual plugins only, not for codecs */ | 155 | if (open_files_bitmap_is_clear(&open_files_bitmap)) |
117 | static int plugin_size = 0; | 156 | return; |
118 | static bool (*pfn_tsr_exit)(bool reenter) = NULL; /* TSR exit callback */ | ||
119 | static char current_plugin[MAX_PATH]; | ||
120 | /* NULL if no plugin is loaded, otherwise the handle that lc_open() returned */ | ||
121 | static void *current_plugin_handle; | ||
122 | 157 | ||
123 | char *plugin_get_current_filename(void); | 158 | logf("Plugin '%s' leaks file handles", plugin); |
124 | 159 | ||
125 | /* Some wrappers used to monitor open and close and detect leaks*/ | 160 | static const char *lines[] = |
126 | static int open_wrapper(const char* pathname, int flags, ...); | 161 | { ID2P(LANG_PLUGIN_ERROR), "#leak-file-handles" }; |
127 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 162 | static const struct text_message message = { lines, 2 }; |
128 | static int close_wrapper(int fd); | 163 | button_clear_queue(); /* Empty the keyboard buffer */ |
129 | static int creat_wrapper(const char *pathname, mode_t mode); | 164 | gui_syncyesno_run(&message, NULL, NULL); |
130 | #endif | ||
131 | 165 | ||
132 | static void* plugin_get_audio_buffer(size_t *buffer_size); | 166 | FOR_EACH_BITARRAY_SET_BIT(&open_files_bitmap, fildes) |
133 | static void plugin_release_audio_buffer(void); | 167 | WRAPPER(close)(fildes); |
134 | static void plugin_tsr(bool (*exit_callback)(bool)); | 168 | } |
169 | |||
170 | #else /* !HAVE_PLUGIN_CHECK_OPEN_CLOSE */ | ||
171 | |||
172 | #define PCOC_WRAPPER(_x_) FS_PREFIX(_x_) | ||
173 | #define plugin_check_open_close__enter() | ||
174 | #define plugin_check_open_close__exit() | ||
175 | |||
176 | #endif /* HAVE_PLUGIN_CHECK_OPEN_CLOSE */ | ||
135 | 177 | ||
136 | static const struct plugin_api rockbox_api = { | 178 | static const struct plugin_api rockbox_api = { |
137 | 179 | ||
@@ -339,24 +381,16 @@ static const struct plugin_api rockbox_api = { | |||
339 | 381 | ||
340 | /* file */ | 382 | /* file */ |
341 | open_utf8, | 383 | open_utf8, |
342 | (open_func)open_wrapper, | 384 | PCOC_WRAPPER(open), |
343 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 385 | PCOC_WRAPPER(creat), |
344 | close_wrapper, | 386 | PCOC_WRAPPER(close), |
345 | #else | 387 | FS_PREFIX(read), |
346 | PREFIX(close), | 388 | FS_PREFIX(lseek), |
347 | #endif | 389 | FS_PREFIX(write), |
348 | (read_func)PREFIX(read), | 390 | FS_PREFIX(remove), |
349 | PREFIX(lseek), | 391 | FS_PREFIX(rename), |
350 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 392 | FS_PREFIX(ftruncate), |
351 | (creat_func)creat_wrapper, | 393 | FS_PREFIX(filesize), |
352 | #else | ||
353 | PREFIX(creat), | ||
354 | #endif | ||
355 | (write_func)PREFIX(write), | ||
356 | PREFIX(remove), | ||
357 | PREFIX(rename), | ||
358 | PREFIX(ftruncate), | ||
359 | filesize, | ||
360 | fdprintf, | 394 | fdprintf, |
361 | read_line, | 395 | read_line, |
362 | settings_parseline, | 396 | settings_parseline, |
@@ -369,18 +403,18 @@ static const struct plugin_api rockbox_api = { | |||
369 | #endif /* USING_STORAGE_CALLBACK */ | 403 | #endif /* USING_STORAGE_CALLBACK */ |
370 | reload_directory, | 404 | reload_directory, |
371 | create_numbered_filename, | 405 | create_numbered_filename, |
372 | file_exists, | 406 | FS_PREFIX(file_exists), |
373 | strip_extension, | 407 | strip_extension, |
374 | crc_32, | 408 | crc_32, |
375 | filetype_get_attr, | 409 | filetype_get_attr, |
376 | 410 | ||
377 | /* dir */ | 411 | /* dir */ |
378 | (opendir_func)opendir, | 412 | FS_PREFIX(opendir), |
379 | (closedir_func)closedir, | 413 | FS_PREFIX(closedir), |
380 | (readdir_func)readdir, | 414 | FS_PREFIX(readdir), |
381 | mkdir, | 415 | FS_PREFIX(mkdir), |
382 | rmdir, | 416 | FS_PREFIX(rmdir), |
383 | dir_exists, | 417 | FS_PREFIX(dir_exists), |
384 | dir_get_info, | 418 | dir_get_info, |
385 | 419 | ||
386 | /* browsing */ | 420 | /* browsing */ |
@@ -688,10 +722,11 @@ static const struct plugin_api rockbox_api = { | |||
688 | #endif | 722 | #endif |
689 | srand, | 723 | srand, |
690 | rand, | 724 | rand, |
691 | (qsort_func)qsort, | 725 | (void *)qsort, |
692 | kbd_input, | 726 | kbd_input, |
693 | get_time, | 727 | get_time, |
694 | set_time, | 728 | set_time, |
729 | gmtime_r, | ||
695 | #if CONFIG_RTC | 730 | #if CONFIG_RTC |
696 | mktime, | 731 | mktime, |
697 | #endif | 732 | #endif |
@@ -891,9 +926,7 @@ int plugin_load(const char* plugin, const void* parameter) | |||
891 | /* allow voice to back off if the plugin needs lots of memory */ | 926 | /* allow voice to back off if the plugin needs lots of memory */ |
892 | talk_buffer_set_policy(TALK_BUFFER_LOOSE); | 927 | talk_buffer_set_policy(TALK_BUFFER_LOOSE); |
893 | 928 | ||
894 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 929 | plugin_check_open_close__enter(); |
895 | open_files = 0; | ||
896 | #endif | ||
897 | 930 | ||
898 | int rc = p_hdr->entry_point(parameter); | 931 | int rc = p_hdr->entry_point(parameter); |
899 | 932 | ||
@@ -947,24 +980,7 @@ int plugin_load(const char* plugin, const void* parameter) | |||
947 | FOR_NB_SCREENS(i) | 980 | FOR_NB_SCREENS(i) |
948 | viewportmanager_theme_undo(i, true); | 981 | viewportmanager_theme_undo(i, true); |
949 | 982 | ||
950 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | 983 | plugin_check_open_close__exit(); |
951 | if(open_files != 0 && !current_plugin_handle) | ||
952 | { | ||
953 | int fd; | ||
954 | logf("Plugin '%s' leaks file handles", plugin); | ||
955 | |||
956 | static const char *lines[] = | ||
957 | { ID2P(LANG_PLUGIN_ERROR), | ||
958 | "#leak-file-handles" }; | ||
959 | static const struct text_message message={ lines, 2 }; | ||
960 | button_clear_queue(); /* Empty the keyboard buffer */ | ||
961 | gui_syncyesno_run(&message, NULL, NULL); | ||
962 | |||
963 | for(fd=0; fd < MAX_OPEN_FILES; fd++) | ||
964 | if(open_files & (1<<fd)) | ||
965 | close_wrapper(fd); | ||
966 | } | ||
967 | #endif | ||
968 | 984 | ||
969 | if (rc == PLUGIN_ERROR) | 985 | if (rc == PLUGIN_ERROR) |
970 | splash(HZ*2, str(LANG_PLUGIN_ERROR)); | 986 | splash(HZ*2, str(LANG_PLUGIN_ERROR)); |
@@ -1027,55 +1043,3 @@ char *plugin_get_current_filename(void) | |||
1027 | { | 1043 | { |
1028 | return current_plugin; | 1044 | return current_plugin; |
1029 | } | 1045 | } |
1030 | |||
1031 | static int open_wrapper(const char* pathname, int flags, ...) | ||
1032 | { | ||
1033 | /* we don't have an 'open' function. it's a define. and we need | ||
1034 | * the real file_open, hence PREFIX() doesn't work here */ | ||
1035 | int fd; | ||
1036 | #if (CONFIG_PLATFORM & PLATFORM_HOSTED) | ||
1037 | if (flags & O_CREAT) | ||
1038 | { | ||
1039 | va_list ap; | ||
1040 | va_start(ap, flags); | ||
1041 | fd = open(pathname, flags, va_arg(ap, unsigned int)); | ||
1042 | va_end(ap); | ||
1043 | } | ||
1044 | else | ||
1045 | fd = open(pathname, flags); | ||
1046 | #else | ||
1047 | fd = file_open(pathname,flags); | ||
1048 | #endif | ||
1049 | |||
1050 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | ||
1051 | if(fd >= 0) | ||
1052 | open_files |= 1<<fd; | ||
1053 | #endif | ||
1054 | return fd; | ||
1055 | } | ||
1056 | |||
1057 | #ifdef HAVE_PLUGIN_CHECK_OPEN_CLOSE | ||
1058 | static int close_wrapper(int fd) | ||
1059 | { | ||
1060 | if((~open_files) & (1<<fd)) | ||
1061 | { | ||
1062 | logf("double close from plugin"); | ||
1063 | } | ||
1064 | if(fd >= 0) | ||
1065 | open_files &= (~(1<<fd)); | ||
1066 | |||
1067 | return PREFIX(close)(fd); | ||
1068 | } | ||
1069 | |||
1070 | static int creat_wrapper(const char *pathname, mode_t mode) | ||
1071 | { | ||
1072 | (void)mode; | ||
1073 | |||
1074 | int fd = PREFIX(creat)(pathname, mode); | ||
1075 | |||
1076 | if(fd >= 0) | ||
1077 | open_files |= (1<<fd); | ||
1078 | |||
1079 | return fd; | ||
1080 | } | ||
1081 | #endif /* HAVE_PLUGIN_CHECK_OPEN_CLOSE */ | ||
diff --git a/apps/plugin.h b/apps/plugin.h index 8b8481b6ac..e55dcf13cb 100644 --- a/apps/plugin.h +++ b/apps/plugin.h | |||
@@ -75,7 +75,7 @@ void* plugin_get_buffer(size_t *buffer_size); | |||
75 | #include "profile.h" | 75 | #include "profile.h" |
76 | #endif | 76 | #endif |
77 | #include "misc.h" | 77 | #include "misc.h" |
78 | #include "filefuncs.h" | 78 | #include "pathfuncs.h" |
79 | #if (CONFIG_CODEC == SWCODEC) | 79 | #if (CONFIG_CODEC == SWCODEC) |
80 | #include "pcm_mixer.h" | 80 | #include "pcm_mixer.h" |
81 | #include "dsp-util.h" | 81 | #include "dsp-util.h" |
@@ -160,12 +160,12 @@ void* plugin_get_buffer(size_t *buffer_size); | |||
160 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ | 160 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ |
161 | 161 | ||
162 | /* increase this every time the api struct changes */ | 162 | /* increase this every time the api struct changes */ |
163 | #define PLUGIN_API_VERSION 231 | 163 | #define PLUGIN_API_VERSION 232 |
164 | 164 | ||
165 | /* update this to latest version if a change to the api struct breaks | 165 | /* update this to latest version if a change to the api struct breaks |
166 | backwards compatibility (and please take the opportunity to sort in any | 166 | backwards compatibility (and please take the opportunity to sort in any |
167 | new function which are "waiting" at the end of the function table) */ | 167 | new function which are "waiting" at the end of the function table) */ |
168 | #define PLUGIN_MIN_API_VERSION 231 | 168 | #define PLUGIN_MIN_API_VERSION 232 |
169 | 169 | ||
170 | /* plugin return codes */ | 170 | /* plugin return codes */ |
171 | /* internal returns start at 0x100 to make exit(1..255) work */ | 171 | /* internal returns start at 0x100 to make exit(1..255) work */ |
@@ -433,17 +433,17 @@ struct plugin_api { | |||
433 | 433 | ||
434 | /* file */ | 434 | /* file */ |
435 | int (*open_utf8)(const char* pathname, int flags); | 435 | int (*open_utf8)(const char* pathname, int flags); |
436 | int (*open)(const char* pathname, int flags, ...); | 436 | int (*open)(const char *path, int oflag, ...); |
437 | int (*close)(int fd); | 437 | int (*creat)(const char *path, mode_t mode); |
438 | ssize_t (*read)(int fd, void* buf, size_t count); | 438 | int (*close)(int fildes); |
439 | off_t (*lseek)(int fd, off_t offset, int whence); | 439 | ssize_t (*read)(int fildes, void *buf, size_t nbyte); |
440 | int (*creat)(const char *pathname, mode_t mode); | 440 | off_t (*lseek)(int fildes, off_t offset, int whence); |
441 | ssize_t (*write)(int fd, const void* buf, size_t count); | 441 | ssize_t (*write)(int fildes, const void *buf, size_t nbyte); |
442 | int (*remove)(const char* pathname); | 442 | int (*remove)(const char *path); |
443 | int (*rename)(const char* path, const char* newname); | 443 | int (*rename)(const char *old, const char *new); |
444 | int (*ftruncate)(int fd, off_t length); | 444 | int (*ftruncate)(int fildes, off_t length); |
445 | off_t (*filesize)(int fd); | 445 | off_t (*filesize)(int fildes); |
446 | int (*fdprintf)(int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); | 446 | int (*fdprintf)(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); |
447 | int (*read_line)(int fd, char* buffer, int buffer_size); | 447 | int (*read_line)(int fd, char* buffer, int buffer_size); |
448 | bool (*settings_parseline)(char* line, char** name, char** value); | 448 | bool (*settings_parseline)(char* line, char** name, char** value); |
449 | void (*storage_sleep)(void); | 449 | void (*storage_sleep)(void); |
@@ -457,7 +457,7 @@ struct plugin_api { | |||
457 | char *(*create_numbered_filename)(char *buffer, const char *path, | 457 | char *(*create_numbered_filename)(char *buffer, const char *path, |
458 | const char *prefix, const char *suffix, | 458 | const char *prefix, const char *suffix, |
459 | int numberlen IF_CNFN_NUM_(, int *num)); | 459 | int numberlen IF_CNFN_NUM_(, int *num)); |
460 | bool (*file_exists)(const char *file); | 460 | bool (*file_exists)(const char *path); |
461 | char* (*strip_extension)(char* buffer, int buffer_size, const char *filename); | 461 | char* (*strip_extension)(char* buffer, int buffer_size, const char *filename); |
462 | uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32); | 462 | uint32_t (*crc_32)(const void *src, uint32_t len, uint32_t crc32); |
463 | 463 | ||
@@ -466,13 +466,13 @@ struct plugin_api { | |||
466 | 466 | ||
467 | 467 | ||
468 | /* dir */ | 468 | /* dir */ |
469 | DIR* (*opendir)(const char* name); | 469 | DIR * (*opendir)(const char *dirname); |
470 | int (*closedir)(DIR* dir); | 470 | int (*closedir)(DIR *dirp); |
471 | struct dirent* (*readdir)(DIR* dir); | 471 | struct dirent * (*readdir)(DIR *dirp); |
472 | int (*mkdir)(const char *name); | 472 | int (*mkdir)(const char *path); |
473 | int (*rmdir)(const char *name); | 473 | int (*rmdir)(const char *path); |
474 | bool (*dir_exists)(const char *path); | 474 | bool (*dir_exists)(const char *dirname); |
475 | struct dirinfo (*dir_get_info)(DIR* parent, struct dirent *entry); | 475 | struct dirinfo (*dir_get_info)(DIR *dirp, struct dirent *entry); |
476 | 476 | ||
477 | /* browsing */ | 477 | /* browsing */ |
478 | void (*browse_context_init)(struct browse_context *browse, | 478 | void (*browse_context_init)(struct browse_context *browse, |
@@ -838,6 +838,7 @@ struct plugin_api { | |||
838 | int (*kbd_input)(char* buffer, int buflen); | 838 | int (*kbd_input)(char* buffer, int buflen); |
839 | struct tm* (*get_time)(void); | 839 | struct tm* (*get_time)(void); |
840 | int (*set_time)(const struct tm *tm); | 840 | int (*set_time)(const struct tm *tm); |
841 | struct tm * (*gmtime_r)(const time_t *timep, struct tm *tm); | ||
841 | #if CONFIG_RTC | 842 | #if CONFIG_RTC |
842 | time_t (*mktime)(struct tm *t); | 843 | time_t (*mktime)(struct tm *t); |
843 | #endif | 844 | #endif |
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c index 0f3ec5c458..3115da94a4 100644 --- a/apps/plugins/properties.c +++ b/apps/plugins/properties.c | |||
@@ -99,13 +99,12 @@ static bool file_properties(char* selected_file) | |||
99 | log = human_size_log((unsigned long)info.size); | 99 | log = human_size_log((unsigned long)info.size); |
100 | rb->snprintf(str_size, sizeof str_size, "%lu %cB", | 100 | rb->snprintf(str_size, sizeof str_size, "%lu %cB", |
101 | ((unsigned long)info.size) >> (log*10), human_size_prefix[log]); | 101 | ((unsigned long)info.size) >> (log*10), human_size_prefix[log]); |
102 | struct tm tm; | ||
103 | rb->gmtime_r(&info.mtime, &tm); | ||
102 | rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d", | 104 | rb->snprintf(str_date, sizeof str_date, "%04d/%02d/%02d", |
103 | ((info.wrtdate >> 9 ) & 0x7F) + 1980, /* year */ | 105 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); |
104 | ((info.wrtdate >> 5 ) & 0x0F), /* month */ | 106 | rb->snprintf(str_time, sizeof str_time, "%02d:%02d:%02d", |
105 | ((info.wrtdate ) & 0x1F)); /* day */ | 107 | tm.tm_hour, tm.tm_min, tm.tm_sec); |
106 | rb->snprintf(str_time, sizeof str_time, "%02d:%02d", | ||
107 | ((info.wrttime >> 11) & 0x1F), /* hour */ | ||
108 | ((info.wrttime >> 5 ) & 0x3F)); /* minutes */ | ||
109 | 108 | ||
110 | num_properties = 5; | 109 | num_properties = 5; |
111 | 110 | ||
@@ -175,7 +174,10 @@ static bool _dir_properties(DPS* dps) | |||
175 | dirlen = rb->strlen(dps->dirname); | 174 | dirlen = rb->strlen(dps->dirname); |
176 | dir = rb->opendir(dps->dirname); | 175 | dir = rb->opendir(dps->dirname); |
177 | if (!dir) | 176 | if (!dir) |
177 | { | ||
178 | rb->splashf(HZ*2, "%s", dps->dirname); | ||
178 | return false; /* open error */ | 179 | return false; /* open error */ |
180 | } | ||
179 | 181 | ||
180 | /* walk through the directory content */ | 182 | /* walk through the directory content */ |
181 | while(result && (0 != (entry = rb->readdir(dir)))) | 183 | while(result && (0 != (entry = rb->readdir(dir)))) |
diff --git a/apps/radio/presets.c b/apps/radio/presets.c index 9eab4901f1..d9a2aa9bcd 100644 --- a/apps/radio/presets.c +++ b/apps/radio/presets.c | |||
@@ -30,7 +30,7 @@ | |||
30 | #include "file.h" | 30 | #include "file.h" |
31 | #include "string-extra.h" | 31 | #include "string-extra.h" |
32 | #include "misc.h" | 32 | #include "misc.h" |
33 | #include "filefuncs.h" | 33 | #include "pathfuncs.h" |
34 | #include "lang.h" | 34 | #include "lang.h" |
35 | #include "action.h" | 35 | #include "action.h" |
36 | #include "list.h" | 36 | #include "list.h" |
diff --git a/apps/radio/radioart.c b/apps/radio/radioart.c index 283815167a..5e1a0ad5cf 100644 --- a/apps/radio/radioart.c +++ b/apps/radio/radioart.c | |||
@@ -31,7 +31,7 @@ | |||
31 | #include "file.h" | 31 | #include "file.h" |
32 | #include "kernel.h" | 32 | #include "kernel.h" |
33 | #include "string-extra.h" | 33 | #include "string-extra.h" |
34 | #include "filefuncs.h" | 34 | #include "pathfuncs.h" |
35 | #include "core_alloc.h" | 35 | #include "core_alloc.h" |
36 | 36 | ||
37 | #define MAX_RADIOART_IMAGES 10 | 37 | #define MAX_RADIOART_IMAGES 10 |
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c index 4cbabbc8ce..c561e36ae2 100644 --- a/apps/recorder/albumart.c +++ b/apps/recorder/albumart.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include "buffering.h" | 27 | #include "buffering.h" |
28 | #include "dircache.h" | 28 | #include "dircache.h" |
29 | #include "misc.h" | 29 | #include "misc.h" |
30 | #include "filefuncs.h" | 30 | #include "pathfuncs.h" |
31 | #include "settings.h" | 31 | #include "settings.h" |
32 | #include "wps.h" | 32 | #include "wps.h" |
33 | 33 | ||
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index 5b341fd141..1c53c8026f 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c | |||
@@ -56,7 +56,7 @@ | |||
56 | #include "timefuncs.h" | 56 | #include "timefuncs.h" |
57 | #include "debug.h" | 57 | #include "debug.h" |
58 | #include "misc.h" | 58 | #include "misc.h" |
59 | #include "filefuncs.h" | 59 | #include "pathfuncs.h" |
60 | #include "tree.h" | 60 | #include "tree.h" |
61 | #include "string.h" | 61 | #include "string.h" |
62 | #include "dir.h" | 62 | #include "dir.h" |
diff --git a/apps/root_menu.c b/apps/root_menu.c index 189b2ec35c..7ec803f585 100644 --- a/apps/root_menu.c +++ b/apps/root_menu.c | |||
@@ -146,11 +146,10 @@ static int browser(void* param) | |||
146 | int i; | 146 | int i; |
147 | for (i = 0; i < NUM_VOLUMES; i++) | 147 | for (i = 0; i < NUM_VOLUMES; i++) |
148 | { | 148 | { |
149 | char vol_string[VOL_ENUM_POS + 8]; | 149 | char vol_string[VOL_MAX_LEN + 1]; |
150 | if (!volume_removable(i)) | 150 | if (!volume_removable(i)) |
151 | continue; | 151 | continue; |
152 | /* VOL_NAMES contains a %d */ | 152 | get_volume_name(i, vol_string); |
153 | snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, i); | ||
154 | /* test whether we would browse the external card */ | 153 | /* test whether we would browse the external card */ |
155 | if (!volume_present(i) && | 154 | if (!volume_present(i) && |
156 | (strstr(last_folder, vol_string) | 155 | (strstr(last_folder, vol_string) |
diff --git a/apps/scrobbler.c b/apps/scrobbler.c index b8a95f85cb..4f3693e716 100644 --- a/apps/scrobbler.c +++ b/apps/scrobbler.c | |||
@@ -33,7 +33,7 @@ http://www.audioscrobbler.net/wiki/Portable_Player_Logging | |||
33 | #include "core_alloc.h" | 33 | #include "core_alloc.h" |
34 | #include "settings.h" | 34 | #include "settings.h" |
35 | #include "ata_idle_notify.h" | 35 | #include "ata_idle_notify.h" |
36 | #include "filefuncs.h" | 36 | #include "pathfuncs.h" |
37 | #include "appevents.h" | 37 | #include "appevents.h" |
38 | 38 | ||
39 | #if CONFIG_RTC | 39 | #if CONFIG_RTC |
diff --git a/apps/settings.c b/apps/settings.c index f2a923e24d..819924a421 100644 --- a/apps/settings.c +++ b/apps/settings.c | |||
@@ -822,6 +822,10 @@ void settings_apply(bool read_disk) | |||
822 | #ifdef HAVE_LCD_BITMAP | 822 | #ifdef HAVE_LCD_BITMAP |
823 | int rc; | 823 | int rc; |
824 | #endif | 824 | #endif |
825 | CHART(">set_codepage"); | ||
826 | set_codepage(global_settings.default_codepage); | ||
827 | CHART("<set_codepage"); | ||
828 | |||
825 | sound_settings_apply(); | 829 | sound_settings_apply(); |
826 | 830 | ||
827 | #ifdef HAVE_DISK_STORAGE | 831 | #ifdef HAVE_DISK_STORAGE |
@@ -1008,10 +1012,6 @@ void settings_apply(bool read_disk) | |||
1008 | lcd_scroll_delay(global_settings.scroll_delay); | 1012 | lcd_scroll_delay(global_settings.scroll_delay); |
1009 | 1013 | ||
1010 | 1014 | ||
1011 | CHART(">set_codepage"); | ||
1012 | set_codepage(global_settings.default_codepage); | ||
1013 | CHART("<set_codepage"); | ||
1014 | |||
1015 | #ifdef HAVE_PLAY_FREQ | 1015 | #ifdef HAVE_PLAY_FREQ |
1016 | settings_apply_play_freq(global_settings.play_frequency, false); | 1016 | settings_apply_play_freq(global_settings.play_frequency, false); |
1017 | #endif | 1017 | #endif |
diff --git a/apps/settings_list.c b/apps/settings_list.c index af83866356..53acb78d98 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c | |||
@@ -1724,13 +1724,13 @@ const struct settings_list settings[] = { | |||
1724 | OFFON_SETTING(F_BANFROMQS, tagcache_autoupdate, LANG_TAGCACHE_AUTOUPDATE, false, | 1724 | OFFON_SETTING(F_BANFROMQS, tagcache_autoupdate, LANG_TAGCACHE_AUTOUPDATE, false, |
1725 | "tagcache_autoupdate", NULL), | 1725 | "tagcache_autoupdate", NULL), |
1726 | #endif | 1726 | #endif |
1727 | CHOICE_SETTING(0, default_codepage, LANG_DEFAULT_CODEPAGE, 0, | 1727 | CHOICE_SETTING(F_TEMPVAR, default_codepage, LANG_DEFAULT_CODEPAGE, 0, |
1728 | "default codepage", | 1728 | "default codepage", |
1729 | #ifdef HAVE_LCD_BITMAP | 1729 | #ifdef HAVE_LCD_BITMAP |
1730 | /* The order must match with that in unicode.c */ | 1730 | /* The order must match with that in unicode.c */ |
1731 | "iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256," | 1731 | "iso8859-1,iso8859-7,iso8859-8,cp1251,iso8859-11,cp1256," |
1732 | "iso8859-9,iso8859-2,cp1250,cp1252,sjis,gb2312,ksx1001,big5,utf-8", | 1732 | "iso8859-9,iso8859-2,cp1250,cp1252,sjis,gb2312,ksx1001,big5,utf-8", |
1733 | set_codepage, 15, | 1733 | NULL, 15, |
1734 | ID2P(LANG_CODEPAGE_LATIN1), | 1734 | ID2P(LANG_CODEPAGE_LATIN1), |
1735 | ID2P(LANG_CODEPAGE_GREEK), | 1735 | ID2P(LANG_CODEPAGE_GREEK), |
1736 | ID2P(LANG_CODEPAGE_HEBREW), ID2P(LANG_CODEPAGE_CYRILLIC), | 1736 | ID2P(LANG_CODEPAGE_HEBREW), ID2P(LANG_CODEPAGE_CYRILLIC), |
@@ -1745,7 +1745,7 @@ const struct settings_list settings[] = { | |||
1745 | #else /* !HAVE_LCD_BITMAP */ | 1745 | #else /* !HAVE_LCD_BITMAP */ |
1746 | /* The order must match with that in unicode.c */ | 1746 | /* The order must match with that in unicode.c */ |
1747 | "iso8859-1,iso8859-7,cp1251,iso8859-9,iso8859-2,cp1250,cp1252,utf-8", | 1747 | "iso8859-1,iso8859-7,cp1251,iso8859-9,iso8859-2,cp1250,cp1252,utf-8", |
1748 | set_codepage, 8, | 1748 | NULL, 8, |
1749 | ID2P(LANG_CODEPAGE_LATIN1), ID2P(LANG_CODEPAGE_GREEK), | 1749 | ID2P(LANG_CODEPAGE_LATIN1), ID2P(LANG_CODEPAGE_GREEK), |
1750 | ID2P(LANG_CODEPAGE_CYRILLIC), ID2P(LANG_CODEPAGE_TURKISH), | 1750 | ID2P(LANG_CODEPAGE_CYRILLIC), ID2P(LANG_CODEPAGE_TURKISH), |
1751 | ID2P(LANG_CODEPAGE_LATIN_EXTENDED), | 1751 | ID2P(LANG_CODEPAGE_LATIN_EXTENDED), |
diff --git a/apps/shortcuts.c b/apps/shortcuts.c index a9ae8248f1..1153edd2ad 100644 --- a/apps/shortcuts.c +++ b/apps/shortcuts.c | |||
@@ -37,7 +37,7 @@ | |||
37 | #include "misc.h" | 37 | #include "misc.h" |
38 | #include "tree.h" | 38 | #include "tree.h" |
39 | #include "splash.h" | 39 | #include "splash.h" |
40 | #include "filefuncs.h" | 40 | #include "pathfuncs.h" |
41 | #include "filetypes.h" | 41 | #include "filetypes.h" |
42 | #include "shortcuts.h" | 42 | #include "shortcuts.h" |
43 | #include "onplay.h" | 43 | #include "onplay.h" |
diff --git a/apps/tagcache.c b/apps/tagcache.c index b7d5516e81..2b6041227b 100644 --- a/apps/tagcache.c +++ b/apps/tagcache.c | |||
@@ -79,7 +79,7 @@ | |||
79 | #include "misc.h" | 79 | #include "misc.h" |
80 | #include "settings.h" | 80 | #include "settings.h" |
81 | #include "dir.h" | 81 | #include "dir.h" |
82 | #include "filefuncs.h" | 82 | #include "pathfuncs.h" |
83 | #include "structec.h" | 83 | #include "structec.h" |
84 | #include "debug.h" | 84 | #include "debug.h" |
85 | 85 | ||
@@ -88,6 +88,8 @@ | |||
88 | #include "eeprom_settings.h" | 88 | #include "eeprom_settings.h" |
89 | #endif | 89 | #endif |
90 | 90 | ||
91 | #undef HAVE_DIRCACHE | ||
92 | |||
91 | #ifdef __PCTOOL__ | 93 | #ifdef __PCTOOL__ |
92 | #define yield() do { } while(0) | 94 | #define yield() do { } while(0) |
93 | #define sim_sleep(timeout) do { } while(0) | 95 | #define sim_sleep(timeout) do { } while(0) |
@@ -2986,20 +2988,21 @@ static bool commit(void) | |||
2986 | /* Try to steal every buffer we can :) */ | 2988 | /* Try to steal every buffer we can :) */ |
2987 | if (tempbuf_size == 0) | 2989 | if (tempbuf_size == 0) |
2988 | local_allocation = true; | 2990 | local_allocation = true; |
2989 | 2991 | ||
2992 | #if 0 /* FIXME: How much big? dircache buffer can no longer be taken but | ||
2993 | may be freed to make room and the cache resumed. --jethead71 */ | ||
2990 | #ifdef HAVE_DIRCACHE | 2994 | #ifdef HAVE_DIRCACHE |
2991 | if (tempbuf_size == 0) | 2995 | if (tempbuf_size == 0) |
2992 | { | 2996 | { |
2993 | /* Try to steal the dircache buffer. */ | 2997 | /* Shut down dircache to free its allocation. */ |
2994 | tempbuf = dircache_steal_buffer(&tempbuf_size); | 2998 | dircache_free_buffer(); |
2995 | tempbuf_size &= ~0x03; | ||
2996 | |||
2997 | if (tempbuf_size > 0) | 2999 | if (tempbuf_size > 0) |
2998 | { | 3000 | { |
2999 | dircache_buffer_stolen = true; | 3001 | dircache_buffer_stolen = true; |
3000 | } | 3002 | } |
3001 | } | 3003 | } |
3002 | #endif | 3004 | #endif |
3005 | #endif | ||
3003 | 3006 | ||
3004 | #ifdef HAVE_TC_RAMCACHE | 3007 | #ifdef HAVE_TC_RAMCACHE |
3005 | if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0) | 3008 | if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0) |
@@ -4462,7 +4465,7 @@ static bool check_dir(const char *dirname, int add_files) | |||
4462 | tc_stat.curentry = curpath; | 4465 | tc_stat.curentry = curpath; |
4463 | 4466 | ||
4464 | /* Add a new entry to the temporary db file. */ | 4467 | /* Add a new entry to the temporary db file. */ |
4465 | add_tagcache(curpath, (info.wrtdate << 16) | info.wrttime | 4468 | add_tagcache(curpath, info.mtime |
4466 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) | 4469 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) |
4467 | , dir->internal_entry | 4470 | , dir->internal_entry |
4468 | #endif | 4471 | #endif |
@@ -4780,7 +4783,7 @@ void tagcache_shutdown(void) | |||
4780 | /* Flush the command queue. */ | 4783 | /* Flush the command queue. */ |
4781 | run_command_queue(true); | 4784 | run_command_queue(true); |
4782 | 4785 | ||
4783 | #ifdef HAVE_EEPROM_SETTINGS | 4786 | #if defined(HAVE_EEPROM_SETTINGS) && defined(HAVE_TC_RAMCACHE) |
4784 | if (tc_stat.ramcache) | 4787 | if (tc_stat.ramcache) |
4785 | tagcache_dumpsave(); | 4788 | tagcache_dumpsave(); |
4786 | #endif | 4789 | #endif |
diff --git a/apps/tree.c b/apps/tree.c index f72774fe1e..938e44d350 100644 --- a/apps/tree.c +++ b/apps/tree.c | |||
@@ -52,7 +52,7 @@ | |||
52 | #include "talk.h" | 52 | #include "talk.h" |
53 | #include "filetypes.h" | 53 | #include "filetypes.h" |
54 | #include "misc.h" | 54 | #include "misc.h" |
55 | #include "filefuncs.h" | 55 | #include "pathfuncs.h" |
56 | #include "filetree.h" | 56 | #include "filetree.h" |
57 | #include "tagtree.h" | 57 | #include "tagtree.h" |
58 | #ifdef HAVE_RECORDING | 58 | #ifdef HAVE_RECORDING |
@@ -1205,26 +1205,36 @@ void tree_flush(void) | |||
1205 | #endif | 1205 | #endif |
1206 | 1206 | ||
1207 | #ifdef HAVE_DIRCACHE | 1207 | #ifdef HAVE_DIRCACHE |
1208 | int old_val = global_status.dircache_size; | ||
1209 | #ifdef HAVE_EEPROM_SETTINGS | ||
1210 | bool savecache = false; | ||
1211 | #endif | ||
1212 | |||
1213 | if (global_settings.dircache) | ||
1208 | { | 1214 | { |
1209 | int old_val = global_status.dircache_size; | 1215 | dircache_suspend(); |
1210 | if (global_settings.dircache) | 1216 | |
1211 | { | 1217 | struct dircache_info info; |
1212 | if (!dircache_is_initializing()) | 1218 | dircache_get_info(&info); |
1213 | global_status.dircache_size = dircache_get_cache_size(); | 1219 | |
1214 | # ifdef HAVE_EEPROM_SETTINGS | 1220 | global_status.dircache_size = info.last_size; |
1215 | if (firmware_settings.initialized) | 1221 | #ifdef HAVE_EEPROM_SETTINGS |
1216 | dircache_save(); | 1222 | savecache = firmware_settings.initialized; |
1217 | # endif | 1223 | #endif |
1218 | dircache_suspend(); | ||
1219 | } | ||
1220 | else | ||
1221 | { | ||
1222 | global_status.dircache_size = 0; | ||
1223 | } | ||
1224 | if (old_val != global_status.dircache_size) | ||
1225 | status_save(); | ||
1226 | } | 1224 | } |
1227 | #endif | 1225 | else |
1226 | { | ||
1227 | global_status.dircache_size = 0; | ||
1228 | } | ||
1229 | |||
1230 | if (old_val != global_status.dircache_size) | ||
1231 | status_save(); | ||
1232 | |||
1233 | #ifdef HAVE_EEPROM_SETTINGS | ||
1234 | if (savecache) | ||
1235 | dircache_save(); | ||
1236 | #endif | ||
1237 | #endif /* HAVE_DIRCACHE */ | ||
1228 | } | 1238 | } |
1229 | 1239 | ||
1230 | void tree_restore(void) | 1240 | void tree_restore(void) |
@@ -1238,15 +1248,14 @@ void tree_restore(void) | |||
1238 | #endif | 1248 | #endif |
1239 | 1249 | ||
1240 | #ifdef HAVE_DIRCACHE | 1250 | #ifdef HAVE_DIRCACHE |
1241 | remove(DIRCACHE_FILE); | 1251 | if (global_settings.dircache && dircache_resume() > 0) |
1242 | if (global_settings.dircache) | ||
1243 | { | 1252 | { |
1244 | /* Print "Scanning disk..." to the display. */ | 1253 | /* Print "Scanning disk..." to the display. */ |
1245 | splash(0, str(LANG_SCANNING_DISK)); | 1254 | splash(0, str(LANG_SCANNING_DISK)); |
1246 | 1255 | dircache_wait(); | |
1247 | dircache_build(global_status.dircache_size); | ||
1248 | } | 1256 | } |
1249 | #endif | 1257 | #endif |
1258 | |||
1250 | #ifdef HAVE_TAGCACHE | 1259 | #ifdef HAVE_TAGCACHE |
1251 | tagcache_start_scan(); | 1260 | tagcache_start_scan(); |
1252 | #endif | 1261 | #endif |
diff --git a/bootloader/creativezvm.c b/bootloader/creativezvm.c index f31f6490a4..6597448193 100644 --- a/bootloader/creativezvm.c +++ b/bootloader/creativezvm.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "../kernel-internal.h" | 22 | #include "../kernel-internal.h" |
23 | #include "storage.h" | 23 | #include "storage.h" |
24 | #include "ata-target.h" | 24 | #include "ata-target.h" |
25 | #include "file_internal.h" | ||
25 | #include "disk.h" | 26 | #include "disk.h" |
26 | #include "font.h" | 27 | #include "font.h" |
27 | #include "backlight.h" | 28 | #include "backlight.h" |
@@ -73,6 +74,8 @@ void main(void) | |||
73 | ret = storage_init(); | 74 | ret = storage_init(); |
74 | if(ret) | 75 | if(ret) |
75 | printf("ATA error: %d", ret); | 76 | printf("ATA error: %d", ret); |
77 | |||
78 | filesystem_init(); | ||
76 | 79 | ||
77 | /* If no button is held, start the OF */ | 80 | /* If no button is held, start the OF */ |
78 | if(button_read_device() == 0) | 81 | if(button_read_device() == 0) |
@@ -93,8 +96,6 @@ void main(void) | |||
93 | } | 96 | } |
94 | else | 97 | else |
95 | { | 98 | { |
96 | disk_init(); | ||
97 | |||
98 | ret = disk_mount_all(); | 99 | ret = disk_mount_all(); |
99 | if (ret <= 0) | 100 | if (ret <= 0) |
100 | error(EDISK, ret, true); | 101 | error(EDISK, ret, true); |
diff --git a/bootloader/gigabeat-s.c b/bootloader/gigabeat-s.c index 3b852dbaa8..499d9c218c 100644 --- a/bootloader/gigabeat-s.c +++ b/bootloader/gigabeat-s.c | |||
@@ -21,13 +21,16 @@ | |||
21 | #include "config.h" | 21 | #include "config.h" |
22 | #include "system.h" | 22 | #include "system.h" |
23 | #include <stdio.h> | 23 | #include <stdio.h> |
24 | #include <errno.h> | ||
24 | #include "../kernel-internal.h" | 25 | #include "../kernel-internal.h" |
25 | #include "gcc_extensions.h" | 26 | #include "gcc_extensions.h" |
26 | #include "string.h" | 27 | #include "string.h" |
27 | #include "adc.h" | 28 | #include "adc.h" |
28 | #include "powermgmt.h" | 29 | #include "powermgmt.h" |
29 | #include "storage.h" | 30 | #include "storage.h" |
31 | #include "file_internal.h" | ||
30 | #include "dir.h" | 32 | #include "dir.h" |
33 | #include "file.h" | ||
31 | #include "disk.h" | 34 | #include "disk.h" |
32 | #include "common.h" | 35 | #include "common.h" |
33 | #include "rb-loader.h" | 36 | #include "rb-loader.h" |
@@ -219,7 +222,7 @@ static void untar(int tar_fd) | |||
219 | 222 | ||
220 | /* Create the dir */ | 223 | /* Create the dir */ |
221 | ret = mkdir(path); | 224 | ret = mkdir(path); |
222 | if (ret < 0 && ret != -4) | 225 | if (ret < 0 && errno != EEXIST) |
223 | { | 226 | { |
224 | printf("failed to create dir (%d)", ret); | 227 | printf("failed to create dir (%d)", ret); |
225 | } | 228 | } |
@@ -233,14 +236,14 @@ static void handle_untar(void) | |||
233 | char buf[MAX_PATH]; | 236 | char buf[MAX_PATH]; |
234 | char tarstring[6]; | 237 | char tarstring[6]; |
235 | char model[5]; | 238 | char model[5]; |
236 | struct dirent_uncached* entry; | 239 | struct dirent* entry; |
237 | DIR_UNCACHED* dir; | 240 | DIR* dir; |
238 | int fd; | 241 | int fd; |
239 | int rc; | 242 | int rc; |
240 | 243 | ||
241 | dir = opendir_uncached(basedir); | 244 | dir = opendir(basedir); |
242 | 245 | ||
243 | while ((entry = readdir_uncached(dir))) | 246 | while ((entry = readdir(dir))) |
244 | { | 247 | { |
245 | if (*entry->d_name == '.') | 248 | if (*entry->d_name == '.') |
246 | continue; | 249 | continue; |
@@ -262,7 +265,6 @@ static void handle_untar(void) | |||
262 | verbose = true; | 265 | verbose = true; |
263 | printf("Found rockbox binary. Moving..."); | 266 | printf("Found rockbox binary. Moving..."); |
264 | close(fd); | 267 | close(fd); |
265 | remove( BOOTDIR "/" BOOTFILE); | ||
266 | int ret = rename(buf, BOOTDIR "/" BOOTFILE); | 268 | int ret = rename(buf, BOOTDIR "/" BOOTFILE); |
267 | printf("returned %d", ret); | 269 | printf("returned %d", ret); |
268 | sleep(HZ); | 270 | sleep(HZ); |
@@ -365,7 +367,7 @@ void main(void) | |||
365 | if(rc) | 367 | if(rc) |
366 | error(EATA, rc, true); | 368 | error(EATA, rc, true); |
367 | 369 | ||
368 | disk_init(); | 370 | filesystem_init(); |
369 | 371 | ||
370 | rc = disk_mount_all(); | 372 | rc = disk_mount_all(); |
371 | if (rc <= 0) | 373 | if (rc <= 0) |
diff --git a/bootloader/gigabeat.c b/bootloader/gigabeat.c index 52f55a5cdd..4d52407655 100644 --- a/bootloader/gigabeat.c +++ b/bootloader/gigabeat.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "../kernel-internal.h" | 30 | #include "../kernel-internal.h" |
31 | #include "storage.h" | 31 | #include "storage.h" |
32 | #include "fat.h" | 32 | #include "fat.h" |
33 | #include "file_internal.h" | ||
33 | #include "disk.h" | 34 | #include "disk.h" |
34 | #include "font.h" | 35 | #include "font.h" |
35 | #include "adc.h" | 36 | #include "adc.h" |
@@ -185,7 +186,7 @@ void main(void) | |||
185 | error(EATA, rc, true); | 186 | error(EATA, rc, true); |
186 | } | 187 | } |
187 | 188 | ||
188 | disk_init(); | 189 | filesystem_init(); |
189 | 190 | ||
190 | rc = disk_mount_all(); | 191 | rc = disk_mount_all(); |
191 | if (rc<=0) | 192 | if (rc<=0) |
diff --git a/bootloader/iaudio_coldfire.c b/bootloader/iaudio_coldfire.c index 013e8859e8..2f31958a87 100644 --- a/bootloader/iaudio_coldfire.c +++ b/bootloader/iaudio_coldfire.c | |||
@@ -196,7 +196,7 @@ void main(void) | |||
196 | if(rc) | 196 | if(rc) |
197 | error(EATA, rc, true); | 197 | error(EATA, rc, true); |
198 | 198 | ||
199 | disk_init(); | 199 | filesystem_init(); |
200 | 200 | ||
201 | rc = disk_mount_all(); | 201 | rc = disk_mount_all(); |
202 | if (rc<=0) | 202 | if (rc<=0) |
diff --git a/bootloader/imx233.c b/bootloader/imx233.c index d2562d0676..76fdf884da 100644 --- a/bootloader/imx233.c +++ b/bootloader/imx233.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include "rb-loader.h" | 35 | #include "rb-loader.h" |
36 | #include "loader_strerror.h" | 36 | #include "loader_strerror.h" |
37 | #include "storage.h" | 37 | #include "storage.h" |
38 | #include "file_internal.h" | ||
38 | #include "disk.h" | 39 | #include "disk.h" |
39 | #include "panic.h" | 40 | #include "panic.h" |
40 | #include "power.h" | 41 | #include "power.h" |
@@ -171,7 +172,7 @@ void main(uint32_t arg, uint32_t addr) | |||
171 | if(ret < 0) | 172 | if(ret < 0) |
172 | error(EATA, ret, true); | 173 | error(EATA, ret, true); |
173 | 174 | ||
174 | disk_init_subsystem(); | 175 | filesystem_init(); |
175 | 176 | ||
176 | /* NOTE: disk_mount_all to fail since we can do USB after. | 177 | /* NOTE: disk_mount_all to fail since we can do USB after. |
177 | * We need this order to determine the correct logical sector size */ | 178 | * We need this order to determine the correct logical sector size */ |
diff --git a/bootloader/ipod.c b/bootloader/ipod.c index b2d4532168..8580886ce4 100644 --- a/bootloader/ipod.c +++ b/bootloader/ipod.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "../kernel-internal.h" | 33 | #include "../kernel-internal.h" |
34 | #include "ata.h" | 34 | #include "ata.h" |
35 | #include "fat.h" | 35 | #include "fat.h" |
36 | #include "file_internal.h" | ||
36 | #include "disk.h" | 37 | #include "disk.h" |
37 | #include "font.h" | 38 | #include "font.h" |
38 | #include "adc.h" | 39 | #include "adc.h" |
@@ -295,7 +296,7 @@ void* main(void) | |||
295 | int rc; | 296 | int rc; |
296 | bool haveramos; | 297 | bool haveramos; |
297 | bool button_was_held; | 298 | bool button_was_held; |
298 | struct partinfo* pinfo; | 299 | struct partinfo pinfo; |
299 | unsigned short* identify_info; | 300 | unsigned short* identify_info; |
300 | 301 | ||
301 | /* Check the button hold status as soon as possible - to | 302 | /* Check the button hold status as soon as possible - to |
@@ -353,7 +354,8 @@ void* main(void) | |||
353 | printf("ATA: %d", i); | 354 | printf("ATA: %d", i); |
354 | } | 355 | } |
355 | 356 | ||
356 | disk_init(); | 357 | filesystem_init(); |
358 | |||
357 | rc = disk_mount_all(); | 359 | rc = disk_mount_all(); |
358 | if (rc<=0) | 360 | if (rc<=0) |
359 | { | 361 | { |
@@ -361,9 +363,9 @@ void* main(void) | |||
361 | fatal_error(); | 363 | fatal_error(); |
362 | } | 364 | } |
363 | 365 | ||
364 | pinfo = disk_partinfo(1); | 366 | disk_partinfo(1, &pinfo); |
365 | printf("Partition 1: 0x%02x %ld sectors", | 367 | printf("Partition 1: 0x%02x %ld sectors", |
366 | pinfo->type, pinfo->size); | 368 | pinfo.type, pinfo.size); |
367 | 369 | ||
368 | if (button_was_held || (btn==BUTTON_MENU)) { | 370 | if (button_was_held || (btn==BUTTON_MENU)) { |
369 | /* If either the hold switch was on, or the Menu button was held, then | 371 | /* If either the hold switch was on, or the Menu button was held, then |
diff --git a/bootloader/ipodnano2g.c b/bootloader/ipodnano2g.c index cdd866f71e..d0a8e0206c 100644 --- a/bootloader/ipodnano2g.c +++ b/bootloader/ipodnano2g.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include "lcd.h" | 32 | #include "lcd.h" |
33 | #include "i2c-s5l8700.h" | 33 | #include "i2c-s5l8700.h" |
34 | #include "../kernel-internal.h" | 34 | #include "../kernel-internal.h" |
35 | #include "file_internal.h" | ||
35 | #include "storage.h" | 36 | #include "storage.h" |
36 | #include "fat.h" | 37 | #include "fat.h" |
37 | #include "disk.h" | 38 | #include "disk.h" |
@@ -213,7 +214,8 @@ void main(void) | |||
213 | fatal_error(); | 214 | fatal_error(); |
214 | } | 215 | } |
215 | 216 | ||
216 | disk_init(); | 217 | filesystem_init(); |
218 | |||
217 | rc = disk_mount_all(); | 219 | rc = disk_mount_all(); |
218 | if (rc<=0) | 220 | if (rc<=0) |
219 | { | 221 | { |
diff --git a/bootloader/iriver_h1x0.c b/bootloader/iriver_h1x0.c index 7f236d4510..742d0dd254 100644 --- a/bootloader/iriver_h1x0.c +++ b/bootloader/iriver_h1x0.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "scroll_engine.h" | 31 | #include "scroll_engine.h" |
32 | #include "../kernel-internal.h" | 32 | #include "../kernel-internal.h" |
33 | #include "storage.h" | 33 | #include "storage.h" |
34 | #include "file_internal.h" | ||
34 | #include "usb.h" | 35 | #include "usb.h" |
35 | #include "disk.h" | 36 | #include "disk.h" |
36 | #include "font.h" | 37 | #include "font.h" |
@@ -596,7 +597,7 @@ void main(void) | |||
596 | } | 597 | } |
597 | 598 | ||
598 | 599 | ||
599 | disk_init(); | 600 | filesystem_init(); |
600 | 601 | ||
601 | rc = disk_mount_all(); | 602 | rc = disk_mount_all(); |
602 | if (rc<=0) | 603 | if (rc<=0) |
diff --git a/bootloader/iriver_h300.c b/bootloader/iriver_h300.c index 362eb947c8..100e660a67 100644 --- a/bootloader/iriver_h300.c +++ b/bootloader/iriver_h300.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "scroll_engine.h" | 31 | #include "scroll_engine.h" |
32 | #include "../kernel-internal.h" | 32 | #include "../kernel-internal.h" |
33 | #include "storage.h" | 33 | #include "storage.h" |
34 | #include "file_internal.h" | ||
34 | #include "usb.h" | 35 | #include "usb.h" |
35 | #include "disk.h" | 36 | #include "disk.h" |
36 | #include "font.h" | 37 | #include "font.h" |
@@ -349,8 +350,7 @@ void main(void) | |||
349 | while(!(button_get(true) & BUTTON_REL)); | 350 | while(!(button_get(true) & BUTTON_REL)); |
350 | } | 351 | } |
351 | 352 | ||
352 | 353 | filesystem_init(); | |
353 | disk_init(); | ||
354 | 354 | ||
355 | rc = disk_mount_all(); | 355 | rc = disk_mount_all(); |
356 | if (rc<=0) | 356 | if (rc<=0) |
diff --git a/bootloader/main-e200r-installer.c b/bootloader/main-e200r-installer.c index 490f1f04dd..c5751d3095 100644 --- a/bootloader/main-e200r-installer.c +++ b/bootloader/main-e200r-installer.c | |||
@@ -26,12 +26,13 @@ | |||
26 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | #include "common.h" | 27 | #include "common.h" |
28 | #include "cpu.h" | 28 | #include "cpu.h" |
29 | #include "file.h" | ||
30 | #include "system.h" | 29 | #include "system.h" |
31 | #include "../kernel-internal.h" | 30 | #include "../kernel-internal.h" |
32 | #include "lcd.h" | 31 | #include "lcd.h" |
33 | #include "font.h" | 32 | #include "font.h" |
34 | #include "storage.h" | 33 | #include "storage.h" |
34 | #include "file_internal.h" | ||
35 | #include "file.h" | ||
35 | #include "button.h" | 36 | #include "button.h" |
36 | #include "disk.h" | 37 | #include "disk.h" |
37 | #include "crc32-mi4.h" | 38 | #include "crc32-mi4.h" |
@@ -92,7 +93,7 @@ void* main(void) | |||
92 | int num_partitions; | 93 | int num_partitions; |
93 | int crc32; | 94 | int crc32; |
94 | char sector[512]; | 95 | char sector[512]; |
95 | struct partinfo* pinfo; | 96 | struct partinfo pinfo; |
96 | 97 | ||
97 | system_init(); | 98 | system_init(); |
98 | kernel_init(); | 99 | kernel_init(); |
@@ -117,7 +118,7 @@ void* main(void) | |||
117 | printf(""); | 118 | printf(""); |
118 | 119 | ||
119 | i=storage_init(); | 120 | i=storage_init(); |
120 | disk_init(IF_MV(0)); | 121 | filesystem_init(); |
121 | num_partitions = disk_mount_all(); | 122 | num_partitions = disk_mount_all(); |
122 | 123 | ||
123 | if (num_partitions<=0) | 124 | if (num_partitions<=0) |
@@ -125,17 +126,17 @@ void* main(void) | |||
125 | error(EDISK, num_partitions, true); | 126 | error(EDISK, num_partitions, true); |
126 | } | 127 | } |
127 | 128 | ||
128 | pinfo = disk_partinfo(1); | 129 | disk_partinfo(1, &pinfo); |
129 | 130 | ||
130 | #if 0 /* not needed in release builds */ | 131 | #if 0 /* not needed in release builds */ |
131 | printf("--- Partition info ---"); | 132 | printf("--- Partition info ---"); |
132 | printf("start: %x", pinfo->start); | 133 | printf("start: %x", pinfo.start); |
133 | printf("size: %x", pinfo->size); | 134 | printf("size: %x", pinfo.size); |
134 | printf("type: %x", pinfo->type); | 135 | printf("type: %x", pinfo.type); |
135 | printf("reading: %x", (START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK)*512); | 136 | printf("reading: %x", (START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK)*512); |
136 | #endif | 137 | #endif |
137 | 138 | ||
138 | storage_read_sectors(pinfo->start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK, | 139 | storage_read_sectors(pinfo.start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK, |
139 | 1 , sector); | 140 | 1 , sector); |
140 | crc32 = chksum_crc32 (sector, 512); | 141 | crc32 = chksum_crc32 (sector, 512); |
141 | 142 | ||
@@ -157,7 +158,7 @@ void* main(void) | |||
157 | memcpy(§or[HACK_OFFSET], changedBytes, | 158 | memcpy(§or[HACK_OFFSET], changedBytes, |
158 | sizeof(changedBytes)/sizeof(*changedBytes)); | 159 | sizeof(changedBytes)/sizeof(*changedBytes)); |
159 | storage_write_sectors( | 160 | storage_write_sectors( |
160 | pinfo->start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK, | 161 | pinfo.start + START_SECTOR_OF_ROM + ROMSECTOR_TO_HACK, |
161 | 1 , sector); | 162 | 1 , sector); |
162 | printf("Firmware unlocked"); | 163 | printf("Firmware unlocked"); |
163 | printf("Proceed to Step 2"); | 164 | printf("Proceed to Step 2"); |
diff --git a/bootloader/main-pp.c b/bootloader/main-pp.c index 38760a0e58..562cf17ca4 100644 --- a/bootloader/main-pp.c +++ b/bootloader/main-pp.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "lcd.h" | 33 | #include "lcd.h" |
34 | #include "font.h" | 34 | #include "font.h" |
35 | #include "storage.h" | 35 | #include "storage.h" |
36 | #include "file_internal.h" | ||
36 | #include "adc.h" | 37 | #include "adc.h" |
37 | #include "button.h" | 38 | #include "button.h" |
38 | #include "disk.h" | 39 | #include "disk.h" |
@@ -291,7 +292,7 @@ void* main(void) | |||
291 | int btn; | 292 | int btn; |
292 | int rc; | 293 | int rc; |
293 | int num_partitions; | 294 | int num_partitions; |
294 | struct partinfo* pinfo; | 295 | struct partinfo pinfo; |
295 | #if !(CONFIG_STORAGE & STORAGE_SD) | 296 | #if !(CONFIG_STORAGE & STORAGE_SD) |
296 | char buf[256]; | 297 | char buf[256]; |
297 | unsigned short* identify_info; | 298 | unsigned short* identify_info; |
@@ -370,7 +371,7 @@ void* main(void) | |||
370 | } | 371 | } |
371 | #endif | 372 | #endif |
372 | 373 | ||
373 | disk_init(IF_MV(0)); | 374 | filesystem_init(); |
374 | num_partitions = disk_mount_all(); | 375 | num_partitions = disk_mount_all(); |
375 | if (num_partitions<=0) | 376 | if (num_partitions<=0) |
376 | { | 377 | { |
@@ -381,9 +382,9 @@ void* main(void) | |||
381 | that have more than that */ | 382 | that have more than that */ |
382 | for(i=0; i<NUM_PARTITIONS; i++) | 383 | for(i=0; i<NUM_PARTITIONS; i++) |
383 | { | 384 | { |
384 | pinfo = disk_partinfo(i); | 385 | disk_partinfo(i, &pinfo); |
385 | printf("Partition %d: 0x%02x %ld MB", | 386 | printf("Partition %d: 0x%02x %ld MB", |
386 | i, pinfo->type, pinfo->size / 2048); | 387 | i, pinfo.type, pinfo.size / 2048); |
387 | } | 388 | } |
388 | 389 | ||
389 | /* Now that storage is initialized, check for USB connection */ | 390 | /* Now that storage is initialized, check for USB connection */ |
@@ -430,10 +431,10 @@ void* main(void) | |||
430 | #if (CONFIG_STORAGE & STORAGE_SD) | 431 | #if (CONFIG_STORAGE & STORAGE_SD) |
431 | /* First try a (hidden) firmware partition */ | 432 | /* First try a (hidden) firmware partition */ |
432 | printf("Trying firmware partition"); | 433 | printf("Trying firmware partition"); |
433 | pinfo = disk_partinfo(1); | 434 | disk_partinfo(1, &pinfo); |
434 | if(pinfo->type == PARTITION_TYPE_OS2_HIDDEN_C_DRIVE) | 435 | if(pinfo.type == PARTITION_TYPE_OS2_HIDDEN_C_DRIVE) |
435 | { | 436 | { |
436 | rc = load_mi4_part(loadbuffer, pinfo, MAX_LOADSIZE, | 437 | rc = load_mi4_part(loadbuffer, &pinfo, MAX_LOADSIZE, |
437 | usb == USB_INSERTED); | 438 | usb == USB_INSERTED); |
438 | if (rc <= EFILE_EMPTY) { | 439 | if (rc <= EFILE_EMPTY) { |
439 | printf("Can't load from partition"); | 440 | printf("Can't load from partition"); |
diff --git a/bootloader/mini2440.c b/bootloader/mini2440.c index b3d73d1270..4088065550 100644 --- a/bootloader/mini2440.c +++ b/bootloader/mini2440.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "lcd.h" | 29 | #include "lcd.h" |
30 | #include "../kernel-internal.h" | 30 | #include "../kernel-internal.h" |
31 | #include "storage.h" | 31 | #include "storage.h" |
32 | #include "file_internal.h" | ||
32 | #include "fat.h" | 33 | #include "fat.h" |
33 | #include "disk.h" | 34 | #include "disk.h" |
34 | #include "font.h" | 35 | #include "font.h" |
@@ -88,7 +89,7 @@ int main(void) | |||
88 | error(EATA, rc, true); | 89 | error(EATA, rc, true); |
89 | } | 90 | } |
90 | 91 | ||
91 | disk_init(IF_MD(0)); | 92 | filesystem_init(); |
92 | rc = disk_mount_all(); | 93 | rc = disk_mount_all(); |
93 | if (rc<=0) | 94 | if (rc<=0) |
94 | { | 95 | { |
diff --git a/bootloader/mpio_hd200_hd300.c b/bootloader/mpio_hd200_hd300.c index a4e13e1f88..e66732f848 100644 --- a/bootloader/mpio_hd200_hd300.c +++ b/bootloader/mpio_hd200_hd300.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "lcd.h" | 29 | #include "lcd.h" |
30 | #include "../kernel-internal.h" | 30 | #include "../kernel-internal.h" |
31 | #include "storage.h" | 31 | #include "storage.h" |
32 | #include "file_internal.h" | ||
32 | #include "usb.h" | 33 | #include "usb.h" |
33 | #include "disk.h" | 34 | #include "disk.h" |
34 | #include "font.h" | 35 | #include "font.h" |
@@ -202,7 +203,7 @@ static void rb_boot(void) | |||
202 | if(rc) | 203 | if(rc) |
203 | error(EATA, rc, true); | 204 | error(EATA, rc, true); |
204 | 205 | ||
205 | disk_init(); | 206 | filesystem_init(); |
206 | 207 | ||
207 | rc = disk_mount_all(); | 208 | rc = disk_mount_all(); |
208 | if (rc <= 0) | 209 | if (rc <= 0) |
diff --git a/bootloader/mrobe500.c b/bootloader/mrobe500.c index 0b9ffb6281..08ba6f2acd 100644 --- a/bootloader/mrobe500.c +++ b/bootloader/mrobe500.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "lcd.h" | 26 | #include "lcd.h" |
27 | #include "../kernel-internal.h" | 27 | #include "../kernel-internal.h" |
28 | #include "storage.h" | 28 | #include "storage.h" |
29 | #include "file_internal.h" | ||
29 | #include "fat.h" | 30 | #include "fat.h" |
30 | #include "disk.h" | 31 | #include "disk.h" |
31 | #include "font.h" | 32 | #include "font.h" |
@@ -123,8 +124,8 @@ void main(void) | |||
123 | error(EATA, rc, true); | 124 | error(EATA, rc, true); |
124 | } | 125 | } |
125 | 126 | ||
126 | printf("disk"); | 127 | printf("filesystem"); |
127 | disk_init(); | 128 | filesystem_init(); |
128 | 129 | ||
129 | printf("mount"); | 130 | printf("mount"); |
130 | rc = disk_mount_all(); | 131 | rc = disk_mount_all(); |
diff --git a/bootloader/ondavx747.c b/bootloader/ondavx747.c index 3d03c36eae..0f1c504553 100644 --- a/bootloader/ondavx747.c +++ b/bootloader/ondavx747.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "rb-loader.h" | 33 | #include "rb-loader.h" |
34 | #include "loader_strerror.h" | 34 | #include "loader_strerror.h" |
35 | #include "storage.h" | 35 | #include "storage.h" |
36 | #include "file_internal.h" | ||
36 | #include "disk.h" | 37 | #include "disk.h" |
37 | #include "string.h" | 38 | #include "string.h" |
38 | #include "adc.h" | 39 | #include "adc.h" |
@@ -269,6 +270,8 @@ int main(void) | |||
269 | 270 | ||
270 | show_logo(); | 271 | show_logo(); |
271 | 272 | ||
273 | filesystem_init(); | ||
274 | |||
272 | rc = storage_init(); | 275 | rc = storage_init(); |
273 | if(rc) | 276 | if(rc) |
274 | error(EATA, rc, true); | 277 | error(EATA, rc, true); |
diff --git a/bootloader/rk27xx.c b/bootloader/rk27xx.c index d190ea5a5a..57e5868a87 100644 --- a/bootloader/rk27xx.c +++ b/bootloader/rk27xx.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "button.h" | 12 | #include "button.h" |
13 | #include "common.h" | 13 | #include "common.h" |
14 | #include "storage.h" | 14 | #include "storage.h" |
15 | #include "file_internal.h" | ||
15 | #include "disk.h" | 16 | #include "disk.h" |
16 | #include "panic.h" | 17 | #include "panic.h" |
17 | #include "power.h" | 18 | #include "power.h" |
@@ -146,8 +147,7 @@ void main(void) | |||
146 | if(ret < 0) | 147 | if(ret < 0) |
147 | error(EATA, ret, true); | 148 | error(EATA, ret, true); |
148 | 149 | ||
149 | while(!disk_init(IF_MV(0))) | 150 | filesystem_init(); |
150 | panicf("disk_init failed!"); | ||
151 | 151 | ||
152 | while((ret = disk_mount_all()) <= 0) | 152 | while((ret = disk_mount_all()) <= 0) |
153 | error(EDISK, ret, true); | 153 | error(EDISK, ret, true); |
diff --git a/bootloader/sansa_as3525.c b/bootloader/sansa_as3525.c index 8592f2450f..01fb8459b4 100644 --- a/bootloader/sansa_as3525.c +++ b/bootloader/sansa_as3525.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include "rb-loader.h" | 39 | #include "rb-loader.h" |
40 | #include "loader_strerror.h" | 40 | #include "loader_strerror.h" |
41 | #include "storage.h" | 41 | #include "storage.h" |
42 | #include "file_internal.h" | ||
42 | #include "disk.h" | 43 | #include "disk.h" |
43 | #include "panic.h" | 44 | #include "panic.h" |
44 | #include "power.h" | 45 | #include "power.h" |
@@ -119,6 +120,8 @@ void main(void) | |||
119 | if(ret < 0) | 120 | if(ret < 0) |
120 | error(EATA, ret, true); | 121 | error(EATA, ret, true); |
121 | 122 | ||
123 | filesystem_init(); | ||
124 | |||
122 | #ifdef USE_ROCKBOX_USB | 125 | #ifdef USE_ROCKBOX_USB |
123 | usb_init(); | 126 | usb_init(); |
124 | usb_start_monitoring(); | 127 | usb_start_monitoring(); |
@@ -128,13 +131,6 @@ void main(void) | |||
128 | usb_mode(); | 131 | usb_mode(); |
129 | #endif /* USE_ROCKBOX_USB */ | 132 | #endif /* USE_ROCKBOX_USB */ |
130 | 133 | ||
131 | while(!disk_init(IF_MV(0))) | ||
132 | #ifdef USE_ROCKBOX_USB | ||
133 | usb_mode(); | ||
134 | #else | ||
135 | panicf("disk_init failed!"); | ||
136 | #endif | ||
137 | |||
138 | while((ret = disk_mount_all()) <= 0) | 134 | while((ret = disk_mount_all()) <= 0) |
139 | { | 135 | { |
140 | #ifdef USE_ROCKBOX_USB | 136 | #ifdef USE_ROCKBOX_USB |
diff --git a/bootloader/sansaconnect.c b/bootloader/sansaconnect.c index 48617ec337..5bd59b3ac4 100644 --- a/bootloader/sansaconnect.c +++ b/bootloader/sansaconnect.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "lcd.h" | 21 | #include "lcd.h" |
22 | #include "../kernel-internal.h" | 22 | #include "../kernel-internal.h" |
23 | #include "storage.h" | 23 | #include "storage.h" |
24 | #include "file_internal.h" | ||
24 | #include "disk.h" | 25 | #include "disk.h" |
25 | #include "font.h" | 26 | #include "font.h" |
26 | #include "backlight.h" | 27 | #include "backlight.h" |
@@ -72,7 +73,7 @@ void main(void) | |||
72 | if(ret) | 73 | if(ret) |
73 | printf("SD error: %d", ret); | 74 | printf("SD error: %d", ret); |
74 | 75 | ||
75 | disk_init(IF_MD(0)); | 76 | filesystem_init(); |
76 | 77 | ||
77 | ret = disk_mount_all(); | 78 | ret = disk_mount_all(); |
78 | if (ret <= 0) | 79 | if (ret <= 0) |
diff --git a/bootloader/telechips.c b/bootloader/telechips.c index 17ba509a6c..d2cf10eb21 100644 --- a/bootloader/telechips.c +++ b/bootloader/telechips.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "lcd.h" | 30 | #include "lcd.h" |
31 | #include "../kernel-internal.h" | 31 | #include "../kernel-internal.h" |
32 | #include "storage.h" | 32 | #include "storage.h" |
33 | #include "file_internal.h" | ||
33 | #include "fat.h" | 34 | #include "fat.h" |
34 | #include "disk.h" | 35 | #include "disk.h" |
35 | #include "font.h" | 36 | #include "font.h" |
@@ -162,6 +163,8 @@ void* main(void) | |||
162 | error(EATA, rc, true); | 163 | error(EATA, rc, true); |
163 | } | 164 | } |
164 | 165 | ||
166 | filesystem_init(); | ||
167 | |||
165 | printf("mount"); | 168 | printf("mount"); |
166 | rc = disk_mount_all(); | 169 | rc = disk_mount_all(); |
167 | if (rc<=0) | 170 | if (rc<=0) |
diff --git a/bootloader/tpj1022.c b/bootloader/tpj1022.c index 159dcc63cd..30adb6ba4e 100644 --- a/bootloader/tpj1022.c +++ b/bootloader/tpj1022.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "kernel.h" | 31 | #include "kernel.h" |
32 | #include "thread.h" | 32 | #include "thread.h" |
33 | #include "storage.h" | 33 | #include "storage.h" |
34 | #include "file_internal.h" | ||
34 | #include "fat.h" | 35 | #include "fat.h" |
35 | #include "disk.h" | 36 | #include "disk.h" |
36 | #include "font.h" | 37 | #include "font.h" |
@@ -56,7 +57,7 @@ void* main(void) | |||
56 | 57 | ||
57 | i=storage_init(); | 58 | i=storage_init(); |
58 | 59 | ||
59 | disk_init(); | 60 | filesystem_init(); |
60 | rc = disk_mount_all(); | 61 | rc = disk_mount_all(); |
61 | 62 | ||
62 | #if 0 | 63 | #if 0 |
diff --git a/firmware/SOURCES b/firmware/SOURCES index a68c4921ba..070f0d4a62 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -59,17 +59,27 @@ target/hosted/sdl/lcd-remote-bitmap.c | |||
59 | target/hosted/sdl/lcd-sdl.c | 59 | target/hosted/sdl/lcd-sdl.c |
60 | target/hosted/sdl/system-sdl.c | 60 | target/hosted/sdl/system-sdl.c |
61 | #ifdef HAVE_SDL_THREADS | 61 | #ifdef HAVE_SDL_THREADS |
62 | target/hosted/sdl/thread-sdl.c | 62 | target/hosted/sdl/filesystem-sdl.c |
63 | #else | ||
64 | #endif | 63 | #endif |
64 | target/hosted/sdl/load_code-sdl.c | ||
65 | target/hosted/sdl/timer-sdl.c | 65 | target/hosted/sdl/timer-sdl.c |
66 | #ifdef HAVE_TOUCHSCREEN | 66 | #ifdef HAVE_TOUCHSCREEN |
67 | target/hosted/sdl/key_to_touch-sdl.c | 67 | target/hosted/sdl/key_to_touch-sdl.c |
68 | #endif | 68 | #endif |
69 | #ifdef APPLICATION | 69 | #ifdef APPLICATION |
70 | target/hosted/sdl/app/load_code-sdl-app.c | ||
70 | target/hosted/sdl/app/button-application.c | 71 | target/hosted/sdl/app/button-application.c |
71 | #endif | 72 | #ifdef WIN32 |
72 | #endif | 73 | target/hosted/filesystem-win32.c |
74 | #else /* !WIN32 */ | ||
75 | target/hosted/filesystem-unix.c | ||
76 | #endif /* WIN32 */ | ||
77 | #endif /* APPLICATION */ | ||
78 | #endif /* HAVE_SDL */ | ||
79 | |||
80 | #ifdef APPLICATION | ||
81 | target/hosted/filesystem-app.c | ||
82 | #endif /* APPLICATION */ | ||
73 | 83 | ||
74 | #if defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) | 84 | #if defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1) |
75 | target/hosted/kernel-unix.c | 85 | target/hosted/kernel-unix.c |
@@ -163,19 +173,19 @@ common/crc32-mi4.c | |||
163 | common/crc32-rkw.c | 173 | common/crc32-rkw.c |
164 | #endif | 174 | #endif |
165 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 175 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
166 | common/dir_uncached.c | 176 | common/dir.c |
177 | common/disk_cache.c | ||
167 | common/file.c | 178 | common/file.c |
179 | common/file_internal.c | ||
168 | common/disk.c | 180 | common/disk.c |
181 | common/fileobj_mgr.c | ||
169 | #endif /* PLATFORM_NATIVE */ | 182 | #endif /* PLATFORM_NATIVE */ |
170 | #ifdef HAVE_DIRCACHE | 183 | #ifdef HAVE_DIRCACHE |
171 | common/dircache.c | 184 | common/dircache.c |
172 | #endif /* HAVE_DIRCACHE */ | 185 | #endif /* HAVE_DIRCACHE */ |
173 | common/filefuncs.c | 186 | common/pathfuncs.c |
174 | common/format.c | 187 | common/format.c |
175 | common/linked_list.c | 188 | common/linked_list.c |
176 | #ifdef APPLICATION | ||
177 | common/rbpaths.c | ||
178 | #endif | ||
179 | common/strcasecmp.c | 189 | common/strcasecmp.c |
180 | common/strcasestr.c | 190 | common/strcasestr.c |
181 | common/strnatcmp.c | 191 | common/strnatcmp.c |
@@ -1816,7 +1826,20 @@ target/hosted/android/app/button-application.c | |||
1816 | drivers/audio/android.c | 1826 | drivers/audio/android.c |
1817 | #endif | 1827 | #endif |
1818 | 1828 | ||
1819 | #endif /* defined(SIMULATOR) */ | 1829 | #else /* defined(SIMULATOR) */ |
1830 | |||
1831 | #ifdef WIN32 | ||
1832 | asm/mempcpy.c | ||
1833 | target/hosted/filesystem-win32.c | ||
1834 | #else /* !WIN32 */ | ||
1835 | target/hosted/filesystem-unix.c | ||
1836 | #endif /* WIN32 */ | ||
1837 | target/hosted/sdl/load_code-sdl.c | ||
1838 | #ifdef HAVE_SDL_THREADS | ||
1839 | target/hosted/sdl/filesystem-sdl.c | ||
1840 | #endif | ||
1841 | |||
1842 | #endif /* !defined(SIMULATOR) */ | ||
1820 | 1843 | ||
1821 | #if defined(HAVE_TOUCHPAD) && !defined(HAS_BUTTON_HOLD) | 1844 | #if defined(HAVE_TOUCHPAD) && !defined(HAS_BUTTON_HOLD) |
1822 | drivers/touchpad.c | 1845 | drivers/touchpad.c |
@@ -1826,9 +1849,7 @@ drivers/touchpad.c | |||
1826 | #ifdef HAVE_CORELOCK_OBJECT | 1849 | #ifdef HAVE_CORELOCK_OBJECT |
1827 | kernel/corelock.c | 1850 | kernel/corelock.c |
1828 | #endif | 1851 | #endif |
1829 | #if 0 /* pending dependent code */ | ||
1830 | kernel/mrsw_lock.c | 1852 | kernel/mrsw_lock.c |
1831 | #endif | ||
1832 | kernel/mutex.c | 1853 | kernel/mutex.c |
1833 | kernel/queue.c | 1854 | kernel/queue.c |
1834 | #ifdef HAVE_SEMAPHORE_OBJECTS | 1855 | #ifdef HAVE_SEMAPHORE_OBJECTS |
diff --git a/firmware/asm/mips/memcpy.S b/firmware/asm/mips/memcpy.S index 2e7f245c69..edbf5ac5eb 100644 --- a/firmware/asm/mips/memcpy.S +++ b/firmware/asm/mips/memcpy.S | |||
@@ -63,13 +63,13 @@ memcpy: | |||
63 | SWHI t0, 0(a0) | 63 | SWHI t0, 0(a0) |
64 | addu a0, t1 | 64 | addu a0, t1 |
65 | 65 | ||
66 | chk8w: | 66 | chk8w: |
67 | andi t0, a2, 0x1f # 32 or more bytes left? | 67 | andi t0, a2, 0x1f # 32 or more bytes left? |
68 | beq t0, a2, chk1w | 68 | beq t0, a2, chk1w |
69 | subu a3, a2, t0 # Yes | 69 | subu a3, a2, t0 # Yes |
70 | addu a3, a1 # a3 = end address of loop | 70 | addu a3, a1 # a3 = end address of loop |
71 | move a2, t0 # a2 = what will be left after loop | 71 | move a2, t0 # a2 = what will be left after loop |
72 | lop8w: | 72 | lop8w: |
73 | lw t0, 0(a1) # Loop taking 8 words at a time | 73 | lw t0, 0(a1) # Loop taking 8 words at a time |
74 | lw t1, 4(a1) | 74 | lw t1, 4(a1) |
75 | lw t2, 8(a1) | 75 | lw t2, 8(a1) |
@@ -90,34 +90,34 @@ lop8w: | |||
90 | bne a1, a3, lop8w | 90 | bne a1, a3, lop8w |
91 | sw t7, -4(a0) | 91 | sw t7, -4(a0) |
92 | 92 | ||
93 | chk1w: | 93 | chk1w: |
94 | andi t0, a2, 0x3 # 4 or more bytes left? | 94 | andi t0, a2, 0x3 # 4 or more bytes left? |
95 | beq t0, a2, last8 | 95 | beq t0, a2, last8 |
96 | subu a3, a2, t0 # Yes, handle them one word at a time | 96 | subu a3, a2, t0 # Yes, handle them one word at a time |
97 | addu a3, a1 # a3 again end address | 97 | addu a3, a1 # a3 again end address |
98 | move a2, t0 | 98 | move a2, t0 |
99 | lop1w: | 99 | lop1w: |
100 | lw t0, 0(a1) | 100 | lw t0, 0(a1) |
101 | addiu a0, 4 | 101 | addiu a0, 4 |
102 | addiu a1, 4 | 102 | addiu a1, 4 |
103 | bne a1, a3, lop1w | 103 | bne a1, a3, lop1w |
104 | sw t0, -4(a0) | 104 | sw t0, -4(a0) |
105 | 105 | ||
106 | last8: | 106 | last8: |
107 | blez a2, lst8e # Handle last 8 bytes, one at a time | 107 | blez a2, lst8e # Handle last 8 bytes, one at a time |
108 | addu a3, a2, a1 | 108 | addu a3, a2, a1 |
109 | lst8l: | 109 | lst8l: |
110 | lb t0, 0(a1) | 110 | lb t0, 0(a1) |
111 | addiu a0, 1 | 111 | addiu a0, 1 |
112 | addiu a1, 1 | 112 | addiu a1, 1 |
113 | bne a1, a3, lst8l | 113 | bne a1, a3, lst8l |
114 | sb t0, -1(a0) | 114 | sb t0, -1(a0) |
115 | lst8e: | 115 | lst8e: |
116 | jr ra # Bye, bye | 116 | jr ra # Bye, bye |
117 | nop | 117 | nop |
118 | 118 | ||
119 | shift: | 119 | shift: |
120 | subu a3, zero, a0 # Src and Dest unaligned | 120 | subu a3, zero, a0 # Src and Dest unaligned |
121 | andi a3, 0x3 # (unoptimized case...) | 121 | andi a3, 0x3 # (unoptimized case...) |
122 | beq a3, zero, shft1 | 122 | beq a3, zero, shft1 |
123 | subu a2, a3 # a2 = bytes left | 123 | subu a2, a3 # a2 = bytes left |
@@ -126,11 +126,11 @@ shift: | |||
126 | addu a1, a3 | 126 | addu a1, a3 |
127 | SWHI t0, 0(a0) | 127 | SWHI t0, 0(a0) |
128 | addu a0, a3 | 128 | addu a0, a3 |
129 | shft1: | 129 | shft1: |
130 | andi t0, a2, 0x3 | 130 | andi t0, a2, 0x3 |
131 | subu a3, a2, t0 | 131 | subu a3, a2, t0 |
132 | addu a3, a1 | 132 | addu a3, a1 |
133 | shfth: | 133 | shfth: |
134 | LWHI t1, 0(a1) # Limp through, word by word | 134 | LWHI t1, 0(a1) # Limp through, word by word |
135 | LWLO t1, 3(a1) | 135 | LWLO t1, 3(a1) |
136 | addiu a0, 4 | 136 | addiu a0, 4 |
diff --git a/firmware/asm/sh/memcpy.S b/firmware/asm/sh/memcpy.S index e23a579b05..59c5801ac0 100644 --- a/firmware/asm/sh/memcpy.S +++ b/firmware/asm/sh/memcpy.S | |||
@@ -60,13 +60,13 @@ ___memcpy_fwd_entry: | |||
60 | cmp/hs r0,r6 /* at least 11 bytes to copy? (ensures 2 aligned longs) */ | 60 | cmp/hs r0,r6 /* at least 11 bytes to copy? (ensures 2 aligned longs) */ |
61 | add r5,r6 /* r6 = source_end */ | 61 | add r5,r6 /* r6 = source_end */ |
62 | bf .start_b2 /* no: jump directly to byte loop */ | 62 | bf .start_b2 /* no: jump directly to byte loop */ |
63 | 63 | ||
64 | mov #3,r0 | 64 | mov #3,r0 |
65 | neg r5,r3 | 65 | neg r5,r3 |
66 | and r0,r3 /* r3 = (4 - align_offset) % 4 */ | 66 | and r0,r3 /* r3 = (4 - align_offset) % 4 */ |
67 | tst r3,r3 /* already aligned? */ | 67 | tst r3,r3 /* already aligned? */ |
68 | bt .end_b1 /* yes: skip leading byte loop */ | 68 | bt .end_b1 /* yes: skip leading byte loop */ |
69 | 69 | ||
70 | add r5,r3 /* r3 = first source long bound */ | 70 | add r5,r3 /* r3 = first source long bound */ |
71 | 71 | ||
72 | /* leading byte loop: copies 0..3 bytes */ | 72 | /* leading byte loop: copies 0..3 bytes */ |
@@ -89,7 +89,7 @@ ___memcpy_fwd_entry: | |||
89 | mov r6,r3 /* move end address to r3 */ | 89 | mov r6,r3 /* move end address to r3 */ |
90 | jmp @r1 /* and jump to it */ | 90 | jmp @r1 /* and jump to it */ |
91 | add #-7,r3 /* adjust end addr for main loops doing 2 longs/pass */ | 91 | add #-7,r3 /* adjust end addr for main loops doing 2 longs/pass */ |
92 | 92 | ||
93 | /** main loops, copying 2 longs per pass to profit from fast page mode **/ | 93 | /** main loops, copying 2 longs per pass to profit from fast page mode **/ |
94 | 94 | ||
95 | /* long aligned destination (fastest) */ | 95 | /* long aligned destination (fastest) */ |
@@ -102,11 +102,11 @@ ___memcpy_fwd_entry: | |||
102 | mov.l r0,@-r4 /* store second long */ | 102 | mov.l r0,@-r4 /* store second long */ |
103 | mov.l r1,@-r4 /* store first long; NOT ALIGNED - no speed loss here! */ | 103 | mov.l r1,@-r4 /* store first long; NOT ALIGNED - no speed loss here! */ |
104 | bt .loop_do0 | 104 | bt .loop_do0 |
105 | 105 | ||
106 | add #4,r3 /* readjust end address */ | 106 | add #4,r3 /* readjust end address */ |
107 | cmp/hi r5,r3 /* one long left? */ | 107 | cmp/hi r5,r3 /* one long left? */ |
108 | bf .start_b2 /* no, jump to trailing byte loop */ | 108 | bf .start_b2 /* no, jump to trailing byte loop */ |
109 | 109 | ||
110 | mov.l @r5+,r0 /* load last long & increment source addr */ | 110 | mov.l @r5+,r0 /* load last long & increment source addr */ |
111 | add #4,r4 /* increment dest addr */ | 111 | add #4,r4 /* increment dest addr */ |
112 | bra .start_b2 /* jump to trailing byte loop */ | 112 | bra .start_b2 /* jump to trailing byte loop */ |
diff --git a/firmware/common/dir.c b/firmware/common/dir.c new file mode 100644 index 0000000000..da798c71d5 --- /dev/null +++ b/firmware/common/dir.c | |||
@@ -0,0 +1,413 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * Copyright (C) 2014 by Michael Sevakis | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #define DIRFUNCTIONS_DEFINED | ||
23 | #include "config.h" | ||
24 | #include <errno.h> | ||
25 | #include <string.h> | ||
26 | #include "debug.h" | ||
27 | #include "dir.h" | ||
28 | #include "pathfuncs.h" | ||
29 | #include "fileobj_mgr.h" | ||
30 | #include "dircache_redirect.h" | ||
31 | |||
32 | /* structure used for open directory streams */ | ||
33 | static struct dirstr_desc | ||
34 | { | ||
35 | struct filestr_base stream; /* basic stream info (first!) */ | ||
36 | struct dirscan_info scan; /* directory scan cursor */ | ||
37 | struct dirent entry; /* current parsed entry information */ | ||
38 | #ifdef HAVE_MULTIVOLUME | ||
39 | int volumecounter; /* counter for root volume entries */ | ||
40 | #endif | ||
41 | } open_streams[MAX_OPEN_DIRS]; | ||
42 | |||
43 | /* check and return a struct dirstr_desc* from a DIR* */ | ||
44 | static struct dirstr_desc * get_dirstr(DIR *dirp) | ||
45 | { | ||
46 | struct dirstr_desc *dir = (struct dirstr_desc *)dirp; | ||
47 | |||
48 | if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) | ||
49 | dir = NULL; | ||
50 | else if (dir->stream.flags & FDO_BUSY) | ||
51 | return dir; | ||
52 | |||
53 | int errnum; | ||
54 | |||
55 | if (!dir) | ||
56 | { | ||
57 | errnum = EFAULT; | ||
58 | } | ||
59 | else if (dir->stream.flags == FV_NONEXIST) | ||
60 | { | ||
61 | DEBUGF("dir #%d: nonexistant device\n", (int)(dir - open_streams)); | ||
62 | errnum = ENXIO; | ||
63 | } | ||
64 | else | ||
65 | { | ||
66 | DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams)); | ||
67 | errnum = EBADF; | ||
68 | } | ||
69 | |||
70 | errno = errnum; | ||
71 | return NULL; | ||
72 | } | ||
73 | |||
74 | #define GET_DIRSTR(type, dirp) \ | ||
75 | ({ \ | ||
76 | file_internal_lock_##type(); \ | ||
77 | struct dirstr_desc *_dir = get_dirstr(dirp); \ | ||
78 | if (_dir) \ | ||
79 | FILESTR_LOCK(type, &_dir->stream); \ | ||
80 | else \ | ||
81 | file_internal_unlock_##type(); \ | ||
82 | _dir; \ | ||
83 | }) | ||
84 | |||
85 | /* release the lock on the dirstr_desc* */ | ||
86 | #define RELEASE_DIRSTR(type, dir) \ | ||
87 | ({ \ | ||
88 | FILESTR_UNLOCK(type, &(dir)->stream); \ | ||
89 | file_internal_unlock_##type(); \ | ||
90 | }) | ||
91 | |||
92 | |||
93 | /* find a free dir stream descriptor */ | ||
94 | static struct dirstr_desc * alloc_dirstr(void) | ||
95 | { | ||
96 | for (unsigned int dd = 0; dd < MAX_OPEN_DIRS; dd++) | ||
97 | { | ||
98 | struct dirstr_desc *dir = &open_streams[dd]; | ||
99 | if (!dir->stream.flags) | ||
100 | return dir; | ||
101 | } | ||
102 | |||
103 | DEBUGF("Too many dirs open\n"); | ||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | #ifdef HAVE_MULTIVOLUME | ||
108 | static int readdir_volume_inner(struct dirstr_desc *dir, struct dirent *entry) | ||
109 | { | ||
110 | /* Volumes (secondary file systems) get inserted into the system root | ||
111 | * directory. If the path specified volume 0, enumeration will not | ||
112 | * include other volumes, but just its own files and directories. | ||
113 | * | ||
114 | * Fake special directories, which don't really exist, that will get | ||
115 | * redirected upon opendir() | ||
116 | */ | ||
117 | while (++dir->volumecounter < NUM_VOLUMES) | ||
118 | { | ||
119 | /* on the system root */ | ||
120 | if (!fat_ismounted(dir->volumecounter)) | ||
121 | continue; | ||
122 | |||
123 | get_volume_name(dir->volumecounter, entry->d_name); | ||
124 | dir->entry.info.attr = ATTR_MOUNT_POINT; | ||
125 | dir->entry.info.size = 0; | ||
126 | dir->entry.info.wrtdate = 0; | ||
127 | dir->entry.info.wrttime = 0; | ||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | /* do normal directory entry fetching */ | ||
132 | return 0; | ||
133 | } | ||
134 | #endif /* HAVE_MULTIVOLUME */ | ||
135 | |||
136 | static inline int readdir_volume(struct dirstr_desc *dir, | ||
137 | struct dirent *entry) | ||
138 | { | ||
139 | #ifdef HAVE_MULTIVOLUME | ||
140 | /* fetch virtual volume entries? */ | ||
141 | if (dir->volumecounter < NUM_VOLUMES) | ||
142 | return readdir_volume_inner(dir, entry); | ||
143 | #endif /* HAVE_MULTIVOLUME */ | ||
144 | |||
145 | /* do normal directory entry fetching */ | ||
146 | return 0; | ||
147 | (void)dir; (void)entry; | ||
148 | } | ||
149 | |||
150 | |||
151 | /** POSIX interface **/ | ||
152 | |||
153 | /* open a directory */ | ||
154 | DIR * opendir(const char *dirname) | ||
155 | { | ||
156 | DEBUGF("opendir(dirname=\"%s\"\n", dirname); | ||
157 | |||
158 | DIR *dirp = NULL; | ||
159 | |||
160 | file_internal_lock_WRITER(); | ||
161 | |||
162 | int rc; | ||
163 | |||
164 | struct dirstr_desc * const dir = alloc_dirstr(); | ||
165 | if (!dir) | ||
166 | FILE_ERROR(EMFILE, RC); | ||
167 | |||
168 | rc = open_stream_internal(dirname, FF_DIR, &dir->stream, NULL); | ||
169 | if (rc < 0) | ||
170 | { | ||
171 | DEBUGF("Open failed: %d\n", rc); | ||
172 | FILE_ERROR(ERRNO, RC); | ||
173 | } | ||
174 | |||
175 | #ifdef HAVE_MULTIVOLUME | ||
176 | /* volume counter is relevant only to the system root */ | ||
177 | dir->volumecounter = rc > 1 ? 0 : INT_MAX; | ||
178 | #endif /* HAVE_MULTIVOLUME */ | ||
179 | |||
180 | fat_rewind(&dir->stream.fatstr); | ||
181 | rewinddir_dirent(&dir->scan); | ||
182 | |||
183 | dirp = (DIR *)dir; | ||
184 | file_error: | ||
185 | file_internal_unlock_WRITER(); | ||
186 | return dirp; | ||
187 | } | ||
188 | |||
189 | /* close a directory stream */ | ||
190 | int closedir(DIR *dirp) | ||
191 | { | ||
192 | int rc; | ||
193 | |||
194 | file_internal_lock_WRITER(); | ||
195 | |||
196 | /* needs to work even if marked "nonexistant" */ | ||
197 | struct dirstr_desc * const dir = (struct dirstr_desc *)dirp; | ||
198 | if (!PTR_IN_ARRAY(open_streams, dir, MAX_OPEN_DIRS)) | ||
199 | FILE_ERROR(EFAULT, -1); | ||
200 | |||
201 | if (!dir->stream.flags) | ||
202 | { | ||
203 | DEBUGF("dir #%d: dir not open\n", (int)(dir - open_streams)); | ||
204 | FILE_ERROR(EBADF, -2); | ||
205 | } | ||
206 | |||
207 | rc = close_stream_internal(&dir->stream); | ||
208 | if (rc < 0) | ||
209 | FILE_ERROR(ERRNO, rc * 10 - 3); | ||
210 | |||
211 | file_error: | ||
212 | file_internal_unlock_WRITER(); | ||
213 | return rc; | ||
214 | } | ||
215 | |||
216 | /* read a directory */ | ||
217 | struct dirent * readdir(DIR *dirp) | ||
218 | { | ||
219 | struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp); | ||
220 | if (!dir) | ||
221 | FILE_ERROR_RETURN(ERRNO, NULL); | ||
222 | |||
223 | struct dirent *res = NULL; | ||
224 | |||
225 | int rc = readdir_volume(dir, &dir->entry); | ||
226 | if (rc == 0) | ||
227 | { | ||
228 | rc = readdir_dirent(&dir->stream, &dir->scan, &dir->entry); | ||
229 | if (rc < 0) | ||
230 | FILE_ERROR(EIO, RC); | ||
231 | } | ||
232 | |||
233 | if (rc > 0) | ||
234 | res = &dir->entry; | ||
235 | |||
236 | file_error: | ||
237 | RELEASE_DIRSTR(READER, dir); | ||
238 | |||
239 | if (rc > 1) | ||
240 | iso_decode_d_name(res->d_name); | ||
241 | |||
242 | return res; | ||
243 | } | ||
244 | |||
245 | #if 0 /* not included now but probably should be */ | ||
246 | /* read a directory (reentrant) */ | ||
247 | int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) | ||
248 | { | ||
249 | if (!result) | ||
250 | FILE_ERROR_RETURN(EFAULT, -2); | ||
251 | |||
252 | *result = NULL; | ||
253 | |||
254 | if (!entry) | ||
255 | FILE_ERROR_RETURN(EFAULT, -3); | ||
256 | |||
257 | struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp); | ||
258 | if (!dir) | ||
259 | FILE_ERROR_RETURN(ERRNO, -1); | ||
260 | |||
261 | int rc = readdir_volume(dir, entry); | ||
262 | if (rc == 0) | ||
263 | { | ||
264 | rc = readdir_dirent(&dir->stream, &dir->scan, entry); | ||
265 | if (rc < 0) | ||
266 | FILE_ERROR(EIO, rc * 10 - 4); | ||
267 | } | ||
268 | |||
269 | file_error: | ||
270 | RELEASE_DIRSTR(READER, dir); | ||
271 | |||
272 | if (rc > 0) | ||
273 | { | ||
274 | if (rc > 1) | ||
275 | iso_decode_d_name(entry->d_name); | ||
276 | |||
277 | *result = entry; | ||
278 | rc = 0; | ||
279 | } | ||
280 | |||
281 | return rc; | ||
282 | } | ||
283 | |||
284 | /* reset the position of a directory stream to the beginning of a directory */ | ||
285 | void rewinddir(DIR *dirp) | ||
286 | { | ||
287 | struct dirstr_desc * const dir = GET_DIRSTR(READER, dirp); | ||
288 | if (!dir) | ||
289 | FILE_ERROR_RETURN(ERRNO); | ||
290 | |||
291 | rewinddir_dirent(&dir->scan); | ||
292 | |||
293 | #ifdef HAVE_MULTIVOLUME | ||
294 | if (dir->volumecounter != INT_MAX) | ||
295 | dir->volumecounter = 0; | ||
296 | #endif /* HAVE_MULTIVOLUME */ | ||
297 | |||
298 | RELEASE_DIRSTR(READER, dir); | ||
299 | } | ||
300 | |||
301 | #endif /* 0 */ | ||
302 | |||
303 | /* make a directory */ | ||
304 | int mkdir(const char *path) | ||
305 | { | ||
306 | DEBUGF("mkdir(path=\"%s\")\n", path); | ||
307 | |||
308 | int rc; | ||
309 | |||
310 | file_internal_lock_WRITER(); | ||
311 | |||
312 | struct filestr_base stream; | ||
313 | struct path_component_info compinfo; | ||
314 | rc = open_stream_internal(path, FF_DIR, &stream, &compinfo); | ||
315 | if (rc < 0) | ||
316 | { | ||
317 | DEBUGF("Can't open parent dir or path is not a directory\n"); | ||
318 | FILE_ERROR(ERRNO, rc * 10 - 1); | ||
319 | } | ||
320 | else if (rc > 0) | ||
321 | { | ||
322 | DEBUGF("File exists\n"); | ||
323 | FILE_ERROR(EEXIST, -2); | ||
324 | } | ||
325 | |||
326 | rc = create_stream_internal(&compinfo.parentinfo, compinfo.name, | ||
327 | compinfo.length, ATTR_NEW_DIRECTORY, | ||
328 | FO_DIRECTORY, &stream); | ||
329 | if (rc < 0) | ||
330 | FILE_ERROR(ERRNO, rc * 10 - 3); | ||
331 | |||
332 | rc = 0; | ||
333 | file_error: | ||
334 | close_stream_internal(&stream); | ||
335 | file_internal_unlock_WRITER(); | ||
336 | return rc; | ||
337 | } | ||
338 | |||
339 | /* remove a directory */ | ||
340 | int rmdir(const char *name) | ||
341 | { | ||
342 | DEBUGF("rmdir(name=\"%s\")\n", name); | ||
343 | |||
344 | if (name) | ||
345 | { | ||
346 | /* path may not end with "." */ | ||
347 | const char *basename; | ||
348 | size_t len = path_basename(name, &basename); | ||
349 | if (basename[0] == '.' && len == 1) | ||
350 | { | ||
351 | DEBUGF("Invalid path; last component is \".\"\n"); | ||
352 | FILE_ERROR_RETURN(EINVAL, -9); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | file_internal_lock_WRITER(); | ||
357 | int rc = remove_stream_internal(name, NULL, FF_DIR); | ||
358 | file_internal_unlock_WRITER(); | ||
359 | return rc; | ||
360 | } | ||
361 | |||
362 | |||
363 | /** Extended interface **/ | ||
364 | |||
365 | /* return if two directory streams refer to the same directory */ | ||
366 | int samedir(DIR *dirp1, DIR *dirp2) | ||
367 | { | ||
368 | struct dirstr_desc * const dir1 = GET_DIRSTR(WRITER, dirp1); | ||
369 | if (!dir1) | ||
370 | FILE_ERROR_RETURN(ERRNO, -1); | ||
371 | |||
372 | int rc = -2; | ||
373 | |||
374 | struct dirstr_desc * const dir2 = get_dirstr(dirp2); | ||
375 | if (dir2) | ||
376 | rc = dir1->stream.bindp == dir2->stream.bindp ? 1 : 0; | ||
377 | |||
378 | RELEASE_DIRSTR(WRITER, dir1); | ||
379 | return rc; | ||
380 | } | ||
381 | |||
382 | /* test directory existence (returns 'false' if a file) */ | ||
383 | bool dir_exists(const char *dirname) | ||
384 | { | ||
385 | file_internal_lock_WRITER(); | ||
386 | bool rc = test_stream_exists_internal(dirname, FF_DIR) > 0; | ||
387 | file_internal_unlock_WRITER(); | ||
388 | return rc; | ||
389 | } | ||
390 | |||
391 | /* get the portable info from the native entry */ | ||
392 | struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry) | ||
393 | { | ||
394 | int rc; | ||
395 | if (!dirp || !entry) | ||
396 | FILE_ERROR(EFAULT, RC); | ||
397 | |||
398 | if (entry->d_name[0] == '\0') | ||
399 | FILE_ERROR(ENOENT, RC); | ||
400 | |||
401 | if ((file_size_t)entry->info.size > FILE_SIZE_MAX) | ||
402 | FILE_ERROR(EOVERFLOW, RC); | ||
403 | |||
404 | return (struct dirinfo) | ||
405 | { | ||
406 | .attribute = entry->info.attr, | ||
407 | .size = entry->info.size, | ||
408 | .mtime = fattime_mktime(entry->info.wrtdate, entry->info.wrttime), | ||
409 | }; | ||
410 | |||
411 | file_error: | ||
412 | return (struct dirinfo){ .attribute = 0 }; | ||
413 | } | ||
diff --git a/firmware/common/dir_uncached.c b/firmware/common/dir_uncached.c deleted file mode 100644 index b850a514e7..0000000000 --- a/firmware/common/dir_uncached.c +++ /dev/null | |||
@@ -1,312 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: dir.c 13741 2007-06-30 02:08:27Z jethead71 $ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <stdio.h> | ||
22 | #include <errno.h> | ||
23 | #include <string.h> | ||
24 | #include <stdbool.h> | ||
25 | #include <stdlib.h> | ||
26 | #include "fat.h" | ||
27 | #include "dir.h" | ||
28 | #include "debug.h" | ||
29 | #include "filefuncs.h" | ||
30 | |||
31 | #if (MEMORYSIZE > 8) | ||
32 | #define MAX_OPEN_DIRS 12 | ||
33 | #else | ||
34 | #define MAX_OPEN_DIRS 8 | ||
35 | #endif | ||
36 | |||
37 | static DIR_UNCACHED opendirs[MAX_OPEN_DIRS]; | ||
38 | |||
39 | // release all dir handles on a given volume "by force", to avoid leaks | ||
40 | int release_dirs(int volume) | ||
41 | { | ||
42 | DIR_UNCACHED* pdir = opendirs; | ||
43 | int dd; | ||
44 | int closed = 0; | ||
45 | for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++) | ||
46 | { | ||
47 | #ifdef HAVE_MULTIVOLUME | ||
48 | if (pdir->fatdir.file.volume == volume) | ||
49 | #else | ||
50 | (void)volume; | ||
51 | #endif | ||
52 | { | ||
53 | pdir->busy = false; /* mark as available, no further action */ | ||
54 | closed++; | ||
55 | } | ||
56 | } | ||
57 | return closed; /* return how many we did */ | ||
58 | } | ||
59 | |||
60 | DIR_UNCACHED* opendir_uncached(const char* name) | ||
61 | { | ||
62 | char namecopy[MAX_PATH]; | ||
63 | char* part; | ||
64 | char* end; | ||
65 | struct fat_direntry entry; | ||
66 | int dd; | ||
67 | DIR_UNCACHED* pdir = opendirs; | ||
68 | #ifdef HAVE_MULTIVOLUME | ||
69 | int volume; | ||
70 | #endif | ||
71 | |||
72 | if ( name[0] != '/' ) { | ||
73 | DEBUGF("Only absolute paths supported right now\n"); | ||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | /* find a free dir descriptor */ | ||
78 | for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++) | ||
79 | if ( !pdir->busy ) | ||
80 | break; | ||
81 | |||
82 | if ( dd == MAX_OPEN_DIRS ) { | ||
83 | DEBUGF("Too many dirs open\n"); | ||
84 | errno = EMFILE; | ||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | pdir->busy = true; | ||
89 | |||
90 | #ifdef HAVE_MULTIVOLUME | ||
91 | /* try to extract a heading volume name, if present */ | ||
92 | volume = strip_volume(name, namecopy); | ||
93 | pdir->volumecounter = 0; | ||
94 | #else | ||
95 | strlcpy(namecopy, name, sizeof(namecopy)); /* just copy */ | ||
96 | #endif | ||
97 | |||
98 | if ( fat_opendir(IF_MV(volume,) &pdir->fatdir, 0, NULL) < 0 ) { | ||
99 | DEBUGF("Failed opening root dir\n"); | ||
100 | pdir->busy = false; | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
104 | for ( part = strtok_r(namecopy, "/", &end); part; | ||
105 | part = strtok_r(NULL, "/", &end)) { | ||
106 | /* scan dir for name */ | ||
107 | while (1) { | ||
108 | if ((fat_getnext(&pdir->fatdir,&entry) < 0) || | ||
109 | (!entry.name[0])) { | ||
110 | pdir->busy = false; | ||
111 | return NULL; | ||
112 | } | ||
113 | if ( (entry.attr & FAT_ATTR_DIRECTORY) && | ||
114 | (!strcasecmp(part, entry.name)) ) { | ||
115 | /* In reality, the parent_dir parameter of fat_opendir seems | ||
116 | * useless because it's sole purpose it to have a way to | ||
117 | * update the file metadata, but here we are only reading | ||
118 | * a directory so there's no need for that kind of stuff. | ||
119 | * However, the rmdir_uncached function uses a ugly hack to | ||
120 | * avoid opening a directory twice when deleting it and thus | ||
121 | * needs those information. That's why we pass pdir->fatdir both | ||
122 | * as the parent directory and the resulting one (this is safe, | ||
123 | * in doubt, check fat_open(dir) code) which will allow this kind of | ||
124 | * (ugly) things */ | ||
125 | if ( fat_opendir(IF_MV(volume,) | ||
126 | &pdir->fatdir, | ||
127 | entry.firstcluster, | ||
128 | &pdir->fatdir) < 0 ) { | ||
129 | DEBUGF("Failed opening dir '%s' (%ld)\n", | ||
130 | part, entry.firstcluster); | ||
131 | pdir->busy = false; | ||
132 | return NULL; | ||
133 | } | ||
134 | #ifdef HAVE_MULTIVOLUME | ||
135 | pdir->volumecounter = -1; /* n.a. to subdirs */ | ||
136 | #endif | ||
137 | break; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | return pdir; | ||
143 | } | ||
144 | |||
145 | int closedir_uncached(DIR_UNCACHED* dir) | ||
146 | { | ||
147 | dir->busy=false; | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir) | ||
152 | { | ||
153 | struct fat_direntry entry; | ||
154 | struct dirent_uncached* theent = &(dir->theent); | ||
155 | |||
156 | if (!dir->busy) | ||
157 | return NULL; | ||
158 | |||
159 | #ifdef HAVE_MULTIVOLUME | ||
160 | /* Volumes (secondary file systems) get inserted into the root directory | ||
161 | of the first volume, since we have no separate top level. */ | ||
162 | if (dir->volumecounter >= 0 /* on a root dir */ | ||
163 | && dir->volumecounter < NUM_VOLUMES /* in range */ | ||
164 | && dir->fatdir.file.volume == 0) /* at volume 0 */ | ||
165 | { /* fake special directories, which don't really exist, but | ||
166 | will get redirected upon opendir_uncached() */ | ||
167 | while (++dir->volumecounter < NUM_VOLUMES) | ||
168 | { | ||
169 | if (fat_ismounted(dir->volumecounter)) | ||
170 | { | ||
171 | memset(theent, 0, sizeof(*theent)); | ||
172 | theent->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; | ||
173 | snprintf(theent->d_name, sizeof(theent->d_name), | ||
174 | VOL_NAMES, dir->volumecounter); | ||
175 | return theent; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | #endif | ||
180 | /* normal directory entry fetching follows here */ | ||
181 | if (fat_getnext(&(dir->fatdir),&entry) < 0) | ||
182 | return NULL; | ||
183 | |||
184 | if ( !entry.name[0] ) | ||
185 | return NULL; | ||
186 | |||
187 | strlcpy(theent->d_name, entry.name, sizeof(theent->d_name)); | ||
188 | theent->info.attribute = entry.attr; | ||
189 | theent->info.wrtdate = entry.wrtdate; | ||
190 | theent->info.wrttime = entry.wrttime; | ||
191 | theent->info.size = entry.filesize; | ||
192 | theent->startcluster = entry.firstcluster; | ||
193 | |||
194 | return theent; | ||
195 | } | ||
196 | |||
197 | int mkdir_uncached(const char *name) | ||
198 | { | ||
199 | DIR_UNCACHED *dir; | ||
200 | char namecopy[MAX_PATH]; | ||
201 | char* end; | ||
202 | char *basename; | ||
203 | char *parent; | ||
204 | struct dirent_uncached *entry; | ||
205 | int dd; | ||
206 | DIR_UNCACHED* pdir = opendirs; | ||
207 | struct fat_dir *newdir; | ||
208 | int rc; | ||
209 | |||
210 | if ( name[0] != '/' ) { | ||
211 | DEBUGF("mkdir: Only absolute paths supported right now\n"); | ||
212 | return -1; | ||
213 | } | ||
214 | /* find a free dir descriptor */ | ||
215 | for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++) | ||
216 | if ( !pdir->busy ) | ||
217 | break; | ||
218 | |||
219 | if ( dd == MAX_OPEN_DIRS ) { | ||
220 | DEBUGF("Too many dirs open\n"); | ||
221 | errno = EMFILE; | ||
222 | return -5; | ||
223 | } | ||
224 | |||
225 | pdir->busy = true; | ||
226 | newdir = &pdir->fatdir; | ||
227 | |||
228 | strlcpy(namecopy, name, sizeof(namecopy)); | ||
229 | |||
230 | /* Split the base name and the path */ | ||
231 | end = strrchr(namecopy, '/'); | ||
232 | *end = 0; | ||
233 | basename = end+1; | ||
234 | |||
235 | if(namecopy == end) /* Root dir? */ | ||
236 | parent = "/"; | ||
237 | else | ||
238 | parent = namecopy; | ||
239 | |||
240 | DEBUGF("mkdir: parent: %s, name: %s\n", parent, basename); | ||
241 | |||
242 | dir = opendir_uncached(parent); | ||
243 | |||
244 | if(!dir) { | ||
245 | DEBUGF("mkdir: can't open parent dir\n"); | ||
246 | pdir->busy = false; | ||
247 | return -2; | ||
248 | } | ||
249 | |||
250 | if(basename[0] == 0) { | ||
251 | DEBUGF("mkdir: Empty dir name\n"); | ||
252 | pdir->busy = false; | ||
253 | errno = EINVAL; | ||
254 | return -3; | ||
255 | } | ||
256 | |||
257 | /* Now check if the name already exists */ | ||
258 | while ((entry = readdir_uncached(dir))) { | ||
259 | if ( !strcasecmp(basename, entry->d_name) ) { | ||
260 | DEBUGF("mkdir error: file exists\n"); | ||
261 | errno = EEXIST; | ||
262 | closedir_uncached(dir); | ||
263 | pdir->busy = false; | ||
264 | return - 4; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | memset(newdir, 0, sizeof(struct fat_dir)); | ||
269 | |||
270 | rc = fat_create_dir(basename, newdir, &(dir->fatdir)); | ||
271 | closedir_uncached(dir); | ||
272 | pdir->busy = false; | ||
273 | |||
274 | return rc; | ||
275 | } | ||
276 | |||
277 | int rmdir_uncached(const char* name) | ||
278 | { | ||
279 | int rc; | ||
280 | DIR_UNCACHED* dir; | ||
281 | struct dirent_uncached* entry; | ||
282 | |||
283 | dir = opendir_uncached(name); | ||
284 | if (!dir) | ||
285 | { | ||
286 | errno = ENOENT; /* open error */ | ||
287 | return -1; | ||
288 | } | ||
289 | |||
290 | /* check if the directory is empty */ | ||
291 | while ((entry = readdir_uncached(dir))) | ||
292 | { | ||
293 | if (strcmp(entry->d_name, ".") && | ||
294 | strcmp(entry->d_name, "..")) | ||
295 | { | ||
296 | DEBUGF("rmdir error: not empty\n"); | ||
297 | errno = ENOTEMPTY; | ||
298 | closedir_uncached(dir); | ||
299 | return -2; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | rc = fat_remove(&(dir->fatdir.file)); | ||
304 | if ( rc < 0 ) { | ||
305 | DEBUGF("Failed removing dir: %d\n", rc); | ||
306 | errno = EIO; | ||
307 | rc = rc * 10 - 3; | ||
308 | } | ||
309 | |||
310 | closedir_uncached(dir); | ||
311 | return rc; | ||
312 | } | ||
diff --git a/firmware/common/dircache.c b/firmware/common/dircache.c index b53fc4d7a6..1e580bf3af 100644 --- a/firmware/common/dircache.c +++ b/firmware/common/dircache.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2005 by Miika Pekkarinen | 10 | * Copyright (C) 2005 by Miika Pekkarinen |
11 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -18,13 +19,7 @@ | |||
18 | * KIND, either express or implied. | 19 | * KIND, either express or implied. |
19 | * | 20 | * |
20 | ****************************************************************************/ | 21 | ****************************************************************************/ |
21 | |||
22 | /* TODO: | ||
23 | - Allow cache live updating while transparent rebuild is running. | ||
24 | */ | ||
25 | |||
26 | #include "config.h" | 22 | #include "config.h" |
27 | |||
28 | #include <stdio.h> | 23 | #include <stdio.h> |
29 | #include <errno.h> | 24 | #include <errno.h> |
30 | #include "string-extra.h" | 25 | #include "string-extra.h" |
@@ -33,6 +28,8 @@ | |||
33 | #include "debug.h" | 28 | #include "debug.h" |
34 | #include "system.h" | 29 | #include "system.h" |
35 | #include "logf.h" | 30 | #include "logf.h" |
31 | #include "fileobj_mgr.h" | ||
32 | #include "pathfuncs.h" | ||
36 | #include "dircache.h" | 33 | #include "dircache.h" |
37 | #include "thread.h" | 34 | #include "thread.h" |
38 | #include "kernel.h" | 35 | #include "kernel.h" |
@@ -42,393 +39,1626 @@ | |||
42 | #include "dir.h" | 39 | #include "dir.h" |
43 | #include "storage.h" | 40 | #include "storage.h" |
44 | #include "audio.h" | 41 | #include "audio.h" |
45 | #if CONFIG_RTC | ||
46 | #include "time.h" | ||
47 | #include "timefuncs.h" | ||
48 | #endif | ||
49 | #include "rbpaths.h" | 42 | #include "rbpaths.h" |
43 | #include "linked_list.h" | ||
44 | #ifdef HAVE_EEPROM_SETTINGS | ||
45 | #include "crc32.h" | ||
46 | #endif | ||
50 | 47 | ||
48 | /** | ||
49 | * Cache memory layout: | ||
50 | * x - array of struct dircache_entry | ||
51 | * r - reserved buffer | ||
52 | * d - name buffer for the name entry of the struct dircache_entry | ||
53 | * 0 - zero bytes to assist free name block sentinel scanning (not 0xfe or 0xff) | ||
54 | * |xxxxxx|rrrrrrrrr|0|dddddd|0| | ||
55 | * | ||
56 | * Subsequent x are allocated from the front, d are allocated from the back, | ||
57 | * using the reserve buffer for entries added after initial scan. | ||
58 | * | ||
59 | * After a while the cache may look like: | ||
60 | * |xxxxxxxx|rrrrr|0|dddddddd|0| | ||
61 | * | ||
62 | * After a reboot, the reserve buffer is restored in it's size, so that the | ||
63 | * total allocation size grows: | ||
64 | * |xxxxxxxx|rrrrrrrrr|0|dddddddd|0| | ||
65 | * | ||
66 | * | ||
67 | * Cache structure: | ||
68 | * Format is memory position independent and uses only indexes as links. The | ||
69 | * buffer pointers are offset back by one entry to make the array 1-based so | ||
70 | * that an index of 0 may be considered an analog of a NULL pointer. | ||
71 | * | ||
72 | * Entry elements are linked together analagously to the filesystem directory | ||
73 | * structure with minor variations that are helpful to the cache's algorithms. | ||
74 | * Each volume has a special root structure in the dircache structure, not an | ||
75 | * entry in the cache, comprising a forest of volume trees which facilitates | ||
76 | * mounting or dismounting of specified volumes on the fly. | ||
77 | * | ||
78 | * Indexes identifying a volume are computed as: index = -volume - 1 | ||
79 | * Returning the volume from these indexes is thus: volume = -index - 1 | ||
80 | * Such indexes are used in root binding and as the 'up' index for an entry | ||
81 | * who's parent is the root directory. | ||
82 | * | ||
83 | * Open files list: | ||
84 | * When dircache is made it is the maintainer of the main volume open files | ||
85 | * lists, even when it is off. Any files open before dircache is enabled or | ||
86 | * initialized must be bound to cache entries by the scan and build operation. | ||
87 | * It maintains these lists in a special way. | ||
88 | * | ||
89 | * Queued (unresolved) bindings are at the back and resolved at the front. | ||
90 | * A pointer to the first of each kind of binding is provided to skip to help | ||
91 | * iterating one sublist or another. | ||
92 | * | ||
93 | * r0->r1->r2->q0->q1->q2->NULL | ||
94 | * ^resolved0 ^queued0 | ||
95 | */ | ||
51 | 96 | ||
52 | /* Queue commands. */ | 97 | #ifdef DIRCACHE_NATIVE |
53 | #define DIRCACHE_BUILD 1 | 98 | #define dircache_lock() file_internal_lock_WRITER() |
54 | #define DIRCACHE_STOP 2 | 99 | #define dircache_unlock() file_internal_unlock_WRITER() |
55 | 100 | ||
56 | #if (MEMORYSIZE > 8) | 101 | /* scan and build parameter data */ |
57 | #define MAX_OPEN_DIRS 12 | 102 | struct sab_component; |
58 | #else | 103 | struct sab |
59 | #define MAX_OPEN_DIRS 8 | 104 | { |
60 | #endif | 105 | struct filestr_base stream; /* scan directory stream */ |
61 | static DIR_CACHED opendirs[MAX_OPEN_DIRS]; | 106 | struct file_base_info info; /* scanned entry info */ |
62 | static char opendir_dnames[MAX_OPEN_DIRS][MAX_PATH]; | 107 | bool volatile quit; /* halt all scanning */ |
108 | struct sab_component *stackend; /* end of stack pointer */ | ||
109 | struct sab_component *top; /* current top of stack */ | ||
110 | struct sab_component | ||
111 | { | ||
112 | int volatile idx; /* cache index of directory */ | ||
113 | int *downp; /* pointer to ce->down */ | ||
114 | int *volatile prevp; /* previous item accessed */ | ||
115 | } stack[]; /* "recursion" stack */ | ||
116 | }; | ||
117 | |||
118 | #else /* !DIRCACHE_NATIVE */ | ||
119 | |||
120 | #error need locking scheme | ||
121 | #define FILESYS_WRITER_LOCK() | ||
122 | #define FILESYS_WRITER_UNLOCK() | ||
63 | 123 | ||
64 | #define MAX_PENDING_BINDINGS 2 | 124 | struct sab_component |
65 | struct fdbind_queue { | 125 | { |
126 | }; | ||
127 | |||
128 | struct sab | ||
129 | { | ||
130 | #ifdef HAVE_MUTLIVOLUME | ||
131 | int volume; | ||
132 | #endif /* HAVE_MULTIVOLUME */ | ||
66 | char path[MAX_PATH]; | 133 | char path[MAX_PATH]; |
67 | int fd; | 134 | unsigned int append; |
135 | }; | ||
136 | #endif /* DIRCACHE_NATIVE */ | ||
137 | |||
138 | enum | ||
139 | { | ||
140 | FRONTIER_SETTLED = 0x0, /* dir entry contents are complete */ | ||
141 | FRONTIER_NEW = 0x1, /* dir entry contents are in-progress */ | ||
142 | FRONTIER_ZONED = 0x2, /* frontier entry permanent mark (very sticky!) */ | ||
143 | FRONTIER_RENEW = 0x4, /* override FRONTIER_ZONED sticky (not stored) */ | ||
68 | }; | 144 | }; |
69 | 145 | ||
70 | /* Unions with char to make pointer arithmetic simpler and avoid casting */ | 146 | enum |
71 | struct dircache_entry { | 147 | { |
72 | struct dirinfo info; | 148 | DCM_BUILD, /* build a volume */ |
149 | DCM_PROCEED, /* merged DCM_BUILD messages */ | ||
150 | DCM_FIRST = DCM_BUILD, | ||
151 | DCM_LAST = DCM_PROCEED, | ||
152 | }; | ||
153 | |||
154 | #define MAX_TINYNAME sizeof (uint32_t) | ||
155 | #define DC_MAX_NAME MIN(MAX_NAME, UINT8_MAX) | ||
156 | |||
157 | /* Throw some warnings if about the limits if things may not work */ | ||
158 | #if MAX_NAME > UINT8_MAX | ||
159 | #warning Need more than 8 bits in name length bitfield | ||
160 | #endif | ||
161 | |||
162 | #if DIRCACHE_LIMIT > ((1 << 24)-255) | ||
163 | #warning Names may not be addressable with 24 bits | ||
164 | #endif | ||
165 | |||
166 | /* data structure used by cache entries */ | ||
167 | struct dircache_entry | ||
168 | { | ||
169 | int next; /* next at same level */ | ||
73 | union { | 170 | union { |
74 | struct dircache_entry *next; | 171 | int down; /* first at child level (if directory) */ |
75 | char* next_char; | 172 | file_size_t filesize; /* size of file in bytes (if file) */ |
76 | }; | 173 | }; |
174 | int up; /* parent index (-volume-1 if root) */ | ||
77 | union { | 175 | union { |
78 | struct dircache_entry *up; | 176 | struct { |
79 | char* up_char; | 177 | uint32_t name : 24; /* indirect storage (.tinyname == 0) */ |
178 | uint32_t length : 8; /* length of name indexed by 'name' */ | ||
80 | }; | 179 | }; |
81 | union { | 180 | unsigned char namebuf[MAX_TINYNAME]; /* direct storage (.tinyname == 1) */ |
82 | struct dircache_entry *down; | ||
83 | char* down_char; | ||
84 | }; | 181 | }; |
85 | long startcluster; | 182 | uint32_t direntry : 16; /* entry # in parent - max 0xffff */ |
86 | char *d_name; | 183 | uint32_t direntries : 5; /* # of entries used - max 21 */ |
87 | }; | 184 | uint32_t tinyname : 1; /* if == 1, name fits in .namebuf */ |
88 | 185 | uint32_t frontier : 2; /* (FRONTIER_* bitflags) */ | |
89 | /* Cache Layout: | 186 | uint32_t attr : 8; /* entry file attributes */ |
90 | * | 187 | #ifdef DIRCACHE_NATIVE |
91 | * x - array of struct dircache_entry | 188 | long firstcluster; /* first file cluster - max 0x0ffffff4 */ |
92 | * r - reserved buffer | 189 | uint16_t wrtdate; /* FAT write date */ |
93 | * d - name buffer for the d_name entry of the struct dircache_entry | 190 | uint16_t wrttime; /* FAT write time */ |
94 | * |xxxxxx|rrrrrrrrr|dddddd| | 191 | #else |
95 | * | 192 | time_t mtime; /* file last-modified time */ |
96 | * subsequent x are allocated from the front, d are allocated from the back, | ||
97 | * using the reserve buffer for entries added after initial scan | ||
98 | * | ||
99 | * after a while the cache may look like: | ||
100 | * |xxxxxxxx|rrrrr|dddddddd| | ||
101 | * | ||
102 | * after a reboot, the reserve buffer is restored in it's size, so that the | ||
103 | * total allocation size grows | ||
104 | * |xxxxxxxx|rrrrrrrrr|dddddddd| | ||
105 | */ | ||
106 | /* this points to the beginnging of the buffer and the first entry */ | ||
107 | static struct dircache_entry *dircache_root; | ||
108 | /* these point to the start and end of the name buffer (d above) */ | ||
109 | static char *d_names_start, *d_names_end; | ||
110 | /* put "." and ".." into the d_names buffer to enable easy pointer logic */ | ||
111 | static char *dot, *dotdot; | ||
112 | #ifdef HAVE_MULTIVOLUME | ||
113 | static struct dircache_entry *append_position; | ||
114 | #endif | 193 | #endif |
194 | dc_serial_t serialnum; /* entry serial number */ | ||
195 | }; | ||
115 | 196 | ||
116 | static DIR_CACHED opendirs[MAX_OPEN_DIRS]; | 197 | /* spare us some tedium */ |
117 | static struct dircache_entry *fd_bindings[MAX_OPEN_FILES]; | 198 | #define ENTRYSIZE (sizeof (struct dircache_entry)) |
118 | |||
119 | static bool dircache_initialized = false; | ||
120 | static bool dircache_initializing = false; | ||
121 | static bool thread_enabled = false; | ||
122 | static unsigned long allocated_size = 0; | ||
123 | static unsigned long dircache_size = 0; | ||
124 | static unsigned long entry_count = 0; | ||
125 | static unsigned long reserve_used = 0; | ||
126 | static unsigned int cache_build_ticks = 0; | ||
127 | static unsigned long appflags = 0; | ||
128 | 199 | ||
200 | /* thread and kernel stuff */ | ||
129 | static struct event_queue dircache_queue SHAREDBSS_ATTR; | 201 | static struct event_queue dircache_queue SHAREDBSS_ATTR; |
130 | static long dircache_stack[(DEFAULT_STACK_SIZE + 0x400)/sizeof(long)]; | 202 | static uintptr_t dircache_stack[DIRCACHE_STACK_SIZE / sizeof (uintptr_t)]; |
131 | static const char dircache_thread_name[] = "dircache"; | 203 | static const char dircache_thread_name[] = "dircache"; |
132 | 204 | ||
133 | static struct fdbind_queue fdbind_cache[MAX_PENDING_BINDINGS]; | 205 | /* struct that is both used during run time and for persistent storage */ |
134 | static int fdbind_idx = 0; | 206 | static struct dircache |
207 | { | ||
208 | /* cache-wide data */ | ||
209 | int free_list; /* first index of free entry list */ | ||
210 | size_t size; /* total size of data (including holes) */ | ||
211 | size_t sizeused; /* bytes of .size bytes actually used */ | ||
212 | union { | ||
213 | unsigned int numentries; /* entry count (including holes) */ | ||
214 | #ifdef HAVE_EEPROM_SETTINGS | ||
215 | size_t sizeentries; /* used when persisting */ | ||
216 | #endif | ||
217 | }; | ||
218 | int names; /* index of first name in name block */ | ||
219 | size_t sizenames; /* size of all names (including holes) */ | ||
220 | size_t namesfree; /* amount of wasted name space */ | ||
221 | int nextnamefree; /* hint of next free name in buffer */ | ||
222 | /* per-volume data */ | ||
223 | struct dircache_volume /* per volume cache data */ | ||
224 | { | ||
225 | uint32_t status : 2; /* cache status of this volume */ | ||
226 | uint32_t frontier : 2; /* (FRONTIER_* bitflags) */ | ||
227 | dc_serial_t serialnum; /* dircache serial number of root */ | ||
228 | int root_down; /* index of first entry of volume root */ | ||
229 | union { | ||
230 | long start_tick; /* when did scan start (scanning) */ | ||
231 | long build_ticks; /* how long to build volume? (ready) */ | ||
232 | }; | ||
233 | } dcvol[NUM_VOLUMES]; | ||
234 | /* these remain unchanged between cache resets */ | ||
235 | size_t last_size; /* last reported size at boot */ | ||
236 | size_t reserve_used; /* reserved used at last build */ | ||
237 | dc_serial_t last_serialnum; /* last serialnumber generated */ | ||
238 | } dircache; | ||
239 | |||
240 | /* struct that is used only for the cache in main memory */ | ||
241 | struct dircache_runinfo | ||
242 | { | ||
243 | /* cache setting and build info */ | ||
244 | int suspended; /* dircache suspend count */ | ||
245 | bool enabled; /* dircache master enable switch */ | ||
246 | unsigned int thread_id; /* current/last thread id */ | ||
247 | bool thread_done; /* thread has exited */ | ||
248 | /* cache buffer info */ | ||
249 | int handle; /* buflib buffer handle */ | ||
250 | size_t bufsize; /* size of buflib allocation - 1 */ | ||
251 | int buflocked; /* don't move due to other allocs */ | ||
252 | union { | ||
253 | void *p; /* address of buffer - ENTRYSIZE */ | ||
254 | struct dircache_entry *pentry; /* alias of .p to assist entry resolution */ | ||
255 | unsigned char *pname; /* alias of .p to assist name resolution */ | ||
256 | }; | ||
257 | struct buflib_callbacks ops; /* buflib ops callbacks */ | ||
258 | /* per-volume data */ | ||
259 | struct dircache_runinfo_volume | ||
260 | { | ||
261 | struct file_base_binding *resolved0; /* first resolved binding in list */ | ||
262 | struct file_base_binding *queued0; /* first queued binding in list */ | ||
263 | struct sab *sabp; /* if building, struct sab in use */ | ||
264 | } dcrivol[NUM_VOLUMES]; | ||
265 | } dircache_runinfo; | ||
266 | |||
267 | #define BINDING_NEXT(bindp) \ | ||
268 | ((struct file_base_binding *)(bindp)->node.next) | ||
269 | |||
270 | #define FOR_EACH_BINDING(start, p) \ | ||
271 | for (struct file_base_binding *p = (start); p; p = BINDING_NEXT(p)) | ||
272 | |||
273 | #define FOR_EACH_CACHE_ENTRY(ce) \ | ||
274 | for (struct dircache_entry *ce = &dircache_runinfo.pentry[1], \ | ||
275 | *_ceend = ce + dircache.numentries; \ | ||
276 | ce < _ceend; ce++) if (ce->serialnum) | ||
277 | |||
278 | #define FOR_EACH_SAB_COMP(sabp, p) \ | ||
279 | for (struct sab_component *p = (sabp)->top; p < (sabp)->stackend; p++) | ||
280 | |||
281 | /* "overloaded" macros to get volume structures */ | ||
282 | #define DCVOL_i(i) (&dircache.dcvol[i]) | ||
283 | #define DCVOL_volume(volume) (&dircache.dcvol[volume]) | ||
284 | #define DCVOL_infop(infop) (&dircache.dcvol[BASEINFO_VOL(infop)]) | ||
285 | #define DCVOL_dirinfop(dirinfop) (&dircache.dcvol[BASEINFO_VOL(dirinfop)]) | ||
286 | #define DCVOL(x) DCVOL_##x(x) | ||
287 | |||
288 | #define DCRIVOL_i(i) (&dircache_runinfo.dcrivol[i]) | ||
289 | #define DCRIVOL_infop(infop) (&dircache_runinfo.dcrivol[BASEINFO_VOL(infop)]) | ||
290 | #define DCRIVOL_bindp(bindp) (&dircache_runinfo.dcrivol[BASEBINDING_VOL(bindp)]) | ||
291 | #define DCRIVOL(x) DCRIVOL_##x(x) | ||
292 | |||
293 | /* reserve over 75% full? */ | ||
294 | #define DIRCACHE_STUFFED(reserve_used) \ | ||
295 | ((reserve_used) > 3*DIRCACHE_RESERVE / 4) | ||
135 | 296 | ||
136 | /* --- Internal cache structure control functions --- */ | 297 | #ifdef HAVE_EEPROM_SETTINGS |
298 | /** | ||
299 | * remove the snapshot file | ||
300 | */ | ||
301 | static int remove_dircache_file(void) | ||
302 | { | ||
303 | return remove(DIRCACHE_FILE); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * open or create the snapshot file | ||
308 | */ | ||
309 | static int open_dircache_file(int oflag) | ||
310 | { | ||
311 | return open(DIRCACHE_FILE, oflag, 0666); | ||
312 | } | ||
313 | #endif /* HAVE_EEPROM_SETTINGS */ | ||
137 | 314 | ||
138 | static inline struct dircache_entry* get_entry(int id) | 315 | #ifdef DIRCACHE_DUMPSTER |
316 | /** | ||
317 | * clean up the memory allocation to make viewing in a hex editor more friendly | ||
318 | * and highlight problems | ||
319 | */ | ||
320 | static inline void dumpster_clean_buffer(void *p, size_t size) | ||
139 | { | 321 | { |
140 | return &dircache_root[id]; | 322 | memset(p, 0xAA, size); |
141 | } | 323 | } |
324 | #endif /* DIRCACHE_DUMPSTER */ | ||
142 | 325 | ||
143 | /* flag to make sure buffer doesn't move due to other allocs. | 326 | /** |
144 | * this is set to true completely during dircache build */ | 327 | * relocate the cache when the buffer has moved |
145 | static bool dont_move = false; | 328 | */ |
146 | static int dircache_handle; | 329 | static int move_callback(int handle, void *current, void *new) |
147 | static int move_callback(int handle, void* current, void* new) | ||
148 | { | 330 | { |
149 | (void)handle; | 331 | if (dircache_runinfo.buflocked) |
150 | if (dont_move) | ||
151 | return BUFLIB_CB_CANNOT_MOVE; | 332 | return BUFLIB_CB_CANNOT_MOVE; |
152 | 333 | ||
153 | #define UPDATE(x) if (x) { x = PTR_ADD(x, diff); } | 334 | dircache_runinfo.p = new - ENTRYSIZE; |
154 | /* relocate the cache */ | 335 | |
155 | ptrdiff_t diff = new - current; | 336 | return BUFLIB_CB_OK; |
156 | for(unsigned i = 0; i < entry_count; i++) | 337 | (void)handle; (void)current; |
338 | } | ||
339 | |||
340 | /** | ||
341 | * add a "don't move" lock count | ||
342 | */ | ||
343 | static inline void buffer_lock(void) | ||
344 | { | ||
345 | dircache_runinfo.buflocked++; | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * remove a "don't move" lock count | ||
350 | */ | ||
351 | static inline void buffer_unlock(void) | ||
352 | { | ||
353 | dircache_runinfo.buflocked--; | ||
354 | } | ||
355 | |||
356 | |||
357 | /** Open file bindings management **/ | ||
358 | |||
359 | /* compare the basic file information and return 'true' if they are logically | ||
360 | equivalent or the same item, else return 'false' if not */ | ||
361 | static inline bool binding_compare(const struct file_base_info *infop1, | ||
362 | const struct file_base_info *infop2) | ||
363 | { | ||
364 | #ifdef DIRCACHE_NATIVE | ||
365 | return fat_file_is_same(&infop1->fatfile, &infop2->fatfile); | ||
366 | #else | ||
367 | #error hey watch it! | ||
368 | #endif | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * bind a file to the cache; "queued" or "resolved" depending upon whether or | ||
373 | * not it has entry information | ||
374 | */ | ||
375 | static void binding_open(struct file_base_binding *bindp) | ||
376 | { | ||
377 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(bindp); | ||
378 | if (bindp->info.dcfile.serialnum) | ||
157 | { | 379 | { |
158 | UPDATE(dircache_root[i].d_name); | 380 | /* already resolved */ |
159 | UPDATE(dircache_root[i].next_char); | 381 | dcrivolp->resolved0 = bindp; |
160 | UPDATE(dircache_root[i].up_char); | 382 | file_binding_insert_first(bindp); |
161 | UPDATE(dircache_root[i].down_char); | ||
162 | } | 383 | } |
163 | dircache_root = new; | 384 | else |
164 | UPDATE(d_names_start); | 385 | { |
165 | UPDATE(d_names_end); | 386 | if (dcrivolp->queued0 == NULL) |
166 | UPDATE(dot); | 387 | dcrivolp->queued0 = bindp; |
167 | UPDATE(dotdot); | ||
168 | 388 | ||
169 | for(unsigned i = 0; i < MAX_OPEN_FILES; i++) | 389 | file_binding_insert_last(bindp); |
170 | UPDATE(fd_bindings[i]); | 390 | } |
391 | } | ||
171 | 392 | ||
172 | #ifdef HAVE_MULTIVOLUME | 393 | /** |
173 | UPDATE(append_position); | 394 | * remove a binding from the cache |
174 | #endif | 395 | */ |
175 | return BUFLIB_CB_OK; | 396 | static void binding_close(struct file_base_binding *bindp) |
397 | { | ||
398 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(bindp); | ||
399 | |||
400 | if (bindp == dcrivolp->queued0) | ||
401 | dcrivolp->queued0 = BINDING_NEXT(bindp); | ||
402 | else if (bindp == dcrivolp->resolved0) | ||
403 | { | ||
404 | struct file_base_binding *nextp = BINDING_NEXT(bindp); | ||
405 | dcrivolp->resolved0 = (nextp == dcrivolp->queued0) ? NULL : nextp; | ||
406 | } | ||
407 | |||
408 | file_binding_remove(bindp); | ||
409 | /* no need to reset it */ | ||
176 | } | 410 | } |
177 | 411 | ||
178 | static struct buflib_callbacks ops = { | 412 | /** |
179 | .move_callback = move_callback, | 413 | * resolve a queued binding with the information from the given source file |
180 | .shrink_callback = NULL, | 414 | */ |
181 | }; | 415 | static void binding_resolve(const struct file_base_info *infop) |
416 | { | ||
417 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(infop); | ||
418 | |||
419 | /* quickly check the queued list to see if it's there */ | ||
420 | struct file_base_binding *prevp = NULL; | ||
421 | FOR_EACH_BINDING(dcrivolp->queued0, p) | ||
422 | { | ||
423 | if (!binding_compare(infop, &p->info)) | ||
424 | { | ||
425 | prevp = p; | ||
426 | continue; | ||
427 | } | ||
428 | |||
429 | if (p == dcrivolp->queued0) | ||
430 | dcrivolp->queued0 = BINDING_NEXT(p); | ||
431 | else | ||
432 | { | ||
433 | file_binding_remove_next(prevp, p); | ||
434 | file_binding_insert_first(p); | ||
435 | } | ||
436 | |||
437 | dcrivolp->resolved0 = p; | ||
438 | |||
439 | /* srcinfop may be the actual one */ | ||
440 | if (&p->info != infop) | ||
441 | p->info.dcfile = infop->dcfile; | ||
442 | |||
443 | break; | ||
444 | } | ||
445 | } | ||
182 | 446 | ||
183 | #ifdef HAVE_EEPROM_SETTINGS | ||
184 | /** | 447 | /** |
185 | * Open the dircache file to save a snapshot on disk | 448 | * dissolve a resolved binding on its volume |
186 | */ | 449 | */ |
187 | static int open_dircache_file(unsigned flags, int permissions) | 450 | static void binding_dissolve(struct file_base_binding *prevp, |
451 | struct file_base_binding *bindp) | ||
188 | { | 452 | { |
189 | if (permissions != 0) | 453 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(bindp); |
190 | return open(DIRCACHE_FILE, flags, permissions); | ||
191 | 454 | ||
192 | return open(DIRCACHE_FILE, flags); | 455 | if (bindp == dcrivolp->resolved0) |
456 | { | ||
457 | struct file_base_binding *nextp = BINDING_NEXT(bindp); | ||
458 | dcrivolp->resolved0 = (nextp == dcrivolp->queued0) ? NULL : nextp; | ||
459 | } | ||
460 | |||
461 | if (dcrivolp->queued0 == NULL) | ||
462 | dcrivolp->queued0 = bindp; | ||
463 | |||
464 | file_binding_remove_next(prevp, bindp); | ||
465 | file_binding_insert_last(bindp); | ||
466 | |||
467 | dircache_dcfile_init(&bindp->info.dcfile); | ||
193 | } | 468 | } |
194 | 469 | ||
195 | /** | 470 | /** |
196 | * Remove the snapshot file | 471 | * dissolve all resolved bindings on a given volume |
197 | */ | 472 | */ |
198 | static int remove_dircache_file(void) | 473 | static void binding_dissolve_volume(struct dircache_runinfo_volume *dcrivolp) |
199 | { | 474 | { |
200 | return remove(DIRCACHE_FILE); | 475 | if (!dcrivolp->resolved0) |
476 | return; | ||
477 | |||
478 | FOR_EACH_BINDING(dcrivolp->resolved0, p) | ||
479 | { | ||
480 | if (p == dcrivolp->queued0) | ||
481 | break; | ||
482 | |||
483 | dircache_dcfile_init(&p->info.dcfile); | ||
484 | } | ||
485 | |||
486 | dcrivolp->queued0 = dcrivolp->resolved0; | ||
487 | dcrivolp->resolved0 = NULL; | ||
201 | } | 488 | } |
202 | #endif | 489 | |
203 | /** | 490 | |
204 | * Internal function to allocate a new dircache_entry from memory. | 491 | /** Dircache buffer management **/ |
492 | |||
493 | /** | ||
494 | * allocate the cache's memory block | ||
205 | */ | 495 | */ |
206 | static struct dircache_entry* allocate_entry(void) | 496 | static int alloc_cache(size_t size) |
207 | { | 497 | { |
208 | struct dircache_entry *next_entry; | 498 | /* pad with one extra-- see alloc_name() and free_name() */ |
209 | 499 | return core_alloc_ex("dircache", size + 1, &dircache_runinfo.ops); | |
210 | if (dircache_size > allocated_size - MAX_PATH*2) | 500 | } |
501 | |||
502 | /** | ||
503 | * put the allocation in dircache control | ||
504 | */ | ||
505 | static void set_buffer(int handle, size_t size) | ||
506 | { | ||
507 | void *p = core_get_data(handle); | ||
508 | |||
509 | #ifdef DIRCACHE_DUMPSTER | ||
510 | dumpster_clean_buffer(p, size); | ||
511 | #endif /* DIRCACHE_DUMPSTER */ | ||
512 | |||
513 | /* set it up as a 1-based array */ | ||
514 | dircache_runinfo.p = p - ENTRYSIZE; | ||
515 | |||
516 | if (dircache_runinfo.handle != handle) | ||
211 | { | 517 | { |
212 | logf("size limit reached"); | 518 | /* new buffer */ |
213 | return NULL; | 519 | dircache_runinfo.handle = handle; |
520 | dircache_runinfo.bufsize = size; | ||
521 | dircache.names = size + ENTRYSIZE; | ||
522 | dircache_runinfo.pname[dircache.names - 1] = 0; | ||
523 | dircache_runinfo.pname[dircache.names ] = 0; | ||
214 | } | 524 | } |
215 | 525 | } | |
216 | next_entry = &dircache_root[entry_count++]; | ||
217 | next_entry->d_name = NULL; | ||
218 | next_entry->up = NULL; | ||
219 | next_entry->down = NULL; | ||
220 | next_entry->next = NULL; | ||
221 | 526 | ||
222 | dircache_size += sizeof(struct dircache_entry); | 527 | /** |
528 | * remove the allocation from dircache control and return the handle | ||
529 | */ | ||
530 | static int reset_buffer(void) | ||
531 | { | ||
532 | int handle = dircache_runinfo.handle; | ||
533 | if (handle > 0) | ||
534 | { | ||
535 | /* don't mind .p; it might get changed by the callback even after | ||
536 | this call; buffer presence is determined by the following: */ | ||
537 | dircache_runinfo.handle = 0; | ||
538 | dircache_runinfo.bufsize = 0; | ||
539 | } | ||
540 | |||
541 | return handle; | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * return the number of bytes remaining in the buffer | ||
546 | */ | ||
547 | static size_t dircache_buf_remaining(void) | ||
548 | { | ||
549 | if (!dircache_runinfo.handle) | ||
550 | return 0; | ||
551 | |||
552 | return dircache_runinfo.bufsize - dircache.size; | ||
553 | } | ||
554 | |||
555 | /** | ||
556 | * return the amount of reserve space used | ||
557 | */ | ||
558 | static size_t reserve_buf_used(void) | ||
559 | { | ||
560 | size_t remaining = dircache_buf_remaining(); | ||
561 | return (remaining < DIRCACHE_RESERVE) ? | ||
562 | DIRCACHE_RESERVE - remaining : 0; | ||
563 | } | ||
564 | |||
565 | |||
566 | /** Internal cache structure control functions **/ | ||
567 | |||
568 | /** | ||
569 | * generate the next serial number in the sequence | ||
570 | */ | ||
571 | static dc_serial_t next_serialnum(void) | ||
572 | { | ||
573 | dc_serial_t serialnum = MAX(dircache.last_serialnum + 1, 1); | ||
574 | dircache.last_serialnum = serialnum; | ||
575 | return serialnum; | ||
576 | } | ||
577 | |||
578 | /** | ||
579 | * return the dircache volume pointer for the special index | ||
580 | */ | ||
581 | static struct dircache_volume * get_idx_dcvolp(int idx) | ||
582 | { | ||
583 | if (idx >= 0) | ||
584 | return NULL; | ||
585 | |||
586 | return &dircache.dcvol[IF_MV_VOL(-idx - 1)]; | ||
587 | } | ||
588 | |||
589 | /** | ||
590 | * return the cache entry referenced by idx (NULL if outside buffer) | ||
591 | */ | ||
592 | static struct dircache_entry * get_entry(int idx) | ||
593 | { | ||
594 | if (idx <= 0 || (unsigned int)idx > dircache.numentries) | ||
595 | return NULL; | ||
223 | 596 | ||
224 | return next_entry; | 597 | return &dircache_runinfo.pentry[idx]; |
225 | } | 598 | } |
226 | 599 | ||
227 | /** | 600 | /** |
228 | * Internal function to allocate a dircache_entry and set | 601 | * return the index of the cache entry (0 if outside buffer) |
229 | * ->next entry pointers. | ||
230 | */ | 602 | */ |
231 | static struct dircache_entry* dircache_gen_next(struct dircache_entry *ce) | 603 | static int get_index(const struct dircache_entry *ce) |
232 | { | 604 | { |
233 | struct dircache_entry *next_entry; | 605 | if (!PTR_IN_ARRAY(dircache_runinfo.pentry + 1, ce, |
606 | dircache.numentries + 1)) | ||
607 | { | ||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | return ce - dircache_runinfo.pentry; | ||
612 | } | ||
234 | 613 | ||
235 | if ( (next_entry = allocate_entry()) == NULL) | 614 | /** |
615 | * return the sublist down pointer for the sublist that contains entry 'idx' | ||
616 | */ | ||
617 | static int * get_downidxp(int idx) | ||
618 | { | ||
619 | /* NOTE: 'idx' must refer to a directory or the result is undefined */ | ||
620 | if (idx == 0 || idx < -NUM_VOLUMES) | ||
236 | return NULL; | 621 | return NULL; |
237 | next_entry->up = ce->up; | 622 | |
238 | ce->next = next_entry; | 623 | if (idx > 0) |
239 | 624 | { | |
240 | return next_entry; | 625 | /* a normal entry */ |
626 | struct dircache_entry *ce = get_entry(idx); | ||
627 | return ce ? &ce->down : NULL; | ||
628 | } | ||
629 | else | ||
630 | { | ||
631 | /* a volume root */ | ||
632 | return &get_idx_dcvolp(idx)->root_down; | ||
633 | } | ||
241 | } | 634 | } |
242 | 635 | ||
243 | /* | 636 | /** |
244 | * Internal function to allocate a dircache_entry and set | 637 | * return a pointer to the index referencing the cache entry that 'idx' |
245 | * ->down entry pointers. | 638 | * references |
246 | */ | 639 | */ |
247 | static struct dircache_entry* dircache_gen_down(struct dircache_entry *ce) | 640 | static int * get_previdxp(int idx) |
248 | { | 641 | { |
249 | struct dircache_entry *next_entry; | 642 | struct dircache_entry *ce = get_entry(idx); |
250 | 643 | ||
251 | if ( (next_entry = allocate_entry()) == NULL) | 644 | int *prevp = get_downidxp(ce->up); |
645 | if (!prevp) | ||
252 | return NULL; | 646 | return NULL; |
253 | next_entry->up = ce; | 647 | |
254 | ce->down = next_entry; | 648 | while (1) |
255 | 649 | { | |
256 | return next_entry; | 650 | int next = *prevp; |
651 | if (!next || next == idx) | ||
652 | break; | ||
653 | |||
654 | prevp = &get_entry(next)->next; | ||
655 | } | ||
656 | |||
657 | return prevp; | ||
658 | } | ||
659 | |||
660 | /** | ||
661 | * if required, adjust the lists and directory read of any scan and build in | ||
662 | * progress | ||
663 | */ | ||
664 | static void sab_sync_scan(struct sab *sabp, int *prevp, int *nextp) | ||
665 | { | ||
666 | struct sab_component *abovep = NULL; | ||
667 | FOR_EACH_SAB_COMP(sabp, p) | ||
668 | { | ||
669 | if (nextp != p->prevp) | ||
670 | { | ||
671 | abovep = p; | ||
672 | continue; | ||
673 | } | ||
674 | |||
675 | /* removing an item being scanned; set the component position to the | ||
676 | entry before this */ | ||
677 | p->prevp = prevp; | ||
678 | |||
679 | if (p == sabp->top) | ||
680 | { | ||
681 | /* removed at item in the directory who's immediate contents are | ||
682 | being scanned */ | ||
683 | if (prevp == p->downp) | ||
684 | { | ||
685 | /* was first item; rewind it */ | ||
686 | dircache_rewinddir_internal(&sabp->info); | ||
687 | } | ||
688 | else | ||
689 | { | ||
690 | struct dircache_entry *ceprev = | ||
691 | container_of(prevp, struct dircache_entry, next); | ||
692 | #ifdef DIRCACHE_NATIVE | ||
693 | sabp->info.fatfile.e.entry = ceprev->direntry; | ||
694 | sabp->info.fatfile.e.entries = ceprev->direntries; | ||
695 | #endif | ||
696 | } | ||
697 | } | ||
698 | else if (abovep) | ||
699 | { | ||
700 | /* the directory being scanned or a parent of it has been removed; | ||
701 | abort its build or cache traversal */ | ||
702 | abovep->idx = 0; | ||
703 | } | ||
704 | |||
705 | break; | ||
706 | } | ||
707 | } | ||
708 | |||
709 | /** | ||
710 | * get a pointer to an allocated name given a cache index | ||
711 | */ | ||
712 | static inline unsigned char * get_name(int nameidx) | ||
713 | { | ||
714 | return &dircache_runinfo.pname[nameidx]; | ||
715 | } | ||
716 | |||
717 | /** | ||
718 | * get the cache buffer index of the given name | ||
719 | */ | ||
720 | static inline int get_nameidx(const unsigned char *pname) | ||
721 | { | ||
722 | return pname - dircache_runinfo.pname; | ||
723 | } | ||
724 | |||
725 | /** | ||
726 | * copy the entry's name to a buffer (which assumed to be of sufficient size) | ||
727 | */ | ||
728 | static void entry_name_copy(char *dst, const struct dircache_entry *ce) | ||
729 | { | ||
730 | if (LIKELY(!ce->tinyname)) | ||
731 | { | ||
732 | strmemcpy(dst, get_name(ce->name), ce->length); | ||
733 | return; | ||
734 | } | ||
735 | |||
736 | const unsigned char *src = ce->namebuf; | ||
737 | size_t len = 0; | ||
738 | while (len++ < MAX_TINYNAME && *src) | ||
739 | *dst++ = *src++; | ||
740 | |||
741 | *dst = '\0'; | ||
742 | } | ||
743 | |||
744 | /** | ||
745 | * set the namesfree hint to a new position | ||
746 | */ | ||
747 | static void set_namesfree_hint(const unsigned char *hintp) | ||
748 | { | ||
749 | int hintidx = get_nameidx(hintp); | ||
750 | |||
751 | if (hintidx >= (int)(dircache.names + dircache.sizenames)) | ||
752 | hintidx = dircache.names; | ||
753 | |||
754 | dircache.nextnamefree = hintidx; | ||
257 | } | 755 | } |
258 | 756 | ||
259 | /** | 757 | /** |
260 | * Returns true if there is an event waiting in the queue | 758 | * allocate a buffer to use for a new name |
261 | * that requires the current operation to be aborted. | ||
262 | */ | 759 | */ |
263 | static bool check_event_queue(void) | 760 | static int alloc_name(size_t size) |
761 | { | ||
762 | int nameidx = 0; | ||
763 | |||
764 | if (dircache.namesfree >= size) | ||
765 | { | ||
766 | /* scan for a free gap starting at the hint point - first fit */ | ||
767 | unsigned char *start = get_name(dircache.nextnamefree), *p = start; | ||
768 | unsigned char *namesend = get_name(dircache.names + dircache.sizenames); | ||
769 | size_t gapsize = 0; | ||
770 | |||
771 | while (gapsize < size) | ||
772 | { | ||
773 | if ((p = memchr(p, 0xff, namesend - p))) | ||
774 | { | ||
775 | /* found a sentinel; see if there are enough in a row */ | ||
776 | gapsize = 1; | ||
777 | while (*++p == 0xff && gapsize < size) | ||
778 | gapsize++; | ||
779 | } | ||
780 | else | ||
781 | { | ||
782 | if (namesend == start) | ||
783 | break; /* exhausted */ | ||
784 | |||
785 | /* wrap */ | ||
786 | namesend = start; | ||
787 | p = get_name(dircache.names); | ||
788 | |||
789 | if (p == namesend) | ||
790 | break; /* initial hint was at names start */ | ||
791 | } | ||
792 | } | ||
793 | |||
794 | if (gapsize >= size) | ||
795 | { | ||
796 | unsigned char *namep = p - gapsize; | ||
797 | nameidx = get_nameidx(namep); | ||
798 | |||
799 | if (*p == 0xff) | ||
800 | { | ||
801 | /* if only a tiny block remains after buffer, claim it too */ | ||
802 | size_t tinysize = 1; | ||
803 | while (*++p == 0xff && tinysize <= MAX_TINYNAME) | ||
804 | tinysize++; | ||
805 | |||
806 | if (tinysize <= MAX_TINYNAME) | ||
807 | { | ||
808 | /* mark with tiny block sentinel */ | ||
809 | memset(p - tinysize, 0xfe, tinysize); | ||
810 | size += tinysize; | ||
811 | } | ||
812 | } | ||
813 | |||
814 | dircache.namesfree -= size; | ||
815 | dircache.sizeused += size; | ||
816 | set_namesfree_hint(namep + size); | ||
817 | } | ||
818 | } | ||
819 | |||
820 | if (!nameidx) | ||
821 | { | ||
822 | /* no sufficiently long free gaps; allocate anew */ | ||
823 | if (dircache_buf_remaining() <= size) | ||
824 | { | ||
825 | dircache.last_size = 0; | ||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | dircache.names -= size; | ||
830 | dircache.sizenames += size; | ||
831 | nameidx = dircache.names; | ||
832 | dircache.size += size; | ||
833 | dircache.sizeused += size; | ||
834 | *get_name(dircache.names - 1) = 0; | ||
835 | } | ||
836 | |||
837 | return nameidx; | ||
838 | } | ||
839 | |||
840 | /** | ||
841 | * mark a name as free and note that its bytes are available | ||
842 | */ | ||
843 | static void free_name(int nameidx, size_t size) | ||
844 | { | ||
845 | unsigned char *beg = get_name(nameidx); | ||
846 | unsigned char *end = beg + size; | ||
847 | |||
848 | /* merge with any adjacent tiny blocks */ | ||
849 | while (beg[-1] == 0xfe) | ||
850 | --beg; | ||
851 | |||
852 | while (end[1] == 0xfe) | ||
853 | ++end; | ||
854 | |||
855 | size = end - beg; | ||
856 | memset(beg, 0xff, size); | ||
857 | dircache.namesfree += size; | ||
858 | dircache.sizeused -= size; | ||
859 | set_namesfree_hint(beg); | ||
860 | } | ||
861 | |||
862 | /** | ||
863 | * allocate and assign a name to the entry | ||
864 | */ | ||
865 | static bool entry_assign_name(struct dircache_entry *ce, | ||
866 | const unsigned char *name, size_t size) | ||
867 | { | ||
868 | unsigned char *copyto; | ||
869 | |||
870 | if (size <= MAX_TINYNAME) | ||
871 | { | ||
872 | copyto = ce->namebuf; | ||
873 | |||
874 | if (size < MAX_TINYNAME) | ||
875 | copyto[size] = '\0'; | ||
876 | |||
877 | ce->tinyname = 1; | ||
878 | } | ||
879 | else | ||
880 | { | ||
881 | if (size > DC_MAX_NAME) | ||
882 | size = DC_MAX_NAME; | ||
883 | |||
884 | int nameidx = alloc_name(size); | ||
885 | if (!nameidx) | ||
886 | return false; | ||
887 | |||
888 | copyto = get_name(nameidx); | ||
889 | |||
890 | ce->tinyname = 0; | ||
891 | ce->name = nameidx; | ||
892 | ce->length = size; | ||
893 | } | ||
894 | |||
895 | memcpy(copyto, name, size); | ||
896 | return true; | ||
897 | } | ||
898 | |||
899 | /** | ||
900 | * free the name for the entry | ||
901 | */ | ||
902 | static void entry_unassign_name(struct dircache_entry *ce) | ||
903 | { | ||
904 | if (!ce->tinyname) | ||
905 | free_name(ce->name, ce->length); | ||
906 | } | ||
907 | |||
908 | /** | ||
909 | * assign a new name to the entry | ||
910 | */ | ||
911 | static bool entry_reassign_name(struct dircache_entry *ce, | ||
912 | const unsigned char *newname) | ||
913 | { | ||
914 | size_t oldlen = ce->tinyname ? 0 : ce->length; | ||
915 | size_t newlen = strlen(newname); | ||
916 | |||
917 | if (oldlen == newlen || (oldlen == 0 && newlen <= MAX_TINYNAME)) | ||
918 | { | ||
919 | char *p = mempcpy(oldlen == 0 ? ce->namebuf : get_name(ce->name), | ||
920 | newname, newlen); | ||
921 | if (newlen < MAX_TINYNAME) | ||
922 | *p = '\0'; | ||
923 | return true; | ||
924 | } | ||
925 | |||
926 | /* needs a new name allocation; if the new name fits in the freed block, | ||
927 | it will use it immediately without a lengthy search */ | ||
928 | entry_unassign_name(ce); | ||
929 | return entry_assign_name(ce, newname, newlen); | ||
930 | } | ||
931 | |||
932 | /** | ||
933 | * allocate a dircache_entry from memory using freed ones if available | ||
934 | */ | ||
935 | static int alloc_entry(struct dircache_entry **res) | ||
936 | { | ||
937 | struct dircache_entry *ce; | ||
938 | int idx = dircache.free_list; | ||
939 | |||
940 | if (idx) | ||
941 | { | ||
942 | /* reuse a freed entry */ | ||
943 | ce = get_entry(idx); | ||
944 | dircache.free_list = ce->next; | ||
945 | } | ||
946 | else if (dircache_buf_remaining() > ENTRYSIZE) | ||
947 | { | ||
948 | /* allocate a new one */ | ||
949 | idx = ++dircache.numentries; | ||
950 | dircache.size += ENTRYSIZE; | ||
951 | ce = get_entry(idx); | ||
952 | } | ||
953 | else | ||
954 | { | ||
955 | dircache.last_size = 0; | ||
956 | *res = NULL; | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | dircache.sizeused += ENTRYSIZE; | ||
961 | |||
962 | ce->next = 0; | ||
963 | ce->up = 0; | ||
964 | ce->down = 0; | ||
965 | ce->length = 0; | ||
966 | ce->frontier = FRONTIER_SETTLED; | ||
967 | ce->serialnum = next_serialnum(); | ||
968 | |||
969 | *res = ce; | ||
970 | return idx; | ||
971 | } | ||
972 | |||
973 | /** | ||
974 | * free an entry's allocations in the cache; must not be linked to anything | ||
975 | * by this time (orphan!) | ||
976 | */ | ||
977 | static void free_orphan_entry(struct dircache_runinfo_volume *dcrivolp, | ||
978 | struct dircache_entry *ce, int idx) | ||
979 | { | ||
980 | if (dcrivolp) | ||
981 | { | ||
982 | /* was an established entry; find any associated resolved binding and | ||
983 | dissolve it; bindings are kept strictly synchronized with changes | ||
984 | to the storage so a simple serial number comparison is sufficient */ | ||
985 | struct file_base_binding *prevp = NULL; | ||
986 | FOR_EACH_BINDING(dcrivolp->resolved0, p) | ||
987 | { | ||
988 | if (p == dcrivolp->queued0) | ||
989 | break; | ||
990 | |||
991 | if (ce->serialnum == p->info.dcfile.serialnum) | ||
992 | { | ||
993 | binding_dissolve(prevp, p); | ||
994 | break; | ||
995 | } | ||
996 | |||
997 | prevp = p; | ||
998 | } | ||
999 | } | ||
1000 | |||
1001 | entry_unassign_name(ce); | ||
1002 | |||
1003 | /* no serialnum says "it's free" (for cache-wide iterators) */ | ||
1004 | ce->serialnum = 0; | ||
1005 | |||
1006 | /* add to free list */ | ||
1007 | ce->next = dircache.free_list; | ||
1008 | dircache.free_list = idx; | ||
1009 | dircache.sizeused -= ENTRYSIZE; | ||
1010 | } | ||
1011 | |||
1012 | /** | ||
1013 | * allocates a new entry of with the name specified by 'basename' | ||
1014 | */ | ||
1015 | static int create_entry(const char *basename, | ||
1016 | struct dircache_entry **res) | ||
1017 | { | ||
1018 | int idx = alloc_entry(res); | ||
1019 | if (!idx) | ||
1020 | { | ||
1021 | logf("size limit reached (entry)"); | ||
1022 | return 0; /* full */ | ||
1023 | } | ||
1024 | |||
1025 | if (!entry_assign_name(*res, basename, strlen(basename))) | ||
1026 | { | ||
1027 | free_orphan_entry(NULL, *res, idx); | ||
1028 | logf("size limit reached (name)"); | ||
1029 | return 0; /* really full! */ | ||
1030 | } | ||
1031 | |||
1032 | return idx; | ||
1033 | } | ||
1034 | |||
1035 | /** | ||
1036 | * unlink the entry at *prevp and adjust the scanner if needed | ||
1037 | */ | ||
1038 | static void remove_entry(struct dircache_runinfo_volume *dcrivolp, | ||
1039 | struct dircache_entry *ce, int *prevp) | ||
1040 | { | ||
1041 | /* unlink it from its list */ | ||
1042 | *prevp = ce->next; | ||
1043 | |||
1044 | if (dcrivolp) | ||
1045 | { | ||
1046 | /* adjust scanner iterator if needed */ | ||
1047 | struct sab *sabp = dcrivolp->sabp; | ||
1048 | if (sabp) | ||
1049 | sab_sync_scan(sabp, prevp, &ce->next); | ||
1050 | } | ||
1051 | } | ||
1052 | |||
1053 | /** | ||
1054 | * free the entire subtree in the referenced parent down index | ||
1055 | */ | ||
1056 | static void free_subentries(struct dircache_runinfo_volume *dcrivolp, int *downp) | ||
1057 | { | ||
1058 | while (1) | ||
1059 | { | ||
1060 | int idx = *downp; | ||
1061 | struct dircache_entry *ce = get_entry(idx); | ||
1062 | if (!ce) | ||
1063 | break; | ||
1064 | |||
1065 | if ((ce->attr & ATTR_DIRECTORY) && ce->down) | ||
1066 | free_subentries(dcrivolp, &ce->down); | ||
1067 | |||
1068 | remove_entry(dcrivolp, ce, downp); | ||
1069 | free_orphan_entry(dcrivolp, ce, idx); | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | /** | ||
1074 | * free the specified file entry and its children | ||
1075 | */ | ||
1076 | static void free_file_entry(struct file_base_info *infop) | ||
1077 | { | ||
1078 | int idx = infop->dcfile.idx; | ||
1079 | if (idx <= 0) | ||
1080 | return; /* can't remove a root/invalid */ | ||
1081 | |||
1082 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(infop); | ||
1083 | |||
1084 | struct dircache_entry *ce = get_entry(idx); | ||
1085 | if ((ce->attr & ATTR_DIRECTORY) && ce->down) | ||
1086 | { | ||
1087 | /* gonna get all this contents (normally the "." and "..") */ | ||
1088 | free_subentries(dcrivolp, &ce->down); | ||
1089 | } | ||
1090 | |||
1091 | remove_entry(dcrivolp, ce, get_previdxp(idx)); | ||
1092 | free_orphan_entry(dcrivolp, ce, idx); | ||
1093 | } | ||
1094 | |||
1095 | /** | ||
1096 | * insert the new entry into the parent, sorted into position | ||
1097 | */ | ||
1098 | static void insert_file_entry(struct file_base_info *dirinfop, | ||
1099 | struct dircache_entry *ce) | ||
1100 | { | ||
1101 | /* DIRCACHE_NATIVE: the entires are sorted into the spot it would be on | ||
1102 | * the storage medium based upon the directory entry number, in-progress | ||
1103 | * scans will catch it or miss it in just the same way they would if | ||
1104 | * directly scanning the disk. If this is behind an init scan, it gets | ||
1105 | * added anyway; if in front of it, then scanning will compare what it | ||
1106 | * finds in order to avoid adding a duplicate. | ||
1107 | * | ||
1108 | * All others: the order of entries of the host filesystem is not known so | ||
1109 | * this must be placed at the end so that a build scan won't miss it and | ||
1110 | * add a duplicate since it will be comparing any entries it finds in front | ||
1111 | * of it. | ||
1112 | */ | ||
1113 | int diridx = dirinfop->dcfile.idx; | ||
1114 | int *nextp = get_downidxp(diridx); | ||
1115 | |||
1116 | while (8675309) | ||
1117 | { | ||
1118 | struct dircache_entry *nextce = get_entry(*nextp); | ||
1119 | if (!nextce) | ||
1120 | break; | ||
1121 | |||
1122 | #ifdef DIRCACHE_NATIVE | ||
1123 | if (nextce->direntry > ce->direntry) | ||
1124 | break; | ||
1125 | |||
1126 | /* now, nothing should be equal to ours or that is a bug since it | ||
1127 | would already exist (and it shouldn't because it's just been | ||
1128 | created or moved) */ | ||
1129 | #endif /* DIRCACHE_NATIVE */ | ||
1130 | |||
1131 | nextp = &nextce->next; | ||
1132 | } | ||
1133 | |||
1134 | ce->up = diridx; | ||
1135 | ce->next = *nextp; | ||
1136 | *nextp = get_index(ce); | ||
1137 | } | ||
1138 | |||
1139 | /** | ||
1140 | * unlink the entry from its parent and return its pointer to the caller | ||
1141 | */ | ||
1142 | static struct dircache_entry * remove_file_entry(struct file_base_info *infop) | ||
1143 | { | ||
1144 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(infop); | ||
1145 | struct dircache_entry *ce = get_entry(infop->dcfile.idx); | ||
1146 | remove_entry(dcrivolp, ce, get_previdxp(infop->dcfile.idx)); | ||
1147 | return ce; | ||
1148 | } | ||
1149 | |||
1150 | /** | ||
1151 | * set the frontier indicator for the given cache index | ||
1152 | */ | ||
1153 | static void establish_frontier(int idx, uint32_t code) | ||
1154 | { | ||
1155 | if (idx < 0) | ||
1156 | { | ||
1157 | int volume = IF_MV_VOL(-idx - 1); | ||
1158 | uint32_t val = dircache.dcvol[volume].frontier; | ||
1159 | if (code & FRONTIER_RENEW) | ||
1160 | val &= ~FRONTIER_ZONED; | ||
1161 | dircache.dcvol[volume].frontier = code | (val & FRONTIER_ZONED); | ||
1162 | } | ||
1163 | else if (idx > 0) | ||
1164 | { | ||
1165 | struct dircache_entry *ce = get_entry(idx); | ||
1166 | uint32_t val = ce->frontier; | ||
1167 | if (code & FRONTIER_RENEW) | ||
1168 | val &= ~FRONTIER_ZONED; | ||
1169 | ce->frontier = code | (val & FRONTIER_ZONED); | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | /** | ||
1174 | * remove all messages from the queue, responding to anyone waiting | ||
1175 | */ | ||
1176 | static void clear_dircache_queue(void) | ||
264 | { | 1177 | { |
265 | struct queue_event ev; | 1178 | struct queue_event ev; |
266 | 1179 | ||
267 | if(!queue_peek(&dircache_queue, &ev)) | 1180 | while (1) |
268 | return false; | 1181 | { |
269 | 1182 | queue_wait_w_tmo(&dircache_queue, &ev, 0); | |
270 | switch (ev.id) | 1183 | if (ev.id == SYS_TIMEOUT) |
271 | { | 1184 | break; |
272 | case DIRCACHE_STOP: | 1185 | |
273 | case SYS_USB_CONNECTED: | 1186 | /* respond to any synchronous build queries; since we're already |
274 | #ifdef HAVE_HOTSWAP | 1187 | building and thusly allocated, any additional requests can be |
275 | case SYS_FS_CHANGED: | 1188 | processed async */ |
276 | #endif | 1189 | if (ev.id == DCM_BUILD) |
277 | return true; | 1190 | { |
1191 | int *rcp = (int *)ev.data; | ||
1192 | if (rcp) | ||
1193 | *rcp = 0; | ||
1194 | } | ||
278 | } | 1195 | } |
279 | |||
280 | return false; | ||
281 | } | 1196 | } |
282 | 1197 | ||
283 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 1198 | /** |
284 | /* scan and build static data (avoid redundancy on stack) */ | 1199 | * service dircache_queue during a scan and build |
285 | static struct | 1200 | */ |
1201 | static void process_events(void) | ||
286 | { | 1202 | { |
287 | #ifdef HAVE_MULTIVOLUME | 1203 | yield(); |
288 | int volume; | 1204 | |
289 | #endif | 1205 | /* only count externally generated commands */ |
290 | struct fat_dir *dir; | 1206 | if (!queue_peek_ex(&dircache_queue, NULL, 0, QPEEK_FILTER1(DCM_BUILD))) |
291 | struct fat_direntry *direntry; | 1207 | return; |
292 | }sab; | 1208 | |
293 | 1209 | clear_dircache_queue(); | |
294 | static int sab_process_dir(unsigned long startcluster, struct dircache_entry *ce) | 1210 | |
295 | { | 1211 | /* this reminds us to keep moving after we're done here; a volume we passed |
296 | /* normally, opendir expects a full fat_dir as parent but in our case, | 1212 | up earlier could have been mounted and need refreshing; it just condenses |
297 | * it's completely useless because we don't modify anything | 1213 | a slew of requests into one and isn't mistaken for an externally generated |
298 | * WARNING: this heavily relies on current FAT implementation ! */ | 1214 | command */ |
299 | 1215 | queue_post(&dircache_queue, DCM_PROCEED, 0); | |
300 | /* those field are necessary to update the FAT entry in case of modification | 1216 | } |
301 | here we don't touch anything so we put dummy values */ | 1217 | |
302 | sab.dir->entry = 0; | 1218 | #if defined (DIRCACHE_NATIVE) |
303 | sab.dir->entrycount = 0; | 1219 | /** |
304 | sab.dir->file.firstcluster = 0; | 1220 | * scan and build the contents of a subdirectory |
305 | /* open directory */ | 1221 | */ |
306 | int rc = fat_opendir(IF_MV(sab.volume,) sab.dir, startcluster, sab.dir); | 1222 | static void sab_process_sub(struct sab *sabp) |
307 | if(rc < 0) | 1223 | { |
308 | { | 1224 | struct fat_direntry *const fatentp = get_dir_fatent(); |
309 | logf("fat_opendir failed: %d", rc); | 1225 | struct filestr_base *const streamp = &sabp->stream; |
310 | return rc; | 1226 | struct file_base_info *const infop = &sabp->info; |
1227 | |||
1228 | int idx = infop->dcfile.idx; | ||
1229 | int *downp = get_downidxp(idx); | ||
1230 | if (!downp) | ||
1231 | return; | ||
1232 | |||
1233 | while (1) | ||
1234 | { | ||
1235 | struct sab_component *compp = --sabp->top; | ||
1236 | compp->idx = idx; | ||
1237 | compp->downp = downp; | ||
1238 | compp->prevp = downp; | ||
1239 | |||
1240 | /* open directory stream */ | ||
1241 | filestr_base_init(streamp); | ||
1242 | fileobj_fileop_open(streamp, infop, FO_DIRECTORY); | ||
1243 | fat_rewind(&streamp->fatstr); | ||
1244 | uncached_rewinddir_internal(infop); | ||
1245 | |||
1246 | const long dircluster = streamp->infop->fatfile.firstcluster; | ||
1247 | |||
1248 | /* first pass: read directory */ | ||
1249 | while (1) | ||
1250 | { | ||
1251 | if (sabp->stack + 1 < sabp->stackend) | ||
1252 | { | ||
1253 | /* release control and process queued events */ | ||
1254 | dircache_unlock(); | ||
1255 | process_events(); | ||
1256 | dircache_lock(); | ||
1257 | |||
1258 | if (sabp->quit || !compp->idx) | ||
1259 | break; | ||
1260 | } | ||
1261 | /* else an immediate-contents directory scan */ | ||
1262 | |||
1263 | int rc = uncached_readdir_internal(streamp, infop, fatentp); | ||
1264 | if (rc <= 0) | ||
1265 | { | ||
1266 | if (rc < 0) | ||
1267 | sabp->quit = true; | ||
1268 | else | ||
1269 | compp->prevp = downp; /* rewind list */ | ||
1270 | |||
1271 | break; | ||
1272 | } | ||
1273 | |||
1274 | struct dircache_entry *ce; | ||
1275 | int prev = *compp->prevp; | ||
1276 | |||
1277 | if (prev) | ||
1278 | { | ||
1279 | /* there are entries ahead of us; they will be what was just | ||
1280 | read or something to be subsequently read; if it belongs | ||
1281 | ahead of this one, insert a new entry before it; if it's | ||
1282 | the entry just scanned, do nothing further and continue | ||
1283 | with the next */ | ||
1284 | ce = get_entry(prev); | ||
1285 | if (ce->direntry == infop->fatfile.e.entry) | ||
1286 | { | ||
1287 | compp->prevp = &ce->next; | ||
1288 | continue; /* already there */ | ||
1289 | } | ||
1290 | } | ||
1291 | |||
1292 | int idx = create_entry(fatentp->name, &ce); | ||
1293 | if (!idx) | ||
1294 | { | ||
1295 | sabp->quit = true; | ||
1296 | break; | ||
1297 | } | ||
1298 | |||
1299 | /* link it in */ | ||
1300 | ce->up = compp->idx; | ||
1301 | ce->next = prev; | ||
1302 | *compp->prevp = idx; | ||
1303 | compp->prevp = &ce->next; | ||
1304 | |||
1305 | if (!(fatentp->attr & ATTR_DIRECTORY)) | ||
1306 | ce->filesize = fatentp->filesize; | ||
1307 | else if (!is_dotdir_name(fatentp->name)) | ||
1308 | ce->frontier = FRONTIER_NEW; /* this needs scanning */ | ||
1309 | |||
1310 | /* copy remaining FS info */ | ||
1311 | ce->direntry = infop->fatfile.e.entry; | ||
1312 | ce->direntries = infop->fatfile.e.entries; | ||
1313 | ce->attr = fatentp->attr; | ||
1314 | ce->firstcluster = fatentp->firstcluster; | ||
1315 | ce->wrtdate = fatentp->wrtdate; | ||
1316 | ce->wrttime = fatentp->wrttime; | ||
1317 | |||
1318 | /* resolve queued user bindings */ | ||
1319 | infop->fatfile.firstcluster = fatentp->firstcluster; | ||
1320 | infop->fatfile.dircluster = dircluster; | ||
1321 | infop->dcfile.idx = idx; | ||
1322 | infop->dcfile.serialnum = ce->serialnum; | ||
1323 | binding_resolve(infop); | ||
1324 | } /* end while */ | ||
1325 | |||
1326 | close_stream_internal(streamp); | ||
1327 | |||
1328 | if (sabp->quit) | ||
1329 | return; | ||
1330 | |||
1331 | establish_frontier(compp->idx, FRONTIER_SETTLED); | ||
1332 | |||
1333 | /* second pass: "recurse!" */ | ||
1334 | struct dircache_entry *ce = NULL; | ||
1335 | |||
1336 | while (1) | ||
1337 | { | ||
1338 | idx = compp->idx && compp > sabp->stack ? *compp->prevp : 0; | ||
1339 | if (idx) | ||
1340 | { | ||
1341 | ce = get_entry(idx); | ||
1342 | compp->prevp = &ce->next; | ||
1343 | |||
1344 | if (ce->frontier != FRONTIER_SETTLED) | ||
1345 | break; | ||
1346 | } | ||
1347 | else | ||
1348 | { | ||
1349 | /* directory completed or removed/deepest level */ | ||
1350 | compp = ++sabp->top; | ||
1351 | if (compp >= sabp->stackend) | ||
1352 | return; /* scan completed/initial directory removed */ | ||
1353 | } | ||
1354 | } | ||
1355 | |||
1356 | /* even if it got zoned from outside it is about to be scanned in | ||
1357 | its entirety and may be considered new again */ | ||
1358 | ce->frontier = FRONTIER_NEW; | ||
1359 | downp = &ce->down; | ||
1360 | |||
1361 | /* set up info for next open | ||
1362 | * IF_MV: "volume" was set when scan began */ | ||
1363 | infop->fatfile.firstcluster = ce->firstcluster; | ||
1364 | infop->fatfile.dircluster = dircluster; | ||
1365 | infop->fatfile.e.entry = ce->direntry; | ||
1366 | infop->fatfile.e.entries = ce->direntries; | ||
1367 | } /* end while */ | ||
1368 | } | ||
1369 | |||
1370 | /** | ||
1371 | * scan and build the contents of a directory or volume root | ||
1372 | */ | ||
1373 | static void sab_process_dir(struct file_base_info *infop, bool issab) | ||
1374 | { | ||
1375 | /* infop should have been fully opened meaning that all its parent | ||
1376 | directory information is filled in and intact; the binding information | ||
1377 | should also filled in beforehand */ | ||
1378 | |||
1379 | /* allocate the stack right now to the max demand */ | ||
1380 | struct dirsab | ||
1381 | { | ||
1382 | struct sab sab; | ||
1383 | struct sab_component stack[issab ? DIRCACHE_MAX_DEPTH : 1]; | ||
1384 | } dirsab; | ||
1385 | struct sab *sabp = &dirsab.sab; | ||
1386 | |||
1387 | sabp->quit = false; | ||
1388 | sabp->stackend = &sabp->stack[ARRAYLEN(dirsab.stack)]; | ||
1389 | sabp->top = sabp->stackend; | ||
1390 | sabp->info = *infop; | ||
1391 | |||
1392 | if (issab) | ||
1393 | DCRIVOL(infop)->sabp = sabp; | ||
1394 | |||
1395 | establish_frontier(infop->dcfile.idx, FRONTIER_NEW | FRONTIER_RENEW); | ||
1396 | sab_process_sub(sabp); | ||
1397 | |||
1398 | if (issab) | ||
1399 | DCRIVOL(infop)->sabp = NULL; | ||
1400 | } | ||
1401 | |||
1402 | /** | ||
1403 | * scan and build the entire tree for a volume | ||
1404 | */ | ||
1405 | static void sab_process_volume(struct dircache_volume *dcvolp) | ||
1406 | { | ||
1407 | int rc; | ||
1408 | |||
1409 | int volume = IF_MV_VOL(dcvolp - dircache.dcvol); | ||
1410 | int idx = -volume - 1; | ||
1411 | |||
1412 | logf("dircache - building volume %d", volume); | ||
1413 | |||
1414 | /* gather everything sab_process_dir() needs in order to begin a scan */ | ||
1415 | struct file_base_info info; | ||
1416 | rc = fat_open_rootdir(IF_MV(volume,) &info.fatfile); | ||
1417 | if (rc < 0) | ||
1418 | { | ||
1419 | /* probably not mounted */ | ||
1420 | logf("SAB - no root %d: %d", volume, rc); | ||
1421 | establish_frontier(idx, FRONTIER_NEW); | ||
1422 | return; | ||
311 | } | 1423 | } |
312 | 1424 | ||
313 | /* first pass : read dir */ | 1425 | info.dcfile.idx = idx; |
314 | struct dircache_entry *first_ce = ce; | 1426 | info.dcfile.serialnum = dcvolp->serialnum; |
315 | 1427 | sab_process_dir(&info, true); | |
316 | /* read through directory */ | 1428 | } |
317 | while((rc = fat_getnext(sab.dir, sab.direntry)) >= 0 && sab.direntry->name[0]) | 1429 | |
1430 | /** | ||
1431 | * this function is the back end to the public API's like readdir() | ||
1432 | */ | ||
1433 | int dircache_readdir_dirent(struct filestr_base *stream, | ||
1434 | struct dirscan_info *scanp, | ||
1435 | struct dirent *entry) | ||
1436 | { | ||
1437 | struct file_base_info *dirinfop = stream->infop; | ||
1438 | |||
1439 | if (!dirinfop->dcfile.serialnum) | ||
1440 | goto read_uncached; /* no parent cached => no entries cached */ | ||
1441 | |||
1442 | struct dircache_volume *dcvolp = DCVOL(dirinfop); | ||
1443 | |||
1444 | int diridx = dirinfop->dcfile.idx; | ||
1445 | unsigned int frontier = diridx <= 0 ? dcvolp->frontier : | ||
1446 | get_entry(diridx)->frontier; | ||
1447 | |||
1448 | /* if not settled, just readthrough; no binding information is needed for | ||
1449 | this; if it becomes settled, we'll get scan->dcfile caught up and do | ||
1450 | subsequent reads with the cache */ | ||
1451 | if (frontier != FRONTIER_SETTLED) | ||
1452 | goto read_uncached; | ||
1453 | |||
1454 | int idx = scanp->dcscan.idx; | ||
1455 | struct dircache_entry *ce; | ||
1456 | |||
1457 | unsigned int direntry = scanp->fatscan.entry; | ||
1458 | while (1) | ||
318 | { | 1459 | { |
319 | if(!strcmp(".", sab.direntry->name) || | 1460 | if (idx == 0 || direntry == FAT_RW_VAL) /* rewound? */ |
320 | !strcmp("..", sab.direntry->name)) | 1461 | { |
321 | continue; | 1462 | idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down; |
1463 | break; | ||
1464 | } | ||
322 | 1465 | ||
323 | size_t size = strlen(sab.direntry->name) + 1; | 1466 | /* validate last entry scanned; it might have been replaced between |
324 | ce->d_name = (d_names_start -= size); | 1467 | calls or not there at all any more; if so, start the cache reader |
325 | ce->startcluster = sab.direntry->firstcluster; | 1468 | at the beginning and fast-forward to the correct point as indicated |
326 | ce->info.size = sab.direntry->filesize; | 1469 | by the FS scanner */ |
327 | ce->info.attribute = sab.direntry->attr; | 1470 | ce = get_entry(idx); |
328 | ce->info.wrtdate = sab.direntry->wrtdate; | 1471 | if (ce && ce->serialnum == scanp->dcscan.serialnum) |
329 | ce->info.wrttime = sab.direntry->wrttime; | 1472 | { |
1473 | idx = ce->next; | ||
1474 | break; | ||
1475 | } | ||
330 | 1476 | ||
331 | strcpy(ce->d_name, sab.direntry->name); | 1477 | idx = 0; |
332 | dircache_size += size; | 1478 | } |
333 | 1479 | ||
334 | if(ce->info.attribute & FAT_ATTR_DIRECTORY) | 1480 | while (1) |
335 | dircache_gen_down(ce); | 1481 | { |
336 | 1482 | ce = get_entry(idx); | |
337 | ce = dircache_gen_next(ce); | 1483 | if (!ce) |
338 | if(ce == NULL) | ||
339 | return -5; | ||
340 | |||
341 | /* When simulator is used, it's only safe to yield here. */ | ||
342 | if(thread_enabled) | ||
343 | { | 1484 | { |
344 | /* Stop if we got an external signal. */ | 1485 | empty_dirent(entry); |
345 | if(check_event_queue()) | 1486 | scanp->fatscan.entries = 0; |
346 | return -6; | 1487 | return 0; /* end of dir */ |
347 | yield(); | ||
348 | } | 1488 | } |
1489 | |||
1490 | if (ce->direntry > direntry || direntry == FAT_RW_VAL) | ||
1491 | break; /* cache reader is caught up to FS scan */ | ||
1492 | |||
1493 | idx = ce->next; | ||
349 | } | 1494 | } |
350 | 1495 | ||
351 | /* add "." and ".." */ | 1496 | /* basic dirent information */ |
352 | ce->d_name = dot; | 1497 | entry_name_copy(entry->d_name, ce); |
353 | ce->info.attribute = FAT_ATTR_DIRECTORY; | 1498 | entry->info.attr = ce->attr; |
354 | ce->startcluster = startcluster; | 1499 | entry->info.size = (ce->attr & ATTR_DIRECTORY) ? 0 : ce->filesize; |
355 | ce->info.size = 0; | 1500 | entry->info.wrtdate = ce->wrtdate; |
356 | ce->down = first_ce; | 1501 | entry->info.wrttime = ce->wrttime; |
357 | 1502 | ||
358 | ce = dircache_gen_next(ce); | 1503 | /* FS scan information */ |
359 | 1504 | scanp->fatscan.entry = ce->direntry; | |
360 | ce->d_name = dotdot; | 1505 | scanp->fatscan.entries = ce->direntries; |
361 | ce->info.attribute = FAT_ATTR_DIRECTORY; | 1506 | |
362 | ce->startcluster = (first_ce->up ? first_ce->up->startcluster : 0); | 1507 | /* dircache scan information */ |
363 | ce->info.size = 0; | 1508 | scanp->dcscan.idx = idx; |
364 | ce->down = first_ce->up; | 1509 | scanp->dcscan.serialnum = ce->serialnum; |
365 | 1510 | ||
366 | /* second pass: recurse ! */ | 1511 | /* return whether this needs decoding */ |
367 | ce = first_ce; | 1512 | int rc = ce->direntries == 1 ? 2 : 1; |
368 | 1513 | ||
369 | while(rc >= 0 && ce) | 1514 | yield(); |
370 | { | ||
371 | if(ce->d_name != NULL && ce->down != NULL && strcmp(ce->d_name, ".") | ||
372 | && strcmp(ce->d_name, "..")) | ||
373 | rc = sab_process_dir(ce->startcluster, ce->down); | ||
374 | |||
375 | ce = ce->next; | ||
376 | } | ||
377 | |||
378 | return rc; | 1515 | return rc; |
1516 | |||
1517 | read_uncached: | ||
1518 | dircache_dcfile_init(&scanp->dcscan); | ||
1519 | return uncached_readdir_dirent(stream, scanp, entry); | ||
379 | } | 1520 | } |
380 | 1521 | ||
381 | /* used during the generation */ | 1522 | /** |
382 | static struct fat_dir sab_fat_dir; | 1523 | * rewind the directory scan cursor |
1524 | */ | ||
1525 | void dircache_rewinddir_dirent(struct dirscan_info *scanp) | ||
1526 | { | ||
1527 | uncached_rewinddir_dirent(scanp); | ||
1528 | dircache_dcfile_init(&scanp->dcscan); | ||
1529 | } | ||
383 | 1530 | ||
384 | static int dircache_scan_and_build(IF_MV(int volume,) struct dircache_entry *ce) | 1531 | /** |
1532 | * this function is the back end to file API internal scanning, which requires | ||
1533 | * much more detail about the directory entries; this is allowed to make | ||
1534 | * assumptions about cache state because the cache will not be altered during | ||
1535 | * the scan process; an additional important property of internal scanning is | ||
1536 | * that any available binding information is not ignored even when a scan | ||
1537 | * directory is frontier zoned. | ||
1538 | */ | ||
1539 | int dircache_readdir_internal(struct filestr_base *stream, | ||
1540 | struct file_base_info *infop, | ||
1541 | struct fat_direntry *fatent) | ||
385 | { | 1542 | { |
386 | memset(ce, 0, sizeof(struct dircache_entry)); | 1543 | /* call with writer exclusion */ |
1544 | struct file_base_info *dirinfop = stream->infop; | ||
1545 | struct dircache_volume *dcvolp = DCVOL(dirinfop); | ||
387 | 1546 | ||
388 | #ifdef HAVE_MULTIVOLUME | 1547 | /* assume binding "not found" */ |
389 | if (volume > 0) | 1548 | infop->dcfile.serialnum = 0; |
390 | { | 1549 | |
391 | /* broken for 100+ volumes because the format string is too small | 1550 | /* is parent cached? if not, readthrough because nothing is here yet */ |
392 | * and we use that for size calculation */ | 1551 | if (!dirinfop->dcfile.serialnum) |
393 | const size_t max_len = VOL_ENUM_POS + 3; | 1552 | return uncached_readdir_internal(stream, infop, fatent); |
394 | ce->d_name = (d_names_start -= max_len); | 1553 | |
395 | snprintf(ce->d_name, max_len, VOL_NAMES, volume); | 1554 | int diridx = dirinfop->dcfile.idx; |
396 | dircache_size += max_len; | 1555 | unsigned int frontier = diridx < 0 ? |
397 | ce->info.attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME; | 1556 | dcvolp->frontier : get_entry(diridx)->frontier; |
398 | ce->info.size = 0; | 1557 | |
399 | append_position = dircache_gen_next(ce); | 1558 | int idx = infop->dcfile.idx; |
400 | ce = dircache_gen_down(ce); | 1559 | if (idx == 0) /* rewound? */ |
1560 | idx = diridx <= 0 ? dcvolp->root_down : get_entry(diridx)->down; | ||
1561 | else | ||
1562 | idx = get_entry(idx)->next; | ||
1563 | |||
1564 | struct dircache_entry *ce = get_entry(idx); | ||
1565 | if (frontier != FRONTIER_SETTLED) | ||
1566 | { | ||
1567 | /* the directory being read is reported to be incompletely cached; | ||
1568 | readthrough and if the entry exists, return it with its binding | ||
1569 | information; otherwise return the uncached read result while | ||
1570 | maintaining the last index */ | ||
1571 | int rc = uncached_readdir_internal(stream, infop, fatent); | ||
1572 | if (rc <= 0 || !ce || ce->direntry > infop->fatfile.e.entry) | ||
1573 | return rc; | ||
1574 | |||
1575 | /* entry matches next one to read */ | ||
1576 | } | ||
1577 | else if (!ce) | ||
1578 | { | ||
1579 | /* end of dir */ | ||
1580 | fat_empty_fat_direntry(fatent); | ||
1581 | infop->fatfile.e.entries = 0; | ||
1582 | return 0; | ||
401 | } | 1583 | } |
402 | #endif | ||
403 | 1584 | ||
404 | struct fat_direntry direntry; /* ditto */ | 1585 | /* FS entry information that we maintain */ |
405 | #ifdef HAVE_MULTIVOLUME | 1586 | entry_name_copy(fatent->name, ce); |
406 | sab.volume = volume; | 1587 | fatent->shortname[0] = '\0'; |
407 | #endif | 1588 | fatent->attr = ce->attr; |
408 | sab.dir = &sab_fat_dir; | 1589 | /* file code file scanning does not need time information */ |
409 | sab.direntry = &direntry; | 1590 | fatent->filesize = (ce->attr & ATTR_DIRECTORY) ? 0 : ce->filesize; |
410 | 1591 | fatent->firstcluster = ce->firstcluster; | |
411 | return sab_process_dir(0, ce); | 1592 | |
1593 | /* FS entry directory information */ | ||
1594 | infop->fatfile.e.entry = ce->direntry; | ||
1595 | infop->fatfile.e.entries = ce->direntries; | ||
1596 | |||
1597 | /* dircache file binding information */ | ||
1598 | infop->dcfile.idx = idx; | ||
1599 | infop->dcfile.serialnum = ce->serialnum; | ||
1600 | |||
1601 | /* return whether this needs decoding */ | ||
1602 | int rc = ce->direntries == 1 ? 2 : 1; | ||
1603 | |||
1604 | if (frontier == FRONTIER_SETTLED) | ||
1605 | { | ||
1606 | static long next_yield; | ||
1607 | if (TIME_AFTER(current_tick, next_yield)) | ||
1608 | { | ||
1609 | yield(); | ||
1610 | next_yield = current_tick + HZ/50; | ||
1611 | } | ||
1612 | } | ||
1613 | |||
1614 | return rc; | ||
412 | } | 1615 | } |
413 | #elif (CONFIG_PLATFORM & PLATFORM_HOSTED) /* PLATFORM_HOSTED */ | 1616 | |
1617 | /** | ||
1618 | * rewind the scan position for an internal scan | ||
1619 | */ | ||
1620 | void dircache_rewinddir_internal(struct file_base_info *infop) | ||
1621 | { | ||
1622 | uncached_rewinddir_internal(infop); | ||
1623 | dircache_dcfile_init(&infop->dcfile); | ||
1624 | } | ||
1625 | |||
1626 | #else /* !DIRCACHE_NATIVE (for all others) */ | ||
1627 | |||
1628 | ##################### | ||
1629 | /* we require access to the host functions */ | ||
1630 | #undef opendir | ||
1631 | #undef readdir | ||
1632 | #undef closedir | ||
1633 | #undef rewinddir | ||
1634 | |||
414 | static char sab_path[MAX_PATH]; | 1635 | static char sab_path[MAX_PATH]; |
415 | 1636 | ||
416 | static int sab_process_dir(struct dircache_entry *ce) | 1637 | static int sab_process_dir(struct dircache_entry *ce) |
417 | { | 1638 | { |
418 | struct dirent_uncached *entry; | 1639 | struct dirent_uncached *entry; |
419 | struct dircache_entry *first_ce = ce; | 1640 | struct dircache_entry *first_ce = ce; |
420 | DIR_UNCACHED *dir = opendir_uncached(sab_path); | 1641 | DIR *dir = opendir(sab_path); |
421 | if(dir == NULL) | 1642 | if(dir == NULL) |
422 | { | 1643 | { |
423 | logf("Failed to opendir_uncached(%s)", sab_path); | 1644 | logf("Failed to opendir_uncached(%s)", sab_path); |
424 | return -1; | 1645 | return -1; |
425 | } | 1646 | } |
426 | 1647 | ||
427 | while((entry = readdir_uncached(dir))) | 1648 | while (1) |
428 | { | 1649 | { |
429 | if(!strcmp(".", entry->d_name) || | 1650 | if (!(entry = readdir(dir))) |
430 | !strcmp("..", entry->d_name)) | 1651 | break; |
1652 | |||
1653 | if (IS_DOTDIR_NAME(entry->d_name)) | ||
1654 | { | ||
1655 | /* add "." and ".." */ | ||
1656 | ce->info.attribute = ATTR_DIRECTORY; | ||
1657 | ce->info.size = 0; | ||
1658 | ce->down = entry->d_name[1] == '\0' ? first_ce : first_ce->up; | ||
1659 | strcpy(ce->dot_d_name, entry->d_name); | ||
431 | continue; | 1660 | continue; |
1661 | } | ||
432 | 1662 | ||
433 | size_t size = strlen(entry->d_name) + 1; | 1663 | size_t size = strlen(entry->d_name) + 1; |
434 | ce->d_name = (d_names_start -= size); | 1664 | ce->d_name = (d_names_start -= size); |
@@ -436,10 +1666,10 @@ static int sab_process_dir(struct dircache_entry *ce) | |||
436 | 1666 | ||
437 | strcpy(ce->d_name, entry->d_name); | 1667 | strcpy(ce->d_name, entry->d_name); |
438 | dircache_size += size; | 1668 | dircache_size += size; |
439 | 1669 | ||
440 | if(entry->info.attribute & ATTR_DIRECTORY) | 1670 | if(entry->info.attribute & ATTR_DIRECTORY) |
441 | { | 1671 | { |
442 | dircache_gen_down(ce); | 1672 | dircache_gen_down(ce, ce); |
443 | if(ce->down == NULL) | 1673 | if(ce->down == NULL) |
444 | { | 1674 | { |
445 | closedir_uncached(dir); | 1675 | closedir_uncached(dir); |
@@ -450,1189 +1680,1504 @@ static int sab_process_dir(struct dircache_entry *ce) | |||
450 | /* append entry */ | 1680 | /* append entry */ |
451 | strlcpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos); | 1681 | strlcpy(&sab_path[pathpos], "/", sizeof(sab_path) - pathpos); |
452 | strlcpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1); | 1682 | strlcpy(&sab_path[pathpos+1], entry->d_name, sizeof(sab_path) - pathpos - 1); |
453 | 1683 | ||
454 | int rc = sab_process_dir(ce->down); | 1684 | int rc = sab_process_dir(ce->down); |
455 | /* restore path */ | 1685 | /* restore path */ |
456 | sab_path[pathpos] = '\0'; | 1686 | sab_path[pathpos] = '\0'; |
457 | 1687 | ||
458 | if(rc < 0) | 1688 | if(rc < 0) |
459 | { | 1689 | { |
460 | closedir_uncached(dir); | 1690 | closedir_uncached(dir); |
461 | return rc; | 1691 | return rc; |
462 | } | 1692 | } |
463 | } | 1693 | } |
464 | 1694 | ||
465 | ce = dircache_gen_next(ce); | 1695 | ce = dircache_gen_entry(ce); |
466 | if(ce == NULL) | 1696 | if (ce == NULL) |
467 | return -5; | 1697 | return -5; |
468 | 1698 | ||
469 | /* When simulator is used, it's only safe to yield here. */ | 1699 | yield(); |
470 | if(thread_enabled) | ||
471 | { | ||
472 | /* Stop if we got an external signal. */ | ||
473 | if(check_event_queue()) | ||
474 | return -1; | ||
475 | yield(); | ||
476 | } | ||
477 | } | 1700 | } |
478 | 1701 | ||
479 | /* add "." and ".." */ | ||
480 | ce->d_name = dot; | ||
481 | ce->info.attribute = ATTR_DIRECTORY; | ||
482 | ce->info.size = 0; | ||
483 | ce->down = first_ce; | ||
484 | |||
485 | ce = dircache_gen_next(ce); | ||
486 | |||
487 | ce->d_name = dotdot; | ||
488 | ce->info.attribute = ATTR_DIRECTORY; | ||
489 | ce->info.size = 0; | ||
490 | ce->down = first_ce->up; | ||
491 | |||
492 | closedir_uncached(dir); | 1702 | closedir_uncached(dir); |
493 | return 0; | 1703 | return 1; |
494 | } | 1704 | } |
495 | 1705 | ||
496 | static int dircache_scan_and_build(IF_MV(int volume,) struct dircache_entry *ce) | 1706 | static int sab_process_volume(IF_MV(int volume,) struct dircache_entry *ce) |
497 | { | 1707 | { |
498 | #ifdef HAVE_MULTIVOLUME | ||
499 | (void) volume; | ||
500 | #endif | ||
501 | memset(ce, 0, sizeof(struct dircache_entry)); | 1708 | memset(ce, 0, sizeof(struct dircache_entry)); |
502 | |||
503 | strlcpy(sab_path, "/", sizeof sab_path); | 1709 | strlcpy(sab_path, "/", sizeof sab_path); |
504 | return sab_process_dir(ce); | 1710 | return sab_process_dir(ce); |
505 | } | 1711 | } |
506 | #endif /* PLATFORM_NATIVE */ | ||
507 | 1712 | ||
508 | /** | 1713 | int dircache_readdir_r(struct dircache_dirscan *dir, struct dirent *result) |
509 | * Internal function to get a pointer to dircache_entry for a given filename. | 1714 | { |
510 | * path: Absolute path to a file or directory (see comment) | 1715 | if (dircache_state != DIRCACHE_READY) |
511 | * go_down: Returns the first entry of the directory given by the path (see comment) | 1716 | return readdir_r(dir->###########3, result, &result); |
512 | * | ||
513 | * As a a special case, accept path="" as an alias for "/". | ||
514 | * Also if the path omits the first '/', it will be accepted. | ||
515 | * | ||
516 | * * If get_down=true: | ||
517 | * If path="/", the returned entry is the first of root directory (ie dircache_root) | ||
518 | * Otherwise, if 'entry' is the returned value when get_down=false, | ||
519 | * the functions returns entry->down (which can be NULL) | ||
520 | * | ||
521 | * * If get_down=false: | ||
522 | * If path="/chunk_1/chunk_2/.../chunk_n" then this functions returns the entry | ||
523 | * root_entry()->chunk_1->chunk_2->...->chunk_(n-1) | ||
524 | * Which means that | ||
525 | * dircache_get_entry(path)->d_name == chunk_n | ||
526 | * | ||
527 | * If path="/", the returned entry is NULL. | ||
528 | * If the entry doesn't exist, return NULL | ||
529 | * | ||
530 | * NOTE: this functions silently handles double '/' | ||
531 | */ | ||
532 | static struct dircache_entry* dircache_get_entry(const char *path, bool go_down) | ||
533 | { | ||
534 | char namecopy[MAX_PATH]; | ||
535 | char* part; | ||
536 | char* end; | ||
537 | |||
538 | bool at_root = true; | ||
539 | struct dircache_entry *cache_entry = dircache_root; | ||
540 | |||
541 | strlcpy(namecopy, path, sizeof(namecopy)); | ||
542 | |||
543 | for(part = strtok_r(namecopy, "/", &end); part; part = strtok_r(NULL, "/", &end)) | ||
544 | { | ||
545 | /* If request another chunk, the current entry has to be directory | ||
546 | * and so cache_entry->down has to be non-NULL/ | ||
547 | * Special case of root because it's already the first entry of the root directory | ||
548 | * | ||
549 | * NOTE: this is safe even if cache_entry->down is NULL */ | ||
550 | if(!at_root) | ||
551 | cache_entry = cache_entry->down; | ||
552 | else | ||
553 | at_root = false; | ||
554 | |||
555 | /* scan dir for name */ | ||
556 | while(cache_entry != NULL) | ||
557 | { | ||
558 | /* skip unused entries */ | ||
559 | if(cache_entry->d_name == NULL) | ||
560 | { | ||
561 | cache_entry = cache_entry->next; | ||
562 | continue; | ||
563 | } | ||
564 | /* compare names */ | ||
565 | if(!strcasecmp(part, cache_entry->d_name)) | ||
566 | break; | ||
567 | /* go to next entry */ | ||
568 | cache_entry = cache_entry->next; | ||
569 | } | ||
570 | |||
571 | /* handle not found case */ | ||
572 | if(cache_entry == NULL) | ||
573 | return NULL; | ||
574 | } | ||
575 | 1717 | ||
576 | /* NOTE: here cache_entry!=NULL so taking ->down is safe */ | 1718 | bool first = dir->dcinfo.scanidx == REWIND_INDEX; |
577 | if(go_down) | 1719 | struct dircache_entry *ce = get_entry(first ? dir->dcinfo.index : |
578 | return at_root ? cache_entry : cache_entry->down; | 1720 | dir->dcinfo.scanidx); |
579 | else | ||
580 | return at_root ? NULL : cache_entry; | ||
581 | } | ||
582 | 1721 | ||
583 | #ifdef HAVE_EEPROM_SETTINGS | 1722 | ce = first ? ce->down : ce->next; |
584 | 1723 | ||
585 | #define DIRCACHE_MAGIC 0x00d0c0a1 | 1724 | if (ce == NULL) |
586 | struct dircache_maindata { | 1725 | return 0; |
587 | long magic; | 1726 | |
588 | long size; | 1727 | dir->scanidx = ce - dircache_root; |
589 | long entry_count; | 1728 | |
590 | long appflags; | 1729 | strlcpy(result->d_name, ce->d_name, sizeof (result->d_name)); |
591 | struct dircache_entry *root_entry; | 1730 | result->info = ce->dirinfo; |
592 | char *d_names_start; | 1731 | |
593 | }; | 1732 | return 1; |
1733 | } | ||
1734 | |||
1735 | #endif /* DIRCACHE_* */ | ||
594 | 1736 | ||
595 | /** | 1737 | /** |
596 | * Function to load the internal cache structure from disk to initialize | 1738 | * reset the cache for the specified volume |
597 | * the dircache really fast and little disk access. | ||
598 | */ | 1739 | */ |
599 | int dircache_load(void) | 1740 | static void reset_volume(IF_MV_NONVOID(int volume)) |
600 | { | 1741 | { |
601 | struct dircache_maindata maindata; | 1742 | FOR_EACH_VOLUME(volume, i) |
602 | ssize_t bytes_read; | ||
603 | int fd; | ||
604 | |||
605 | if (dircache_initialized) | ||
606 | return -1; | ||
607 | |||
608 | logf("Loading directory cache"); | ||
609 | dircache_size = 0; | ||
610 | |||
611 | fd = open_dircache_file(O_RDONLY, 0); | ||
612 | if (fd < 0) | ||
613 | return -2; | ||
614 | |||
615 | bytes_read = read(fd, &maindata, sizeof(struct dircache_maindata)); | ||
616 | if (bytes_read != sizeof(struct dircache_maindata) | ||
617 | || maindata.magic != DIRCACHE_MAGIC || maindata.size <= 0) | ||
618 | { | 1743 | { |
619 | logf("Dircache file header error"); | 1744 | struct dircache_volume *dcvolp = DCVOL(i); |
620 | close(fd); | 1745 | |
621 | remove_dircache_file(); | 1746 | if (dcvolp->status == DIRCACHE_IDLE) |
622 | return -3; | 1747 | continue; /* idle => nothing happening there */ |
623 | } | 1748 | |
624 | 1749 | struct dircache_runinfo_volume *dcrivolp = DCRIVOL(i); | |
625 | allocated_size = maindata.size + DIRCACHE_RESERVE; | 1750 | |
626 | dircache_handle = core_alloc_ex("dircache", allocated_size, &ops); | 1751 | /* stop any scan and build on this one */ |
627 | /* block movement during upcoming I/O */ | 1752 | if (dcrivolp->sabp) |
628 | dont_move = true; | 1753 | dcrivolp->sabp->quit = true; |
629 | dircache_root = core_get_data(dircache_handle); | 1754 | |
630 | ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*)); | 1755 | #ifdef HAVE_MULTIVOLUME |
631 | entry_count = maindata.entry_count; | 1756 | /* if this call is for all volumes, subsequent code will just reset |
632 | appflags = maindata.appflags; | 1757 | the cache memory usage and the freeing of individual entries may |
633 | 1758 | be skipped */ | |
634 | /* read the dircache file into memory, | 1759 | if (volume >= 0) |
635 | * start with the struct dircache_entries */ | 1760 | free_subentries(NULL, &dcvolp->root_down); |
636 | ssize_t bytes_to_read = entry_count*sizeof(struct dircache_entry); | 1761 | else |
637 | bytes_read = read(fd, dircache_root, bytes_to_read); | 1762 | #endif |
638 | 1763 | binding_dissolve_volume(dcrivolp); | |
639 | if (bytes_read != bytes_to_read) | 1764 | |
640 | { | 1765 | /* set it back to unscanned */ |
641 | logf("Dircache read failed #1"); | 1766 | dcvolp->status = DIRCACHE_IDLE; |
642 | return -6; | 1767 | dcvolp->frontier = FRONTIER_NEW; |
643 | } | 1768 | dcvolp->root_down = 0; |
644 | 1769 | dcvolp->build_ticks = 0; | |
645 | /* continue with the d_names. Fix up pointers to them if needed */ | 1770 | dcvolp->serialnum = 0; |
646 | bytes_to_read = maindata.size - bytes_to_read; | ||
647 | d_names_start = (char*)dircache_root + allocated_size - bytes_to_read; | ||
648 | bytes_read = read(fd, d_names_start, bytes_to_read); | ||
649 | close(fd); | ||
650 | remove_dircache_file(); | ||
651 | if (bytes_read != bytes_to_read) | ||
652 | { | ||
653 | logf("Dircache read failed #2"); | ||
654 | return -7; | ||
655 | } | 1771 | } |
1772 | } | ||
656 | 1773 | ||
657 | d_names_end = d_names_start + bytes_read; | 1774 | /** |
658 | dot = d_names_end - sizeof("."); | 1775 | * reset the entire cache state for all volumes |
659 | dotdot = dot - sizeof(".."); | 1776 | */ |
1777 | static void reset_cache(void) | ||
1778 | { | ||
1779 | if (!dircache_runinfo.handle) | ||
1780 | return; /* no buffer => nothing cached */ | ||
1781 | |||
1782 | /* blast all the volumes */ | ||
1783 | reset_volume(IF_MV(-1)); | ||
1784 | |||
1785 | #ifdef DIRCACHE_DUMPSTER | ||
1786 | dumpster_clean_buffer(dircache_runinfo.p + ENTRYSIZE, | ||
1787 | dircache_runinfo.bufsize); | ||
1788 | #endif /* DIRCACHE_DUMPSTER */ | ||
1789 | |||
1790 | /* reset the memory */ | ||
1791 | dircache.free_list = 0; | ||
1792 | dircache.size = 0; | ||
1793 | dircache.sizeused = 0; | ||
1794 | dircache.numentries = 0; | ||
1795 | dircache.names = dircache_runinfo.bufsize + ENTRYSIZE; | ||
1796 | dircache.sizenames = 0; | ||
1797 | dircache.namesfree = 0; | ||
1798 | dircache.nextnamefree = 0; | ||
1799 | *get_name(dircache.names - 1) = 0; | ||
1800 | /* dircache.last_serialnum stays */ | ||
1801 | /* dircache.reserve_used stays */ | ||
1802 | /* dircache.last_size stays */ | ||
1803 | } | ||
660 | 1804 | ||
661 | /* d_names are in reverse order, so the last entry points to the first string */ | 1805 | /** |
662 | ptrdiff_t offset_d_names = maindata.d_names_start - d_names_start; | 1806 | * checks each "idle" volume and builds it |
663 | ptrdiff_t offset_entries = maindata.root_entry - dircache_root; | 1807 | */ |
664 | offset_entries *= sizeof(struct dircache_entry); /* make it bytes */ | 1808 | static void build_volumes(void) |
1809 | { | ||
1810 | buffer_lock(); | ||
665 | 1811 | ||
666 | /* offset_entries is less likely to differ, so check if it's 0 in the loop | 1812 | for (int i = 0; i < NUM_VOLUMES; i++) |
667 | * offset_d_names however is almost always non-zero, since dircache_save() | ||
668 | * creates a file which causes the reserve buffer to be used. since | ||
669 | * we allocate a new, empty DIRCACHE_RESERVE here, the strings are | ||
670 | * farther behind */ | ||
671 | if (offset_entries != 0 || offset_d_names != 0) | ||
672 | { | 1813 | { |
673 | for(unsigned i = 0; i < entry_count; i++) | 1814 | /* this does reader locking but we already own that */ |
674 | { | 1815 | if (!volume_ismounted(IF_MV(i))) |
675 | if (dircache_root[i].d_name) | 1816 | continue; |
676 | dircache_root[i].d_name -= offset_d_names; | ||
677 | 1817 | ||
678 | if (offset_entries == 0) | 1818 | struct dircache_volume *dcvolp = DCVOL(i); |
679 | continue; | 1819 | |
680 | if (dircache_root[i].next_char) | 1820 | /* can't already be "scanning" because that's us; doesn't retry |
681 | dircache_root[i].next_char -= offset_entries; | 1821 | "ready" volumes */ |
682 | if (dircache_root[i].up_char) | 1822 | if (dcvolp->status == DIRCACHE_READY) |
683 | dircache_root[i].up_char -= offset_entries; | 1823 | continue; |
684 | if (dircache_root[i].down_char) | 1824 | |
685 | dircache_root[i].down_char -= offset_entries; | 1825 | /* measure how long it takes to build the cache for each volume */ |
686 | } | 1826 | if (!dcvolp->serialnum) |
1827 | dcvolp->serialnum = next_serialnum(); | ||
1828 | |||
1829 | dcvolp->status = DIRCACHE_SCANNING; | ||
1830 | dcvolp->start_tick = current_tick; | ||
1831 | |||
1832 | sab_process_volume(dcvolp); | ||
1833 | |||
1834 | if (dircache_runinfo.suspended) | ||
1835 | break; | ||
1836 | |||
1837 | /* whatever happened, it's ready unless reset */ | ||
1838 | dcvolp->build_ticks = current_tick - dcvolp->start_tick; | ||
1839 | dcvolp->status = DIRCACHE_READY; | ||
687 | } | 1840 | } |
688 | 1841 | ||
689 | /* Cache successfully loaded. */ | 1842 | size_t reserve_used = reserve_buf_used(); |
690 | dircache_size = maindata.size; | 1843 | if (reserve_used > dircache.reserve_used) |
691 | reserve_used = 0; | 1844 | dircache.reserve_used = reserve_used; |
692 | logf("Done, %ld KiB used", dircache_size / 1024); | 1845 | |
693 | dircache_initialized = true; | 1846 | if (DIRCACHE_STUFFED(reserve_used)) |
694 | memset(fd_bindings, 0, sizeof(fd_bindings)); | 1847 | dircache.last_size = 0; /* reset */ |
695 | dont_move = false; | 1848 | else if (dircache.size > dircache.last_size || |
1849 | dircache.last_size - dircache.size > DIRCACHE_RESERVE) | ||
1850 | dircache.last_size = dircache.size; | ||
1851 | |||
1852 | logf("Done, %ld KiB used", dircache.size / 1024); | ||
696 | 1853 | ||
697 | return 0; | 1854 | buffer_unlock(); |
698 | } | 1855 | } |
699 | 1856 | ||
700 | /** | 1857 | /** |
701 | * Function to save the internal cache stucture to disk for fast loading | 1858 | * allocate buffer and return whether or not a synchronous build should take |
702 | * on boot. | 1859 | * place; if 'realloced' is NULL, it's just a query about what will happen |
703 | */ | 1860 | */ |
704 | int dircache_save(void) | 1861 | static int prepare_build(bool *realloced) |
705 | { | 1862 | { |
706 | struct dircache_maindata maindata; | 1863 | /* called holding dircache lock */ |
707 | int fd; | 1864 | size_t size = dircache.last_size; |
708 | unsigned long bytes_written; | ||
709 | 1865 | ||
710 | remove_dircache_file(); | 1866 | #ifdef HAVE_EEPROM_SETTINGS |
711 | 1867 | if (realloced) | |
712 | if (!dircache_initialized) | 1868 | { |
713 | return -1; | 1869 | dircache_unlock(); |
1870 | remove_dircache_file(); | ||
1871 | dircache_lock(); | ||
714 | 1872 | ||
715 | logf("Saving directory cache"); | 1873 | if (dircache_runinfo.suspended) |
716 | dont_move = true; | 1874 | return -1; |
717 | fd = open_dircache_file(O_WRONLY | O_CREAT | O_TRUNC, 0666); | 1875 | } |
1876 | #endif /* HAVE_EEPROM_SETTINGS */ | ||
1877 | |||
1878 | bool stuffed = DIRCACHE_STUFFED(dircache.reserve_used); | ||
1879 | if (dircache_runinfo.bufsize > size && !stuffed) | ||
1880 | { | ||
1881 | if (realloced) | ||
1882 | *realloced = false; | ||
718 | 1883 | ||
719 | maindata.magic = DIRCACHE_MAGIC; | 1884 | return 0; /* start a transparent rebuild */ |
720 | maindata.size = dircache_size; | 1885 | } |
721 | maindata.root_entry = dircache_root; | ||
722 | maindata.d_names_start = d_names_start; | ||
723 | maindata.entry_count = entry_count; | ||
724 | maindata.appflags = appflags; | ||
725 | 1886 | ||
726 | /* Save the info structure */ | 1887 | int syncbuild = size > 0 && !stuffed ? 0 : 1; |
727 | bytes_written = write(fd, &maindata, sizeof(struct dircache_maindata)); | 1888 | |
728 | if (bytes_written != sizeof(struct dircache_maindata)) | 1889 | if (!realloced) |
1890 | return syncbuild; | ||
1891 | |||
1892 | if (syncbuild) | ||
729 | { | 1893 | { |
730 | close(fd); | 1894 | /* start a non-transparent rebuild */ |
731 | logf("dircache: write failed #1"); | 1895 | /* we'll use the entire audiobuf to allocate the dircache */ |
732 | return -2; | 1896 | size = audio_buffer_available() + dircache_runinfo.bufsize; |
1897 | /* try to allocate at least the min and no more than the limit */ | ||
1898 | size = MAX(DIRCACHE_MIN, MIN(size, DIRCACHE_LIMIT)); | ||
733 | } | 1899 | } |
1900 | else | ||
1901 | { | ||
1902 | /* start a transparent rebuild */ | ||
1903 | size = MAX(size, DIRCACHE_RESERVE) + DIRCACHE_RESERVE*2; | ||
1904 | } | ||
1905 | |||
1906 | *realloced = true; | ||
1907 | reset_cache(); | ||
734 | 1908 | ||
735 | /* Dump whole directory cache to disk | 1909 | buffer_lock(); |
736 | * start by writing the dircache_entries */ | 1910 | |
737 | size_t bytes_to_write = entry_count*sizeof(struct dircache_entry); | 1911 | int handle = reset_buffer(); |
738 | bytes_written = write(fd, dircache_root, bytes_to_write); | 1912 | dircache_unlock(); |
739 | if (bytes_written != bytes_to_write) | 1913 | |
1914 | if (handle > 0) | ||
1915 | core_free(handle); | ||
1916 | |||
1917 | handle = alloc_cache(size); | ||
1918 | |||
1919 | dircache_lock(); | ||
1920 | |||
1921 | if (dircache_runinfo.suspended && handle > 0) | ||
740 | { | 1922 | { |
741 | logf("dircache: write failed #2"); | 1923 | /* if we got suspended, don't keep this huge buffer around */ |
742 | return -3; | 1924 | dircache_unlock(); |
1925 | core_free(handle); | ||
1926 | handle = 0; | ||
1927 | dircache_lock(); | ||
743 | } | 1928 | } |
744 | 1929 | ||
745 | /* continue with the d_names */ | 1930 | if (handle <= 0) |
746 | bytes_to_write = d_names_end - d_names_start; | ||
747 | bytes_written = write(fd, d_names_start, bytes_to_write); | ||
748 | close(fd); | ||
749 | if (bytes_written != bytes_to_write) | ||
750 | { | 1931 | { |
751 | logf("dircache: write failed #3"); | 1932 | buffer_unlock(); |
752 | return -4; | 1933 | return -1; |
753 | } | 1934 | } |
754 | 1935 | ||
755 | dont_move = false; | 1936 | set_buffer(handle, size); |
756 | return 0; | 1937 | buffer_unlock(); |
1938 | |||
1939 | return syncbuild; | ||
757 | } | 1940 | } |
758 | #endif /* HAVE_EEPROM_SETTINGS */ | ||
759 | 1941 | ||
760 | /** | 1942 | /** |
761 | * Internal function which scans the disk and creates the dircache structure. | 1943 | * compact the dircache buffer after a successful full build |
762 | */ | 1944 | */ |
763 | static int dircache_do_rebuild(void) | 1945 | static void compact_cache(void) |
764 | { | 1946 | { |
765 | struct dircache_entry* root_entry; | 1947 | /* called holding dircache lock */ |
766 | unsigned int start_tick; | 1948 | if (dircache_runinfo.suspended) |
767 | int i; | 1949 | return; |
768 | |||
769 | /* Measure how long it takes build the cache. */ | ||
770 | start_tick = current_tick; | ||
771 | dircache_initializing = true; | ||
772 | appflags = 0; | ||
773 | 1950 | ||
774 | /* reset dircache and alloc root entry */ | 1951 | void *p = dircache_runinfo.p + ENTRYSIZE; |
775 | entry_count = 0; | 1952 | size_t leadsize = dircache.numentries * ENTRYSIZE + DIRCACHE_RESERVE; |
776 | root_entry = allocate_entry(); | ||
777 | dont_move = true; | ||
778 | 1953 | ||
779 | #ifdef HAVE_MULTIVOLUME | 1954 | void *dst = p + leadsize; |
780 | append_position = root_entry; | 1955 | void *src = get_name(dircache.names - 1); |
1956 | if (dst >= src) | ||
1957 | return; /* cache got bigger than expected; never mind that */ | ||
1958 | |||
1959 | /* slide the names up in memory */ | ||
1960 | memmove(dst, src, dircache.sizenames + 2); | ||
781 | 1961 | ||
782 | for (i = NUM_VOLUMES; i >= 0; i--) | 1962 | /* fix up name indexes */ |
1963 | ptrdiff_t offset = dst - src; | ||
1964 | |||
1965 | FOR_EACH_CACHE_ENTRY(ce) | ||
783 | { | 1966 | { |
784 | if (fat_ismounted(i)) | 1967 | if (!ce->tinyname) |
785 | { | 1968 | ce->name += offset; |
786 | #endif | ||
787 | cpu_boost(true); | ||
788 | #ifdef HAVE_MULTIVOLUME | ||
789 | if (dircache_scan_and_build(IF_MV(i,) append_position) < 0) | ||
790 | #else | ||
791 | if (dircache_scan_and_build(IF_MV(0,) root_entry) < 0) | ||
792 | #endif /* HAVE_MULTIVOLUME */ | ||
793 | { | ||
794 | logf("dircache_scan_and_build failed"); | ||
795 | cpu_boost(false); | ||
796 | dircache_size = 0; | ||
797 | dircache_initializing = false; | ||
798 | dont_move = false; | ||
799 | return -2; | ||
800 | } | ||
801 | cpu_boost(false); | ||
802 | #ifdef HAVE_MULTIVOLUME | ||
803 | } | ||
804 | } | 1969 | } |
805 | #endif | ||
806 | 1970 | ||
807 | logf("Done, %ld KiB used", dircache_size / 1024); | 1971 | dircache.names += offset; |
808 | |||
809 | dircache_initialized = true; | ||
810 | dircache_initializing = false; | ||
811 | cache_build_ticks = current_tick - start_tick; | ||
812 | |||
813 | /* Initialized fd bindings. */ | ||
814 | memset(fd_bindings, 0, sizeof(fd_bindings)); | ||
815 | for (i = 0; i < fdbind_idx; i++) | ||
816 | dircache_bind(fdbind_cache[i].fd, fdbind_cache[i].path); | ||
817 | fdbind_idx = 0; | ||
818 | |||
819 | if (thread_enabled) | ||
820 | { | ||
821 | if (allocated_size - dircache_size < DIRCACHE_RESERVE) | ||
822 | reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size); | ||
823 | } | ||
824 | |||
825 | dont_move = false; | ||
826 | return 1; | ||
827 | } | ||
828 | 1972 | ||
829 | /* | 1973 | /* assumes beelzelib doesn't do things like calling callbacks or changing |
830 | * Free all associated resources, if any */ | 1974 | the pointer as a result of the shrink operation; it doesn't as of now |
831 | static void dircache_free(void) | 1975 | but if it ever does that may very well cause deadlock problems since |
832 | { | 1976 | we're holding filesystem locks */ |
833 | if (dircache_handle > 0) | 1977 | size_t newsize = leadsize + dircache.sizenames + 1; |
834 | dircache_handle = core_free(dircache_handle); | 1978 | core_shrink(dircache_runinfo.handle, p, newsize + 1); |
835 | dircache_size = allocated_size = 0; | 1979 | dircache_runinfo.bufsize = newsize; |
1980 | dircache.reserve_used = 0; | ||
836 | } | 1981 | } |
837 | 1982 | ||
838 | /** | 1983 | /** |
839 | * Internal thread that controls transparent cache building. | 1984 | * internal thread that controls cache building; exits when no more requests |
1985 | * are pending or the cache is suspended | ||
840 | */ | 1986 | */ |
841 | static void dircache_thread(void) | 1987 | static void dircache_thread(void) |
842 | { | 1988 | { |
843 | struct queue_event ev; | 1989 | struct queue_event ev; |
844 | 1990 | ||
1991 | /* calls made within the loop reopen the lock */ | ||
1992 | dircache_lock(); | ||
1993 | |||
845 | while (1) | 1994 | while (1) |
846 | { | 1995 | { |
847 | queue_wait(&dircache_queue, &ev); | 1996 | queue_wait_w_tmo(&dircache_queue, &ev, 0); |
848 | 1997 | if (ev.id == SYS_TIMEOUT || dircache_runinfo.suspended) | |
849 | switch (ev.id) | ||
850 | { | 1998 | { |
851 | #ifdef HAVE_HOTSWAP | 1999 | /* nothing left to do/suspended */ |
852 | case SYS_FS_CHANGED: | 2000 | if (dircache_runinfo.suspended) |
853 | if (!dircache_initialized) | 2001 | clear_dircache_queue(); |
854 | break; | 2002 | dircache_runinfo.thread_done = true; |
855 | dircache_initialized = false; | 2003 | break; |
856 | #endif | ||
857 | case DIRCACHE_BUILD: | ||
858 | thread_enabled = true; | ||
859 | if (dircache_do_rebuild() < 0) | ||
860 | dircache_free(); | ||
861 | thread_enabled = false; | ||
862 | break ; | ||
863 | |||
864 | case DIRCACHE_STOP: | ||
865 | logf("Stopped the rebuilding."); | ||
866 | dircache_initialized = false; | ||
867 | break ; | ||
868 | |||
869 | case SYS_USB_CONNECTED: | ||
870 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
871 | usb_wait_for_disconnect(&dircache_queue); | ||
872 | break ; | ||
873 | } | 2004 | } |
874 | } | ||
875 | } | ||
876 | 2005 | ||
877 | static void generate_dot_d_names(void) | 2006 | /* background-only builds are not allowed if a synchronous build is |
878 | { | 2007 | required first; test what needs to be done and if it checks out, |
879 | dot = (d_names_start -= sizeof(".")); | 2008 | do it for real below */ |
880 | dotdot = (d_names_start -= sizeof("..")); | 2009 | int *rcp = (int *)ev.data; |
881 | dircache_size += sizeof(".") + sizeof(".."); | 2010 | |
882 | strcpy(dot, "."); | 2011 | if (!rcp && prepare_build(NULL) > 0) |
883 | strcpy(dotdot, ".."); | 2012 | continue; |
2013 | |||
2014 | bool realloced; | ||
2015 | int rc = prepare_build(&realloced); | ||
2016 | if (rcp) | ||
2017 | *rcp = rc; | ||
2018 | |||
2019 | if (rc < 0) | ||
2020 | continue; | ||
2021 | |||
2022 | trigger_cpu_boost(); | ||
2023 | build_volumes(); | ||
2024 | |||
2025 | /* if it was reallocated, compact it */ | ||
2026 | if (realloced) | ||
2027 | compact_cache(); | ||
2028 | } | ||
2029 | |||
2030 | dircache_unlock(); | ||
884 | } | 2031 | } |
885 | 2032 | ||
886 | /** | 2033 | /** |
887 | * Start scanning the disk to build the dircache. | 2034 | * post a scan and build message to the thread, starting it if required |
888 | * Either transparent or non-transparent build method is used. | ||
889 | */ | 2035 | */ |
890 | int dircache_build(int last_size) | 2036 | static bool dircache_thread_post(int volatile *rcp) |
891 | { | 2037 | { |
892 | if (dircache_initialized || thread_enabled) | 2038 | if (dircache_runinfo.thread_done) |
893 | return -3; | 2039 | { |
2040 | /* mustn't recreate until it exits so that the stack isn't reused */ | ||
2041 | thread_wait(dircache_runinfo.thread_id); | ||
2042 | dircache_runinfo.thread_done = false; | ||
2043 | dircache_runinfo.thread_id = create_thread( | ||
2044 | dircache_thread, dircache_stack, sizeof (dircache_stack), 0, | ||
2045 | dircache_thread_name IF_PRIO(, PRIORITY_BACKGROUND) | ||
2046 | IF_COP(, CPU)); | ||
2047 | } | ||
894 | 2048 | ||
895 | logf("Building directory cache"); | 2049 | bool started = dircache_runinfo.thread_id != 0; |
896 | #ifdef HAVE_EEPROM_SETTINGS | ||
897 | remove_dircache_file(); | ||
898 | #endif | ||
899 | 2050 | ||
900 | /* Background build, dircache has been previously allocated and */ | 2051 | if (started) |
901 | if (allocated_size > MAX(last_size, 0)) | 2052 | queue_post(&dircache_queue, DCM_BUILD, (intptr_t)rcp); |
902 | { | ||
903 | d_names_start = d_names_end; | ||
904 | dircache_size = 0; | ||
905 | reserve_used = 0; | ||
906 | thread_enabled = true; | ||
907 | dircache_initializing = true; | ||
908 | generate_dot_d_names(); | ||
909 | |||
910 | queue_post(&dircache_queue, DIRCACHE_BUILD, 0); | ||
911 | return 2; | ||
912 | } | ||
913 | |||
914 | /* start by freeing, as we possibly re-allocate */ | ||
915 | dircache_free(); | ||
916 | |||
917 | if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT ) | ||
918 | { | ||
919 | allocated_size = last_size + DIRCACHE_RESERVE; | ||
920 | dircache_handle = core_alloc_ex("dircache", allocated_size, &ops); | ||
921 | dircache_root = core_get_data(dircache_handle); | ||
922 | ALIGN_BUFFER(dircache_root, allocated_size, sizeof(struct dircache_entry*)); | ||
923 | d_names_start = d_names_end = ((char*)dircache_root)+allocated_size-1; | ||
924 | dircache_size = 0; | ||
925 | thread_enabled = true; | ||
926 | generate_dot_d_names(); | ||
927 | |||
928 | /* Start a transparent rebuild. */ | ||
929 | queue_post(&dircache_queue, DIRCACHE_BUILD, 0); | ||
930 | return 3; | ||
931 | } | ||
932 | |||
933 | /* We'll use the entire audiobuf to allocate the dircache | ||
934 | * struct dircache_entrys are allocated from the beginning | ||
935 | * and their corresponding d_name from the end | ||
936 | * after generation the buffer will be compacted with DIRCACHE_RESERVE | ||
937 | * free bytes inbetween */ | ||
938 | size_t available = audio_buffer_available(); | ||
939 | /* try to allocate at least 1MB, the more the better */ | ||
940 | if (available < 1<<20) available = 1<<20; | ||
941 | if (available > DIRCACHE_LIMIT) available = DIRCACHE_LIMIT; | ||
942 | |||
943 | dircache_handle = core_alloc_ex("dircache", available, &ops); | ||
944 | if (dircache_handle <= 0) | ||
945 | return -1; /* that was not successful, should try rebooting */ | ||
946 | char* buf = core_get_data(dircache_handle); | ||
947 | dircache_root = (struct dircache_entry*)ALIGN_UP(buf, | ||
948 | sizeof(struct dircache_entry*)); | ||
949 | d_names_start = d_names_end = buf + available - 1; | ||
950 | dircache_size = 0; | ||
951 | generate_dot_d_names(); | ||
952 | |||
953 | /* Start a non-transparent rebuild. */ | ||
954 | int res = dircache_do_rebuild(); | ||
955 | if (res < 0) | ||
956 | goto fail; | ||
957 | |||
958 | /* now compact the dircache buffer */ | ||
959 | char* dst = ((char*)&dircache_root[entry_count] + DIRCACHE_RESERVE); | ||
960 | ptrdiff_t offset = d_names_start - dst; | ||
961 | if (offset <= 0) /* something went wrong */ | ||
962 | { | ||
963 | res = -1; | ||
964 | goto fail; | ||
965 | } | ||
966 | |||
967 | /* memmove d_names down, there's a possibility of overlap | ||
968 | * equivaent to dircache_size - entry_count*sizeof(struct dircache_entry) */ | ||
969 | ptrdiff_t size_to_move = d_names_end - d_names_start; | ||
970 | memmove(dst, d_names_start, size_to_move); | ||
971 | |||
972 | /* fix up pointers to the d_names */ | ||
973 | for(unsigned i = 0; i < entry_count; i++) | ||
974 | dircache_root[i].d_name -= offset; | ||
975 | |||
976 | d_names_start -= offset; | ||
977 | d_names_end -= offset; | ||
978 | dot -= offset; | ||
979 | dotdot -= offset; | ||
980 | |||
981 | /* equivalent to dircache_size + DIRCACHE_RESERVE + align */ | ||
982 | allocated_size = (d_names_end - buf); | ||
983 | reserve_used = 0; | ||
984 | |||
985 | core_shrink(dircache_handle, dircache_root, allocated_size); | ||
986 | return res; | ||
987 | fail: | ||
988 | dircache_disable(); | ||
989 | return res; | ||
990 | } | ||
991 | |||
992 | /** | ||
993 | * Main initialization function that must be called before any other | ||
994 | * operations within the dircache. | ||
995 | */ | ||
996 | void dircache_init(void) | ||
997 | { | ||
998 | int i; | ||
999 | int thread_id __attribute__((unused)); | ||
1000 | |||
1001 | dircache_initialized = false; | ||
1002 | dircache_initializing = false; | ||
1003 | |||
1004 | memset(opendirs, 0, sizeof(opendirs)); | ||
1005 | for (i = 0; i < MAX_OPEN_DIRS; i++) | ||
1006 | { | ||
1007 | opendirs[i].theent.d_name = opendir_dnames[i]; | ||
1008 | } | ||
1009 | |||
1010 | queue_init(&dircache_queue, true); | ||
1011 | thread_id = create_thread(dircache_thread, dircache_stack, | ||
1012 | sizeof(dircache_stack), 0, dircache_thread_name | ||
1013 | IF_PRIO(, PRIORITY_BACKGROUND) | ||
1014 | IF_COP(, CPU)); | ||
1015 | #ifdef HAVE_IO_PRIORITY | ||
1016 | thread_set_io_priority(thread_id,IO_PRIORITY_BACKGROUND); | ||
1017 | #endif | ||
1018 | 2053 | ||
2054 | return started; | ||
1019 | } | 2055 | } |
1020 | 2056 | ||
1021 | /** | 2057 | /** |
1022 | * Returns true if dircache has been initialized and is ready to be used. | 2058 | * wait for the dircache thread to finish building; intended for waiting for a |
2059 | * non-transparent build to finish when dircache_resume() returns > 0 | ||
1023 | */ | 2060 | */ |
1024 | bool dircache_is_enabled(void) | 2061 | void dircache_wait(void) |
1025 | { | 2062 | { |
1026 | return dircache_initialized; | 2063 | thread_wait(dircache_runinfo.thread_id); |
1027 | } | 2064 | } |
1028 | 2065 | ||
1029 | /** | 2066 | /** |
1030 | * Returns true if dircache is being initialized. | 2067 | * call after mounting a volume or all volumes |
1031 | */ | 2068 | */ |
1032 | bool dircache_is_initializing(void) | 2069 | void dircache_mount(void) |
1033 | { | 2070 | { |
1034 | return dircache_initializing || thread_enabled; | 2071 | /* call with writer exclusion */ |
2072 | if (dircache_runinfo.suspended) | ||
2073 | return; | ||
2074 | |||
2075 | dircache_thread_post(NULL); | ||
1035 | } | 2076 | } |
1036 | 2077 | ||
1037 | /** | 2078 | /** |
1038 | * Set application flags used to determine if dircache is still intact. | 2079 | * call after unmounting a volume; specifying < 0 for all or >= 0 for the |
2080 | * specific one | ||
1039 | */ | 2081 | */ |
1040 | void dircache_set_appflag(long mask) | 2082 | void dircache_unmount(IF_MV_NONVOID(int volume)) |
1041 | { | 2083 | { |
1042 | appflags |= mask; | 2084 | /* call with writer exclusion */ |
2085 | if (dircache_runinfo.suspended) | ||
2086 | return; | ||
2087 | |||
2088 | #ifdef HAVE_MULTIVOLUME | ||
2089 | if (volume >= 0) | ||
2090 | reset_volume(volume); | ||
2091 | else | ||
2092 | #endif /* HAVE_MULTIVOLUME */ | ||
2093 | reset_cache(); | ||
2094 | } | ||
2095 | |||
2096 | /* backend to dircache_suspend() and dircache_disable() */ | ||
2097 | static void dircache_suspend_internal(bool freeit) | ||
2098 | { | ||
2099 | if (dircache_runinfo.suspended++ > 0 && | ||
2100 | (!freeit || dircache_runinfo.handle <= 0)) | ||
2101 | return; | ||
2102 | |||
2103 | unsigned int thread_id = dircache_runinfo.thread_id; | ||
2104 | |||
2105 | reset_cache(); | ||
2106 | clear_dircache_queue(); | ||
2107 | |||
2108 | /* grab the buffer away into our control; the cache won't need it now */ | ||
2109 | int handle = 0; | ||
2110 | if (freeit) | ||
2111 | handle = reset_buffer(); | ||
2112 | |||
2113 | dircache_unlock(); | ||
2114 | |||
2115 | if (handle > 0) | ||
2116 | core_free(handle); | ||
2117 | |||
2118 | thread_wait(thread_id); | ||
2119 | |||
2120 | dircache_lock(); | ||
2121 | } | ||
2122 | |||
2123 | /* backend to dircache_resume() and dircache_enable() */ | ||
2124 | static int dircache_resume_internal(bool build_now) | ||
2125 | { | ||
2126 | int volatile rc = 0; | ||
2127 | |||
2128 | if (dircache_runinfo.suspended == 0 || --dircache_runinfo.suspended == 0) | ||
2129 | rc = build_now && dircache_runinfo.enabled ? 1 : 0; | ||
2130 | |||
2131 | if (rc) | ||
2132 | rc = dircache_thread_post(&rc) ? INT_MIN : -1; | ||
2133 | |||
2134 | if (rc == INT_MIN) | ||
2135 | { | ||
2136 | dircache_unlock(); | ||
2137 | |||
2138 | while (rc == INT_MIN) /* poll for response */ | ||
2139 | sleep(0); | ||
2140 | |||
2141 | dircache_lock(); | ||
2142 | } | ||
2143 | |||
2144 | return rc < 0 ? rc * 10 - 2 : rc; | ||
1043 | } | 2145 | } |
1044 | 2146 | ||
1045 | /** | 2147 | /** |
1046 | * Get application flags used to determine if dircache is still intact. | 2148 | * service to dircache_enable() and dircache_load(); "build_now" starts a build |
2149 | * immediately if the cache was not enabled | ||
1047 | */ | 2150 | */ |
1048 | bool dircache_get_appflag(long mask) | 2151 | static int dircache_enable_internal(bool build_now) |
1049 | { | 2152 | { |
1050 | return dircache_is_enabled() && (appflags & mask); | 2153 | int rc = 0; |
2154 | |||
2155 | if (!dircache_runinfo.enabled) | ||
2156 | { | ||
2157 | dircache_runinfo.enabled = true; | ||
2158 | rc = dircache_resume_internal(build_now); | ||
2159 | } | ||
2160 | |||
2161 | return rc; | ||
1051 | } | 2162 | } |
1052 | 2163 | ||
1053 | /** | 2164 | /** |
1054 | * Returns the current number of entries (directories and files) in the cache. | 2165 | * service to dircache_disable() |
1055 | */ | 2166 | */ |
1056 | int dircache_get_entry_count(void) | 2167 | static void dircache_disable_internal(void) |
1057 | { | 2168 | { |
1058 | return entry_count; | 2169 | if (dircache_runinfo.enabled) |
2170 | { | ||
2171 | dircache_runinfo.enabled = false; | ||
2172 | dircache_suspend_internal(true); | ||
2173 | } | ||
1059 | } | 2174 | } |
1060 | 2175 | ||
1061 | /** | 2176 | /** |
1062 | * Returns the allocated space for dircache (without reserve space). | 2177 | * disables dircache without freeing the buffer (so it can be re-enabled |
2178 | * afterwards with dircache_resume(); usually called when accepting an USB | ||
2179 | * connection | ||
1063 | */ | 2180 | */ |
1064 | int dircache_get_cache_size(void) | 2181 | void dircache_suspend(void) |
1065 | { | 2182 | { |
1066 | return dircache_is_enabled() ? dircache_size : 0; | 2183 | dircache_lock(); |
2184 | dircache_suspend_internal(false); | ||
2185 | dircache_unlock(); | ||
1067 | } | 2186 | } |
1068 | 2187 | ||
1069 | /** | 2188 | /** |
1070 | * Returns how many bytes of the reserve allocation for live cache | 2189 | * re-enables the dircache if previously suspended by dircache_suspend |
1071 | * updates have been used. | 2190 | * or dircache_steal_buffer(), re-using the already allocated buffer if |
2191 | * available | ||
2192 | * | ||
2193 | * returns: 0 if the background build is started or dircache is still | ||
2194 | * suspended | ||
2195 | * > 0 if the build is non-background | ||
2196 | * < 0 upon failure | ||
1072 | */ | 2197 | */ |
1073 | int dircache_get_reserve_used(void) | 2198 | int dircache_resume(void) |
1074 | { | 2199 | { |
1075 | return dircache_is_enabled() ? reserve_used : 0; | 2200 | dircache_lock(); |
2201 | int rc = dircache_resume_internal(true); | ||
2202 | dircache_unlock(); | ||
2203 | return rc; | ||
1076 | } | 2204 | } |
1077 | 2205 | ||
1078 | /** | 2206 | /** |
1079 | * Returns the time in kernel ticks that took to build the cache. | 2207 | * as dircache_resume() but globally enables it; called by settings and init |
1080 | */ | 2208 | */ |
1081 | int dircache_get_build_ticks(void) | 2209 | int dircache_enable(void) |
1082 | { | 2210 | { |
1083 | return dircache_is_enabled() ? cache_build_ticks : 0; | 2211 | dircache_lock(); |
2212 | int rc = dircache_enable_internal(true); | ||
2213 | dircache_unlock(); | ||
2214 | return rc; | ||
1084 | } | 2215 | } |
1085 | 2216 | ||
1086 | /** | 2217 | /** |
1087 | * Disables dircache without freeing the buffer (so it can be re-enabled | 2218 | * as dircache_suspend() but also frees the buffer; usually called on shutdown |
1088 | * afterwards with dircache_resume() or dircache_build()), usually | 2219 | * or when deactivated |
1089 | * called when accepting an usb connection */ | 2220 | */ |
1090 | void dircache_suspend(void) | 2221 | void dircache_disable(void) |
1091 | { | 2222 | { |
1092 | int i; | 2223 | dircache_lock(); |
1093 | bool cache_in_use; | 2224 | dircache_disable_internal(); |
1094 | 2225 | dircache_unlock(); | |
1095 | if (thread_enabled) | ||
1096 | queue_post(&dircache_queue, DIRCACHE_STOP, 0); | ||
1097 | |||
1098 | while (thread_enabled) | ||
1099 | sleep(1); | ||
1100 | dircache_initialized = false; | ||
1101 | |||
1102 | logf("Waiting for cached dirs to release"); | ||
1103 | do { | ||
1104 | cache_in_use = false; | ||
1105 | for (i = 0; i < MAX_OPEN_DIRS; i++) { | ||
1106 | if (!opendirs[i].regulardir && opendirs[i].busy) | ||
1107 | { | ||
1108 | cache_in_use = true; | ||
1109 | sleep(1); | ||
1110 | break ; | ||
1111 | } | ||
1112 | } | ||
1113 | } while (cache_in_use) ; | ||
1114 | |||
1115 | logf("Cache released"); | ||
1116 | entry_count = 0; | ||
1117 | } | 2226 | } |
1118 | 2227 | ||
1119 | /** | 2228 | /** |
1120 | * Re-enables the dircache if previous suspended by dircache_suspend | 2229 | * have dircache give up its allocation; call dircache_resume() to restart it |
1121 | * or dircache_steal_buffer(), re-using the already allocated buffer | ||
1122 | * | ||
1123 | * Returns true if the background build is started, false otherwise | ||
1124 | * (e.g. if no buffer was previously allocated) | ||
1125 | */ | 2230 | */ |
1126 | bool dircache_resume(void) | 2231 | void dircache_free_buffer(void) |
1127 | { | 2232 | { |
1128 | bool ret = allocated_size > 0; | 2233 | dircache_lock(); |
1129 | if (ret) /* only resume if already allocated */ | 2234 | dircache_suspend_internal(true); |
1130 | ret = (dircache_build(0) > 0); | 2235 | dircache_unlock(); |
2236 | } | ||
2237 | |||
2238 | |||
2239 | /** Dircache live updating **/ | ||
1131 | 2240 | ||
1132 | return (allocated_size > 0); | 2241 | /** |
2242 | * obtain binding information for the file's root volume; this is the starting | ||
2243 | * point for internal path parsing and binding | ||
2244 | */ | ||
2245 | void dircache_get_rootinfo(struct file_base_info *infop) | ||
2246 | { | ||
2247 | int volume = BASEINFO_VOL(infop); | ||
2248 | struct dircache_volume *dcvolp = DCVOL(volume); | ||
2249 | |||
2250 | if (dcvolp->serialnum) | ||
2251 | { | ||
2252 | /* root has a binding */ | ||
2253 | infop->dcfile.idx = -volume - 1; | ||
2254 | infop->dcfile.serialnum = dcvolp->serialnum; | ||
2255 | } | ||
2256 | else | ||
2257 | { | ||
2258 | /* root is idle */ | ||
2259 | dircache_dcfile_init(&infop->dcfile); | ||
2260 | } | ||
1133 | } | 2261 | } |
1134 | 2262 | ||
1135 | /** | 2263 | /** |
1136 | * Disables the dircache entirely. Usually called on shutdown or when | 2264 | * called by file code when the first reference to a file or directory is |
1137 | * deactivated | 2265 | * opened |
1138 | */ | 2266 | */ |
1139 | void dircache_disable(void) | 2267 | void dircache_bind_file(struct file_base_binding *bindp) |
2268 | { | ||
2269 | /* requires write exclusion */ | ||
2270 | logf("dc open: %u", (unsigned int)bindp->info.dcfile.serialnum); | ||
2271 | binding_open(bindp); | ||
2272 | } | ||
2273 | |||
2274 | /** | ||
2275 | * called by file code when the last reference to a file or directory is | ||
2276 | * closed | ||
2277 | */ | ||
2278 | void dircache_unbind_file(struct file_base_binding *bindp) | ||
1140 | { | 2279 | { |
1141 | dircache_suspend(); | 2280 | /* requires write exclusion */ |
1142 | dircache_free(); | 2281 | logf("dc close: %u", (unsigned int)bindp->info.dcfile.serialnum); |
2282 | binding_close(bindp); | ||
1143 | } | 2283 | } |
1144 | 2284 | ||
1145 | /** | 2285 | /** |
1146 | * Steal the allocated dircache buffer and disable dircache. | 2286 | * called by file code when a file is newly created |
1147 | */ | 2287 | */ |
1148 | void* dircache_steal_buffer(size_t *size) | 2288 | void dircache_fileop_create(struct file_base_info *dirinfop, |
2289 | struct file_base_binding *bindp, | ||
2290 | const char *basename, | ||
2291 | const struct dirinfo_native *dinp) | ||
1149 | { | 2292 | { |
1150 | dircache_suspend(); | 2293 | /* requires write exclusion */ |
1151 | if (dircache_size == 0) | 2294 | logf("dc create: %u \"%s\"", |
2295 | (unsigned int)bindp->info.dcfile.serialnum, basename); | ||
2296 | |||
2297 | if (!dirinfop->dcfile.serialnum) | ||
1152 | { | 2298 | { |
1153 | *size = 0; | 2299 | /* no parent binding => no child binding */ |
1154 | return NULL; | 2300 | return; |
1155 | } | 2301 | } |
1156 | 2302 | ||
1157 | /* since we give up the buffer (without freeing), it must not move anymore */ | 2303 | struct dircache_entry *ce; |
1158 | dont_move = true; | 2304 | int idx = create_entry(basename, &ce); |
1159 | *size = dircache_size + (DIRCACHE_RESERVE-reserve_used); | 2305 | if (idx == 0) |
1160 | 2306 | { | |
1161 | return dircache_root; | 2307 | /* failed allocation; parent cache contents are not complete */ |
2308 | establish_frontier(dirinfop->dcfile.idx, FRONTIER_ZONED); | ||
2309 | return; | ||
2310 | } | ||
2311 | |||
2312 | struct file_base_info *infop = &bindp->info; | ||
2313 | |||
2314 | #ifdef DIRCACHE_NATIVE | ||
2315 | ce->firstcluster = infop->fatfile.firstcluster; | ||
2316 | ce->direntry = infop->fatfile.e.entry; | ||
2317 | ce->direntries = infop->fatfile.e.entries; | ||
2318 | ce->wrtdate = dinp->wrtdate; | ||
2319 | ce->wrttime = dinp->wrttime; | ||
2320 | #else | ||
2321 | ce->mtime = dinp->mtime; | ||
2322 | #endif | ||
2323 | ce->attr = dinp->attr; | ||
2324 | if (!(dinp->attr & ATTR_DIRECTORY)) | ||
2325 | ce->filesize = dinp->size; | ||
2326 | |||
2327 | insert_file_entry(dirinfop, ce); | ||
2328 | |||
2329 | /* file binding will have been queued when it was opened; just resolve */ | ||
2330 | infop->dcfile.idx = idx; | ||
2331 | infop->dcfile.serialnum = ce->serialnum; | ||
2332 | binding_resolve(infop); | ||
2333 | |||
2334 | if ((dinp->attr & ATTR_DIRECTORY) && !is_dotdir_name(basename)) | ||
2335 | { | ||
2336 | /* scan-in the contents of the new directory at this level only */ | ||
2337 | buffer_lock(); | ||
2338 | sab_process_dir(infop, false); | ||
2339 | buffer_unlock(); | ||
2340 | } | ||
1162 | } | 2341 | } |
1163 | 2342 | ||
1164 | /** | 2343 | /** |
1165 | * Usermode function to return dircache_entry index to the given path. | 2344 | * called by file code when a file or directory is removed |
1166 | */ | 2345 | */ |
1167 | static int dircache_get_entry_id_ex(const char *filename, bool go_down) | 2346 | void dircache_fileop_remove(struct file_base_binding *bindp) |
1168 | { | 2347 | { |
1169 | if (!dircache_initialized || filename == NULL) | 2348 | /* requires write exclusion */ |
1170 | return -1; | 2349 | logf("dc remove: %u\n", (unsigned int)bindp->info.dcfile.serialnum); |
1171 | |||
1172 | struct dircache_entry* res = dircache_get_entry(filename, go_down); | ||
1173 | return res ? res - dircache_root : -1; | ||
1174 | } | ||
1175 | 2350 | ||
1176 | int dircache_get_entry_id(const char* filename) | 2351 | if (!bindp->info.dcfile.serialnum) |
1177 | { | 2352 | return; /* no binding yet */ |
1178 | return dircache_get_entry_id_ex(filename, false); | 2353 | |
2354 | free_file_entry(&bindp->info); | ||
2355 | |||
2356 | /* if binding was resolved; it should now be queued via above call */ | ||
1179 | } | 2357 | } |
1180 | 2358 | ||
1181 | /** | 2359 | /** |
1182 | * Internal: Get the startcluster for the index | 2360 | * called by file code when a file is renamed |
1183 | */ | 2361 | */ |
1184 | long _dircache_get_entry_startcluster(int id) | 2362 | void dircache_fileop_rename(struct file_base_info *dirinfop, |
2363 | struct file_base_binding *bindp, | ||
2364 | const char *basename) | ||
1185 | { | 2365 | { |
1186 | return get_entry(id)->startcluster; | 2366 | /* requires write exclusion */ |
2367 | logf("dc rename: %u \"%s\"", | ||
2368 | (unsigned int)bindp->info.dcfile.serialnum, basename); | ||
2369 | |||
2370 | if (!dirinfop->dcfile.serialnum) | ||
2371 | { | ||
2372 | /* new parent directory not cached; there is nowhere to put it so | ||
2373 | nuke it */ | ||
2374 | if (bindp->info.dcfile.serialnum) | ||
2375 | free_file_entry(&bindp->info); | ||
2376 | /* else no entry anyway */ | ||
2377 | |||
2378 | return; | ||
2379 | } | ||
2380 | |||
2381 | if (!bindp->info.dcfile.serialnum) | ||
2382 | { | ||
2383 | /* binding not resolved on the old file but it's going into a resolved | ||
2384 | parent which means the parent would be missing an entry in the cache; | ||
2385 | downgrade the parent */ | ||
2386 | establish_frontier(dirinfop->dcfile.idx, FRONTIER_ZONED); | ||
2387 | return; | ||
2388 | } | ||
2389 | |||
2390 | /* unlink the entry but keep it; it needs to be re-sorted since the | ||
2391 | underlying FS probably changed the order */ | ||
2392 | struct dircache_entry *ce = remove_file_entry(&bindp->info); | ||
2393 | |||
2394 | #ifdef DIRCACHE_NATIVE | ||
2395 | /* update other name-related information before inserting */ | ||
2396 | ce->direntry = bindp->info.fatfile.e.entry; | ||
2397 | ce->direntries = bindp->info.fatfile.e.entries; | ||
2398 | #endif | ||
2399 | |||
2400 | /* place it into its new home */ | ||
2401 | insert_file_entry(dirinfop, ce); | ||
2402 | |||
2403 | /* lastly, update the entry name itself */ | ||
2404 | if (entry_reassign_name(ce, basename)) | ||
2405 | { | ||
2406 | /* it's not really the same one now so re-stamp it */ | ||
2407 | dc_serial_t serialnum = next_serialnum(); | ||
2408 | ce->serialnum = serialnum; | ||
2409 | bindp->info.dcfile.serialnum = serialnum; | ||
2410 | } | ||
2411 | else | ||
2412 | { | ||
2413 | /* it cannot be kept around without a valid name */ | ||
2414 | free_file_entry(&bindp->info); | ||
2415 | establish_frontier(dirinfop->dcfile.idx, FRONTIER_ZONED); | ||
2416 | } | ||
1187 | } | 2417 | } |
1188 | 2418 | ||
1189 | /** | 2419 | /** |
1190 | * Internal: Get the struct dirinfo for the index | 2420 | * called by file code to synchronize file entry information |
1191 | */ | 2421 | */ |
1192 | struct dirinfo* _dircache_get_entry_dirinfo(int id) | 2422 | void dircache_fileop_sync(struct file_base_binding *bindp, |
2423 | const struct dirinfo_native *dinp) | ||
1193 | { | 2424 | { |
1194 | return &get_entry(id)->info; | 2425 | /* requires write exclusion */ |
2426 | struct file_base_info *infop = &bindp->info; | ||
2427 | logf("dc sync: %u\n", (unsigned int)infop->dcfile.serialnum); | ||
2428 | |||
2429 | if (!infop->dcfile.serialnum) | ||
2430 | return; /* binding unresolved */ | ||
2431 | |||
2432 | struct dircache_entry *ce = get_entry(infop->dcfile.idx); | ||
2433 | if (!ce) | ||
2434 | { | ||
2435 | logf(" bad index %d", infop->dcfile.idx); | ||
2436 | return; /* a root (should never be called for this) */ | ||
2437 | } | ||
2438 | |||
2439 | #ifdef DIRCACHE_NATIVE | ||
2440 | ce->firstcluster = infop->fatfile.firstcluster; | ||
2441 | ce->wrtdate = dinp->wrtdate; | ||
2442 | ce->wrttime = dinp->wrttime; | ||
2443 | #else | ||
2444 | ce->mtime = dinp->mtime; | ||
2445 | #endif | ||
2446 | ce->attr = dinp->attr; | ||
2447 | if (!(dinp->attr & ATTR_DIRECTORY)) | ||
2448 | ce->filesize = dinp->size; | ||
1195 | } | 2449 | } |
1196 | 2450 | ||
1197 | /* | 2451 | |
1198 | * build a path from an entry upto the root using recursion | 2452 | /** Dircache paths and files **/ |
1199 | * | 2453 | |
1200 | * it appends '/' after strlcat, therefore buf[0] needs to be prepared with '/' | 2454 | #ifdef DIRCACHE_DUMPSTER |
1201 | * and it will leave a trailing '/' | 2455 | /* helper for dircache_get_path() */ |
1202 | * | 2456 | static ssize_t get_path_sub(int idx, char *buf, size_t size) |
1203 | * returns the position of that trailing '/' so it can be deleted afterwards | ||
1204 | * (or, in case of truncation, the position of the nul byte */ | ||
1205 | static size_t copy_path_helper(const struct dircache_entry *entry, char *buf, size_t size) | ||
1206 | { | 2457 | { |
1207 | int offset = 1; | 2458 | if (idx == 0) |
1208 | /* has parent? */ | 2459 | return -2; /* entry is an orphan split from any root */ |
1209 | if (entry->up) | ||
1210 | offset += copy_path_helper(entry->up, buf, size); | ||
1211 | 2460 | ||
1212 | size_t len = strlcpy(buf+offset, entry->d_name, size - offset) + offset; | 2461 | ssize_t offset; |
1213 | if (len < size) | 2462 | char *cename; |
2463 | |||
2464 | if (idx > 0) | ||
1214 | { | 2465 | { |
1215 | buf[len++] = '/'; | 2466 | /* go all the way up then move back down from the root */ |
1216 | buf[len] = '\0'; | 2467 | struct dircache_entry *ce = get_entry(idx); |
2468 | offset = get_path_sub(ce->up, buf, size) - 1; | ||
2469 | if (offset < 0) | ||
2470 | return -3; | ||
2471 | |||
2472 | cename = alloca(MAX_NAME + 1); | ||
2473 | entry_name_copy(cename, ce); | ||
1217 | } | 2474 | } |
1218 | return len-1; | 2475 | else /* idx < 0 */ |
2476 | { | ||
2477 | offset = 0; | ||
2478 | cename = ""; | ||
2479 | |||
2480 | #ifdef HAVE_MULTIVOLUME | ||
2481 | int volume = IF_MV_VOL(-idx - 1); | ||
2482 | if (volume > 0) | ||
2483 | { | ||
2484 | /* prepend the volume specifier for volumes > 0 */ | ||
2485 | cename = alloca(VOL_MAX_LEN+1); | ||
2486 | get_volume_name(volume, cename); | ||
2487 | } | ||
2488 | #endif /* HAVE_MULTIVOLUME */ | ||
2489 | } | ||
2490 | |||
2491 | return offset + path_append(buf + offset, PA_SEP_HARD, cename, | ||
2492 | size > (size_t)offset ? size - offset : 0); | ||
1219 | } | 2493 | } |
2494 | #endif /* DIRCACHE_DUMPSTER */ | ||
2495 | |||
2496 | #if 0 | ||
2497 | |||
1220 | /** | 2498 | /** |
1221 | * Function to copy the full absolute path from dircache to the given buffer | 2499 | * retrieve and validate the file's entry/binding serial number |
1222 | * using the given dircache_entry pointer. | 2500 | * the dircache file's serial number must match the indexed entry's or the |
1223 | * | 2501 | * file reference is stale |
1224 | * Returns the size of the resulting string, or 0 if an error occured | ||
1225 | */ | 2502 | */ |
1226 | size_t dircache_copy_path(int index, char *buf, size_t size) | 2503 | static dc_serial_t get_file_serialnum(const struct dircache_file *dcfilep) |
1227 | { | 2504 | { |
1228 | if (!size || !buf || index < 0) | 2505 | int idx = dcfilep->idx; |
2506 | |||
2507 | if (idx == 0 || idx < -NUM_VOLUMES) | ||
1229 | return 0; | 2508 | return 0; |
1230 | 2509 | ||
1231 | buf[0] = '/'; | 2510 | dc_serial_t serialnum; |
1232 | size_t res = copy_path_helper(&dircache_root[index], buf, size - 1); | 2511 | |
1233 | /* fixup trailing '/' */ | 2512 | if (idx > 0) |
1234 | buf[res] = '\0'; | 2513 | { |
1235 | return res; | 2514 | struct dircache_entry *ce = get_entry(idx); |
2515 | serialnum = ce ? ce->serialnum : 0; | ||
2516 | } | ||
2517 | else | ||
2518 | { | ||
2519 | serialnum = get_idx_dcvolp(idx)->serialnum; | ||
2520 | } | ||
2521 | |||
2522 | return serialnum == dcfilep->serialnum ? serialnum : 0; | ||
1236 | } | 2523 | } |
1237 | 2524 | ||
1238 | /* --- Directory cache live updating functions --- */ | 2525 | /** |
1239 | static int block_until_ready(void) | 2526 | * usermode function to construct a full absolute path from dircache into the |
2527 | * given buffer given the dircache file info | ||
2528 | * | ||
2529 | * returns: | ||
2530 | * success - the length of the string, not including the trailing null | ||
2531 | * failure - a negative value | ||
2532 | * | ||
2533 | * successful return value is as strlcpy() | ||
2534 | * | ||
2535 | * errors: | ||
2536 | * ENOENT - the file or directory does not exist | ||
2537 | */ | ||
2538 | ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf, | ||
2539 | size_t size) | ||
1240 | { | 2540 | { |
1241 | /* Block until dircache has been built. */ | 2541 | /* if missing buffer space, still return what's needed a la strlcpy */ |
1242 | while (!dircache_initialized && dircache_is_initializing()) | 2542 | if (!buf) |
1243 | sleep(1); | 2543 | size = 0; |
1244 | 2544 | else if (size) | |
1245 | if (!dircache_initialized) | 2545 | *buf = '\0'; |
1246 | return -1; | 2546 | |
1247 | 2547 | ssize_t len = -1; | |
1248 | return 0; | 2548 | |
2549 | dircache_lock(); | ||
2550 | |||
2551 | /* first and foremost, there must be a cache and the serial number must | ||
2552 | check out */ | ||
2553 | if (dircache_runinfo.handle && get_file_serialnum(dcfilep)) | ||
2554 | len = get_path_sub(dcfilep->idx, buf, size); | ||
2555 | |||
2556 | if (len < 0) | ||
2557 | errno = ENOENT; | ||
2558 | |||
2559 | dircache_unlock(); | ||
2560 | return len; | ||
1249 | } | 2561 | } |
1250 | 2562 | ||
1251 | static struct dircache_entry* dircache_new_entry(const char *path, int attribute) | 2563 | /** |
1252 | { | 2564 | * searches the sublist starting at 'idx' for the named component |
1253 | struct dircache_entry *entry; | 2565 | */ |
1254 | char basedir[MAX_PATH*2]; | ||
1255 | char *new; | ||
1256 | long last_cache_size = dircache_size; | ||
1257 | 2566 | ||
1258 | strlcpy(basedir, path, sizeof(basedir)); | 2567 | /* helper for get_file_sub() */ |
1259 | new = strrchr(basedir, '/'); | 2568 | static struct dircache_entry * |
1260 | if (new == NULL) | 2569 | get_file_sub_scan(int idx, const char *name, size_t length, int *idxp) |
2570 | { | ||
2571 | struct dircache_entry *ce = get_entry(idx); | ||
2572 | if (ce) | ||
1261 | { | 2573 | { |
1262 | logf("error occurred"); | 2574 | char entname[MAX_NAME+1]; |
1263 | dircache_initialized = false; | 2575 | name = strmemdupa(name, length); |
1264 | return NULL; | 2576 | |
2577 | do | ||
2578 | { | ||
2579 | entry_name_copy(entname, ce); | ||
2580 | if (!strcasecmp(entname, name)) | ||
2581 | { | ||
2582 | *idxp = idx; | ||
2583 | break; | ||
2584 | } | ||
2585 | |||
2586 | idx = ce->next; | ||
2587 | } | ||
2588 | while ((ce = get_entry(idx))); | ||
1265 | } | 2589 | } |
1266 | 2590 | ||
1267 | *new = '\0'; | 2591 | return ce; |
1268 | new++; | 2592 | } |
2593 | |||
2594 | /** | ||
2595 | * searches for the subcomponent of *pathp | ||
2596 | */ | ||
2597 | |||
2598 | /* helper for dircache_get_file() */ | ||
2599 | static int get_file_sub(const char **pathp, int *downp, int *idxp) | ||
2600 | { | ||
2601 | int rc; | ||
2602 | const char *name; | ||
2603 | rc = parse_path_component(pathp, &name, false); | ||
2604 | if (rc <= 0) | ||
2605 | return rc; | ||
2606 | else if (rc >= MAX_PATH) | ||
2607 | return ENAMETOOLONG; /* that's just unpossible, man */ | ||
2608 | |||
2609 | struct dircache_entry *ce = get_file_sub_scan(*downp, name, rc, idxp); | ||
1269 | 2610 | ||
1270 | entry = dircache_get_entry(basedir, true); | 2611 | if (!ce) |
1271 | if (entry == NULL) | 2612 | rc = RC_NOT_FOUND; /* not there; tellibry solly */ |
2613 | else if (!*pathp) | ||
2614 | rc = RC_PATH_ENDED; /* done */ | ||
2615 | else if (!(ce->attr & ATTR_DIRECTORY)) | ||
2616 | rc = ENOTDIR; /* a parent component must be a directory */ | ||
2617 | else | ||
2618 | while ((rc = get_file_sub(pathp, &ce->down, idxp)) == RC_CONTINUE); | ||
2619 | |||
2620 | switch (rc) | ||
1272 | { | 2621 | { |
1273 | logf("basedir not found!"); | 2622 | case RC_GO_UP: /* hit ".."; drop to previous level */ |
1274 | logf("%s", basedir); | 2623 | return RC_CONTINUE; |
1275 | dircache_initialized = false; | 2624 | case RC_PATH_ENDED: /* success! */ |
1276 | return NULL; | 2625 | return RC_FOUND; |
2626 | default: /* component not found or error */ | ||
2627 | return rc; | ||
1277 | } | 2628 | } |
2629 | } | ||
1278 | 2630 | ||
1279 | if (reserve_used + 2*sizeof(struct dircache_entry) + strlen(new)+1 | 2631 | /** |
1280 | >= DIRCACHE_RESERVE) | 2632 | * usermode function to return dircache file info for the given path |
2633 | * | ||
2634 | * returns: | ||
2635 | * success: the volume number that is specified for the file | ||
2636 | * failure: a negative value | ||
2637 | * | ||
2638 | * errors: | ||
2639 | * ENOENT - the file or directory does not exist or path is empty | ||
2640 | * ENAMETOOLONG - a component of the path is too long | ||
2641 | * ENOTDIR - a component of the path is not a directory | ||
2642 | */ | ||
2643 | int dircache_get_file(const char *path, struct dircache_file *dcfilep) | ||
2644 | { | ||
2645 | if (!path_is_absolute(path) || !dcfilep) | ||
1281 | { | 2646 | { |
1282 | logf("not enough space"); | 2647 | errno = ENOENT; |
1283 | dircache_initialized = false; | 2648 | return -1; |
1284 | return NULL; | ||
1285 | } | 2649 | } |
1286 | |||
1287 | while (entry->next != NULL) | ||
1288 | entry = entry->next; | ||
1289 | 2650 | ||
1290 | if (entry->d_name != NULL) | 2651 | dircache_lock(); |
2652 | |||
2653 | if (!dircache_runinfo.handle) | ||
1291 | { | 2654 | { |
1292 | entry = dircache_gen_next(entry); | 2655 | dircache_unlock(); |
1293 | if (entry == NULL) | 2656 | errno = ENOENT; |
2657 | return -2; | ||
2658 | } | ||
2659 | |||
2660 | int volume = 0; | ||
2661 | int idx = 0; | ||
2662 | dc_serial_t serialnum = 0; | ||
2663 | struct dircache_volume *dcvolp = NULL; | ||
2664 | struct dircache_entry *ce = NULL; | ||
2665 | |||
2666 | int rc = RC_GO_UP; | ||
2667 | |||
2668 | while (rc == RC_CONTINUE || rc == RC_GO_UP) | ||
2669 | { | ||
2670 | #ifdef HAVE_MULTIVOLUME | ||
2671 | if (rc == RC_GO_UP) | ||
1294 | { | 2672 | { |
1295 | dircache_initialized = false; | 2673 | volume = path_strip_volume(path, &path, false); |
1296 | return NULL; | 2674 | if (!CHECK_VOL(volume)) |
2675 | { | ||
2676 | rc = ENXIO; | ||
2677 | break; | ||
2678 | } | ||
1297 | } | 2679 | } |
1298 | } | 2680 | #endif /* HAVE_MULTIVOLUME */ |
2681 | |||
2682 | dcvolp = DCVOL(volume); | ||
1299 | 2683 | ||
1300 | size_t size = strlen(new) + 1; | 2684 | int *downp = &dcvolp->root_down; |
1301 | entry->d_name = (d_names_start -= size); | 2685 | if (*downp <= 0) |
1302 | entry->startcluster = 0; | 2686 | { |
1303 | memset(&entry->info, 0, sizeof(entry->info)); | 2687 | rc = ENXIO; |
1304 | entry->info.attribute = attribute; | 2688 | break; |
2689 | } | ||
1305 | 2690 | ||
1306 | strcpy(entry->d_name, new); | 2691 | rc = get_file_sub(&path, downp, &idx); |
1307 | dircache_size += size; | 2692 | } |
1308 | 2693 | ||
1309 | if (attribute & ATTR_DIRECTORY) | 2694 | switch (rc) |
1310 | { | 2695 | { |
1311 | logf("gen_down"); | 2696 | case RC_FOUND: /* hit: component found */ |
1312 | dircache_gen_down(entry); | 2697 | serialnum = ce->serialnum; |
2698 | rc = volume; | ||
2699 | break; | ||
2700 | case RC_PATH_ENDED: /* hit: it's a root (volume or system) */ | ||
2701 | idx = -volume - 1; | ||
2702 | serialnum = dcvolp->serialnum; | ||
2703 | rc = volume; | ||
2704 | break; | ||
2705 | case RC_NOT_FOUND: /* miss */ | ||
2706 | rc = ENOENT; | ||
2707 | default: | ||
2708 | idx = 0; | ||
2709 | errno = rc; | ||
2710 | rc = -3; | ||
2711 | break; | ||
1313 | } | 2712 | } |
1314 | |||
1315 | reserve_used += dircache_size - last_cache_size; | ||
1316 | 2713 | ||
1317 | return entry; | 2714 | dcfilep->idx = idx; |
2715 | dcfilep->serialnum = serialnum; | ||
2716 | |||
2717 | dircache_unlock(); | ||
2718 | return rc; | ||
1318 | } | 2719 | } |
2720 | #endif /* 0 */ | ||
2721 | |||
1319 | 2722 | ||
1320 | void dircache_bind(int fd, const char *path) | 2723 | /** Debug screen/info stuff **/ |
2724 | |||
2725 | /** | ||
2726 | * return cache state parameters | ||
2727 | */ | ||
2728 | void dircache_get_info(struct dircache_info *info) | ||
1321 | { | 2729 | { |
1322 | struct dircache_entry *entry; | 2730 | static const char * const status_descriptions[] = |
1323 | 2731 | { | |
1324 | /* Queue requests until dircache has been built. */ | 2732 | [DIRCACHE_IDLE] = "Idle", |
1325 | if (!dircache_initialized && dircache_is_initializing()) | 2733 | [DIRCACHE_SCANNING] = "Scanning", |
2734 | [DIRCACHE_READY] = "Ready", | ||
2735 | }; | ||
2736 | |||
2737 | if (!info) | ||
2738 | return; | ||
2739 | |||
2740 | memset(info, 0, sizeof (*info)); | ||
2741 | |||
2742 | dircache_lock(); | ||
2743 | |||
2744 | enum dircache_status status = DIRCACHE_IDLE; | ||
2745 | |||
2746 | for (unsigned int volume = 0; volume < NUM_VOLUMES; volume++) | ||
1326 | { | 2747 | { |
1327 | if (fdbind_idx >= MAX_PENDING_BINDINGS) | 2748 | struct dircache_volume *dcvolp = DCVOL(volume); |
1328 | return ; | 2749 | enum dircache_status volstatus = dcvolp->status; |
1329 | strlcpy(fdbind_cache[fdbind_idx].path, path, | 2750 | |
1330 | sizeof(fdbind_cache[fdbind_idx].path)); | 2751 | switch (volstatus) |
1331 | fdbind_cache[fdbind_idx].fd = fd; | 2752 | { |
1332 | fdbind_idx++; | 2753 | case DIRCACHE_SCANNING: |
1333 | return ; | 2754 | /* if any one is scanning then overall status is "scanning" */ |
2755 | status = volstatus; | ||
2756 | |||
2757 | /* sum the time the scanning has taken so far */ | ||
2758 | info->build_ticks += current_tick - dcvolp->start_tick; | ||
2759 | break; | ||
2760 | case DIRCACHE_READY: | ||
2761 | /* if all the rest are idle and at least one is ready, then | ||
2762 | status is "ready". */ | ||
2763 | if (status == DIRCACHE_IDLE) | ||
2764 | status = DIRCACHE_READY; | ||
2765 | |||
2766 | /* sum the build ticks of all "ready" volumes */ | ||
2767 | info->build_ticks += dcvolp->build_ticks; | ||
2768 | break; | ||
2769 | case DIRCACHE_IDLE: | ||
2770 | /* if all are idle; then the whole cache is "idle" */ | ||
2771 | break; | ||
2772 | } | ||
1334 | } | 2773 | } |
1335 | |||
1336 | if (!dircache_initialized) | ||
1337 | return ; | ||
1338 | 2774 | ||
1339 | logf("bind: %d/%s", fd, path); | 2775 | info->status = status; |
1340 | entry = dircache_get_entry(path, false); | 2776 | info->statusdesc = status_descriptions[status]; |
1341 | if (entry == NULL) | 2777 | info->last_size = dircache.last_size; |
2778 | info->size_limit = DIRCACHE_LIMIT; | ||
2779 | info->reserve = DIRCACHE_RESERVE; | ||
2780 | |||
2781 | /* report usage only if there is something ready or being built */ | ||
2782 | if (status != DIRCACHE_IDLE) | ||
1342 | { | 2783 | { |
1343 | logf("not found!"); | 2784 | info->reserve_used = reserve_buf_used(); |
1344 | dircache_initialized = false; | 2785 | info->size = dircache.size; |
1345 | return ; | 2786 | info->sizeused = dircache.sizeused; |
2787 | info->entry_count = dircache.numentries; | ||
1346 | } | 2788 | } |
1347 | 2789 | ||
1348 | fd_bindings[fd] = entry; | 2790 | dircache_unlock(); |
1349 | } | 2791 | } |
1350 | 2792 | ||
1351 | void dircache_update_filesize(int fd, long newsize, long startcluster) | 2793 | #ifdef DIRCACHE_DUMPSTER |
2794 | /** | ||
2795 | * dump RAW binary of buffer and CSV of all valid paths and volumes to disk | ||
2796 | */ | ||
2797 | void dircache_dump(void) | ||
1352 | { | 2798 | { |
1353 | if (!dircache_initialized || fd < 0) | 2799 | /* open both now so they're in the cache */ |
1354 | return ; | 2800 | int fdbin = open(DIRCACHE_DUMPSTER_BIN, O_WRONLY|O_CREAT|O_TRUNC, 0666); |
1355 | 2801 | int fdcsv = open(DIRCACHE_DUMPSTER_CSV, O_WRONLY|O_CREAT|O_TRUNC, 0666); | |
1356 | if (fd_bindings[fd] == NULL) | 2802 | if (fdbin < 0 || fdcsv < 0) |
1357 | { | 2803 | { |
1358 | logf("dircache fd(%d) access error", fd); | 2804 | close(fdbin); |
1359 | dircache_initialized = false; | 2805 | return; |
1360 | return ; | ||
1361 | } | 2806 | } |
1362 | |||
1363 | fd_bindings[fd]->info.size = newsize; | ||
1364 | fd_bindings[fd]->startcluster = startcluster; | ||
1365 | } | ||
1366 | void dircache_update_filetime(int fd) | ||
1367 | { | ||
1368 | #if CONFIG_RTC == 0 | ||
1369 | (void)fd; | ||
1370 | #else | ||
1371 | short year; | ||
1372 | struct tm *now = get_time(); | ||
1373 | if (!dircache_initialized || fd < 0) | ||
1374 | return ; | ||
1375 | |||
1376 | if (fd_bindings[fd] == NULL) | ||
1377 | { | ||
1378 | logf("dircache fd access error"); | ||
1379 | dircache_initialized = false; | ||
1380 | return ; | ||
1381 | } | ||
1382 | year = now->tm_year+1900-1980; | ||
1383 | fd_bindings[fd]->info.wrtdate = (((year)&0x7f)<<9) | | ||
1384 | (((now->tm_mon+1)&0xf)<<5) | | ||
1385 | (((now->tm_mday)&0x1f)); | ||
1386 | fd_bindings[fd]->info.wrttime = (((now->tm_hour)&0x1f)<<11) | | ||
1387 | (((now->tm_min)&0x3f)<<5) | | ||
1388 | (((now->tm_sec/2)&0x1f)); | ||
1389 | #endif | ||
1390 | } | ||
1391 | 2807 | ||
1392 | void dircache_mkdir(const char *path) | 2808 | trigger_cpu_boost(); |
1393 | { /* Test ok. */ | 2809 | dircache_lock(); |
1394 | if (block_until_ready()) | 2810 | |
1395 | return ; | 2811 | if (dircache_runinfo.handle) |
1396 | 2812 | { | |
1397 | 2813 | buffer_lock(); | |
1398 | logf("mkdir: %s", path); | 2814 | |
1399 | dircache_new_entry(path, ATTR_DIRECTORY); | 2815 | /* bin */ |
1400 | } | 2816 | write(fdbin, dircache_runinfo.p + ENTRYSIZE, |
1401 | 2817 | dircache_runinfo.bufsize + 1); | |
1402 | void dircache_rmdir(const char *path) | 2818 | |
1403 | { /* Test ok. */ | 2819 | /* CSV */ |
1404 | struct dircache_entry *entry; | 2820 | fdprintf(fdcsv, "\"Index\",\"Serialnum\"," |
1405 | 2821 | "\"Path\",\"Frontier\"," | |
1406 | if (block_until_ready()) | 2822 | "\"Attribute\",\"File Size\"," |
1407 | return ; | 2823 | "\"Mod Date\",\"Mod Time\"\n"); |
1408 | 2824 | ||
1409 | logf("rmdir: %s", path); | 2825 | FOR_EACH_VOLUME(-1, volume) |
1410 | entry = dircache_get_entry(path, false); | ||
1411 | if (entry == NULL || entry->down == NULL) | ||
1412 | { | ||
1413 | logf("not found or not a directory!"); | ||
1414 | dircache_initialized = false; | ||
1415 | return ; | ||
1416 | } | ||
1417 | |||
1418 | entry->down = NULL; | ||
1419 | entry->d_name = NULL; | ||
1420 | } | ||
1421 | |||
1422 | /* Remove a file from cache */ | ||
1423 | void dircache_remove(const char *name) | ||
1424 | { /* Test ok. */ | ||
1425 | struct dircache_entry *entry; | ||
1426 | |||
1427 | if (block_until_ready()) | ||
1428 | return ; | ||
1429 | |||
1430 | logf("remove: %s", name); | ||
1431 | |||
1432 | entry = dircache_get_entry(name, false); | ||
1433 | |||
1434 | if (entry == NULL) | ||
1435 | { | ||
1436 | logf("not found!"); | ||
1437 | dircache_initialized = false; | ||
1438 | return ; | ||
1439 | } | ||
1440 | |||
1441 | entry->d_name = NULL; | ||
1442 | } | ||
1443 | |||
1444 | void dircache_rename(const char *oldpath, const char *newpath) | ||
1445 | { /* Test ok. */ | ||
1446 | struct dircache_entry *entry, *newentry; | ||
1447 | struct dircache_entry oldentry; | ||
1448 | char absolute_path[MAX_PATH*2]; | ||
1449 | char *p; | ||
1450 | |||
1451 | if (block_until_ready()) | ||
1452 | return ; | ||
1453 | |||
1454 | logf("rename: %s->%s", oldpath, newpath); | ||
1455 | |||
1456 | entry = dircache_get_entry(oldpath, false); | ||
1457 | if (entry == NULL) | ||
1458 | { | ||
1459 | logf("not found!"); | ||
1460 | dircache_initialized = false; | ||
1461 | return ; | ||
1462 | } | ||
1463 | |||
1464 | /* Delete the old entry. */ | ||
1465 | entry->d_name = NULL; | ||
1466 | |||
1467 | /** If we rename the same filename twice in a row, we need to | ||
1468 | * save the data, because the entry will be re-used. */ | ||
1469 | oldentry = *entry; | ||
1470 | |||
1471 | /* Generate the absolute path for destination if necessary. */ | ||
1472 | if (newpath[0] != '/') | ||
1473 | { | ||
1474 | strlcpy(absolute_path, oldpath, sizeof(absolute_path)); | ||
1475 | p = strrchr(absolute_path, '/'); | ||
1476 | if (!p) | ||
1477 | { | 2826 | { |
1478 | logf("Invalid path"); | 2827 | struct dircache_volume *dcvolp = DCVOL(volume); |
1479 | dircache_initialized = false; | 2828 | if (dcvolp->status == DIRCACHE_IDLE) |
1480 | return ; | 2829 | continue; |
2830 | |||
2831 | #ifdef HAVE_MULTIVOLUME | ||
2832 | char name[VOL_MAX_LEN+1]; | ||
2833 | get_volume_name(volume, name); | ||
2834 | #endif | ||
2835 | fdprintf(fdcsv, | ||
2836 | "%d,%lu," | ||
2837 | "\"%c" IF_MV("%s") "\",%u," | ||
2838 | "0x%08X,0," | ||
2839 | "\"\",\"\"\n", | ||
2840 | -volume-1, dcvolp->serialnum, | ||
2841 | PATH_SEPCH, IF_MV(name,) dcvolp->frontier, | ||
2842 | ATTR_DIRECTORY | ATTR_VOLUME); | ||
1481 | } | 2843 | } |
1482 | 2844 | ||
1483 | *p = '\0'; | 2845 | FOR_EACH_CACHE_ENTRY(ce) |
1484 | strlcpy(p, absolute_path, sizeof(absolute_path)-strlen(p)); | 2846 | { |
1485 | newpath = absolute_path; | 2847 | #ifdef DIRCACHE_NATIVE |
1486 | } | 2848 | time_t mtime = fattime_mktime(ce->wrtdate, ce->wrttime); |
1487 | 2849 | #else | |
1488 | newentry = dircache_new_entry(newpath, entry->info.attribute); | 2850 | time_t mtime = ce->mtime; |
1489 | if (newentry == NULL) | 2851 | #endif |
1490 | { | 2852 | struct tm tm; |
1491 | dircache_initialized = false; | 2853 | gmtime_r(&mtime, &tm); |
1492 | return ; | 2854 | |
2855 | char buf[DC_MAX_NAME + 2]; | ||
2856 | *buf = '\0'; | ||
2857 | int idx = get_index(ce); | ||
2858 | get_path_sub(idx, buf, sizeof (buf)); | ||
2859 | |||
2860 | fdprintf(fdcsv, | ||
2861 | "%d,%lu," | ||
2862 | "\"%s\",%u," | ||
2863 | "0x%08X,%lu," | ||
2864 | "%04d/%02d/%02d," | ||
2865 | "%02d:%02d:%02d\n", | ||
2866 | idx, ce->serialnum, | ||
2867 | buf, ce->frontier, | ||
2868 | ce->attr, (ce->attr & ATTR_DIRECTORY) ? | ||
2869 | 0ul : (unsigned long)ce->filesize, | ||
2870 | tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, | ||
2871 | tm.tm_hour, tm.tm_min, tm.tm_sec); | ||
2872 | } | ||
2873 | |||
2874 | buffer_unlock(); | ||
1493 | } | 2875 | } |
1494 | 2876 | ||
1495 | newentry->down = oldentry.down; | 2877 | dircache_unlock(); |
1496 | newentry->startcluster = oldentry.startcluster; | 2878 | cancel_cpu_boost(); |
1497 | newentry->info.size = oldentry.info.size; | 2879 | |
1498 | newentry->info.wrtdate = oldentry.info.wrtdate; | 2880 | close(fdbin); |
1499 | newentry->info.wrttime = oldentry.info.wrttime; | 2881 | close(fdcsv); |
1500 | } | 2882 | } |
2883 | #endif /* DIRCACHE_DUMPSTER */ | ||
2884 | |||
1501 | 2885 | ||
1502 | void dircache_add_file(const char *path, long startcluster) | 2886 | /** Misc. stuff **/ |
2887 | |||
2888 | /** | ||
2889 | * set the dircache file to initial values | ||
2890 | */ | ||
2891 | void dircache_dcfile_init(struct dircache_file *dcfilep) | ||
1503 | { | 2892 | { |
1504 | struct dircache_entry *entry; | 2893 | dcfilep->idx = 0; |
1505 | 2894 | dcfilep->serialnum = 0; | |
1506 | if (block_until_ready()) | ||
1507 | return ; | ||
1508 | |||
1509 | logf("add file: %s", path); | ||
1510 | entry = dircache_new_entry(path, 0); | ||
1511 | if (entry == NULL) | ||
1512 | return ; | ||
1513 | |||
1514 | entry->startcluster = startcluster; | ||
1515 | } | 2895 | } |
1516 | 2896 | ||
1517 | static bool is_disable_msg_pending(void) | 2897 | #ifdef HAVE_EEPROM_SETTINGS |
2898 | |||
2899 | #ifdef HAVE_HOTSWAP | ||
2900 | /* NOTE: This is hazardous to the filesystem of any sort of removable | ||
2901 | storage unless it may be determined that the filesystem from save | ||
2902 | to load is identical. If it's not possible to do so in a timely | ||
2903 | manner, it's not worth persisting the cache. */ | ||
2904 | #warning "Don't do this; you'll find the consequences unpleasant." | ||
2905 | #endif | ||
2906 | |||
2907 | /* dircache persistence file header magic */ | ||
2908 | #define DIRCACHE_MAGIC 0x00d0c0a1 | ||
2909 | |||
2910 | /* dircache persistence file header */ | ||
2911 | struct dircache_maindata | ||
1518 | { | 2912 | { |
1519 | return check_event_queue(); | 2913 | uint32_t magic; /* DIRCACHE_MAGIC */ |
2914 | struct dircache dircache; /* metadata of the cache! */ | ||
2915 | uint32_t datacrc; /* CRC32 of data */ | ||
2916 | uint32_t hdrcrc; /* CRC32 of header through datacrc */ | ||
2917 | } __attribute__((packed, aligned (4))); | ||
2918 | |||
2919 | /** | ||
2920 | * verify that the clean status is A-ok | ||
2921 | */ | ||
2922 | static bool dircache_is_clean(bool saving) | ||
2923 | { | ||
2924 | if (saving) | ||
2925 | return dircache.dcvol[0].status == DIRCACHE_READY; | ||
2926 | else | ||
2927 | { | ||
2928 | return dircache.dcvol[0].status == DIRCACHE_IDLE && | ||
2929 | !dircache_runinfo.enabled; | ||
2930 | } | ||
1520 | } | 2931 | } |
1521 | 2932 | ||
1522 | DIR_CACHED* opendir_cached(const char* name) | 2933 | /** |
2934 | * function to load the internal cache structure from disk to initialize | ||
2935 | * the dircache really fast with little disk access. | ||
2936 | */ | ||
2937 | int dircache_load(void) | ||
1523 | { | 2938 | { |
1524 | int dd; | 2939 | logf("Loading directory cache"); |
1525 | DIR_CACHED* pdir = opendirs; | 2940 | int fd = open_dircache_file(O_RDONLY); |
2941 | if (fd < 0) | ||
2942 | return -1; | ||
2943 | |||
2944 | int rc = -1; | ||
2945 | |||
2946 | ssize_t size; | ||
2947 | struct dircache_maindata maindata; | ||
2948 | uint32_t crc; | ||
2949 | int handle = 0; | ||
2950 | bool hasbuffer = false; | ||
1526 | 2951 | ||
1527 | if ( name[0] != '/' ) | 2952 | size = sizeof (maindata); |
2953 | if (read(fd, &maindata, size) != size) | ||
1528 | { | 2954 | { |
1529 | DEBUGF("Only absolute paths supported right now\n"); | 2955 | logf("dircache: header read failed"); |
1530 | return NULL; | 2956 | goto error_nolock; |
1531 | } | 2957 | } |
1532 | 2958 | ||
1533 | /* find a free dir descriptor */ | 2959 | /* sanity check the header */ |
1534 | for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++) | 2960 | if (maindata.magic != DIRCACHE_MAGIC) |
1535 | if ( !pdir->busy ) | 2961 | { |
1536 | break; | 2962 | logf("dircache: invalid header magic"); |
2963 | goto error_nolock; | ||
2964 | } | ||
1537 | 2965 | ||
1538 | if ( dd == MAX_OPEN_DIRS ) | 2966 | crc = crc_32(&maindata, offsetof(struct dircache_maindata, hdrcrc), |
2967 | 0xffffffff); | ||
2968 | if (crc != maindata.hdrcrc) | ||
1539 | { | 2969 | { |
1540 | DEBUGF("Too many dirs open\n"); | 2970 | logf("dircache: invalid header CRC32"); |
1541 | errno = EMFILE; | 2971 | goto error_nolock; |
1542 | return NULL; | ||
1543 | } | 2972 | } |
1544 | |||
1545 | pdir->busy = true; | ||
1546 | 2973 | ||
1547 | if (!dircache_initialized || is_disable_msg_pending()) | 2974 | if (maindata.dircache.size != |
2975 | maindata.dircache.sizeentries + maindata.dircache.sizenames || | ||
2976 | ALIGN_DOWN(maindata.dircache.size, ENTRYSIZE) != maindata.dircache.size || | ||
2977 | filesize(fd) - sizeof (maindata) != maindata.dircache.size) | ||
1548 | { | 2978 | { |
1549 | pdir->internal_entry = -1; | 2979 | logf("dircache: file header error"); |
1550 | pdir->regulardir = opendir_uncached(name); | 2980 | goto error_nolock; |
1551 | } | 2981 | } |
1552 | else | 2982 | |
2983 | /* allocate so that exactly the reserve size remains */ | ||
2984 | size_t bufsize = maindata.dircache.size + DIRCACHE_RESERVE + 1; | ||
2985 | handle = alloc_cache(bufsize); | ||
2986 | if (handle <= 0) | ||
2987 | { | ||
2988 | logf("dircache: failed load allocation"); | ||
2989 | goto error_nolock; | ||
2990 | } | ||
2991 | |||
2992 | dircache_lock(); | ||
2993 | buffer_lock(); | ||
2994 | |||
2995 | if (!dircache_is_clean(false)) | ||
2996 | goto error; | ||
2997 | |||
2998 | /* from this point on, we're actually dealing with the cache in RAM */ | ||
2999 | dircache = maindata.dircache; | ||
3000 | |||
3001 | set_buffer(handle, bufsize); | ||
3002 | hasbuffer = true; | ||
3003 | |||
3004 | /* convert back to in-RAM representation */ | ||
3005 | dircache.numentries = maindata.dircache.sizeentries / ENTRYSIZE; | ||
3006 | |||
3007 | /* read the dircache file into memory; start with the entries */ | ||
3008 | size = maindata.dircache.sizeentries; | ||
3009 | if (read(fd, dircache_runinfo.pentry + 1, size) != size) | ||
1553 | { | 3010 | { |
1554 | pdir->regulardir = NULL; | 3011 | logf("dircache read failed #1"); |
1555 | pdir->internal_entry = dircache_get_entry_id_ex(name, true); | 3012 | goto error; |
1556 | pdir->theent.info.attribute = -1; /* used to make readdir_cached aware of the first call */ | ||
1557 | } | 3013 | } |
1558 | 3014 | ||
1559 | if (pdir->internal_entry == -1 && pdir->regulardir == NULL) | 3015 | crc = crc_32(dircache_runinfo.pentry + 1, size, 0xffffffff); |
3016 | |||
3017 | /* continue with the names; fix up indexes to them if needed */ | ||
3018 | dircache.names -= maindata.dircache.sizenames; | ||
3019 | *get_name(dircache.names - 1) = 0; | ||
3020 | |||
3021 | size = maindata.dircache.sizenames; | ||
3022 | if (read(fd, get_name(dircache.names), size) != size) | ||
1560 | { | 3023 | { |
1561 | pdir->busy = false; | 3024 | logf("dircache read failed #2"); |
1562 | return NULL; | 3025 | goto error; |
1563 | } | 3026 | } |
1564 | 3027 | ||
1565 | return pdir; | 3028 | crc = crc_32(get_name(dircache.names), size, crc); |
1566 | } | 3029 | if (crc != maindata.datacrc) |
3030 | { | ||
3031 | logf("dircache: data failed CRC32"); | ||
3032 | goto error; | ||
3033 | } | ||
1567 | 3034 | ||
1568 | struct dirent_cached* readdir_cached(DIR_CACHED* dir) | 3035 | /* only names will be changed in relative position so fix up those |
1569 | { | 3036 | references */ |
1570 | struct dircache_entry *ce = get_entry(dir->internal_entry); | 3037 | ssize_t offset = dircache.names - maindata.dircache.names; |
1571 | struct dirent_uncached *regentry; | 3038 | if (offset != 0) |
1572 | 3039 | { | |
1573 | if (!dir->busy) | 3040 | /* nothing should be open besides the dircache file itself therefore |
1574 | return NULL; | 3041 | no bindings need be resolved; the cache will have its own entry |
3042 | but that should get cleaned up when removing the file */ | ||
3043 | FOR_EACH_CACHE_ENTRY(ce) | ||
3044 | { | ||
3045 | if (!ce->tinyname) | ||
3046 | ce->name += offset; | ||
3047 | } | ||
3048 | } | ||
1575 | 3049 | ||
1576 | if (dir->regulardir != NULL) | 3050 | dircache.reserve_used = 0; |
1577 | { | 3051 | |
1578 | regentry = readdir_uncached(dir->regulardir); | 3052 | /* enable the cache but do not try to build it */ |
1579 | if (regentry == NULL) | 3053 | dircache_enable_internal(false); |
1580 | return NULL; | 3054 | |
1581 | 3055 | /* cache successfully loaded */ | |
1582 | strlcpy(dir->theent.d_name, regentry->d_name, MAX_PATH); | 3056 | logf("Done, %ld KiB used", dircache.size / 1024); |
1583 | dir->theent.startcluster = regentry->startcluster; | 3057 | rc = 0; |
1584 | dir->theent.info = regentry->info; | 3058 | error: |
1585 | 3059 | if (rc < 0 && hasbuffer) | |
1586 | return &dir->theent; | 3060 | reset_buffer(); |
1587 | } | ||
1588 | |||
1589 | /* if theent.attribute=-1 then this is the first call */ | ||
1590 | /* otherwise, this is is not so we first take the entry's ->next */ | ||
1591 | /* NOTE: normal file can't have attribute=-1 */ | ||
1592 | if(dir->theent.info.attribute != -1) | ||
1593 | ce = ce->next; | ||
1594 | /* skip unused entries */ | ||
1595 | while(ce != NULL && ce->d_name == NULL) | ||
1596 | ce = ce->next; | ||
1597 | |||
1598 | if (ce == NULL) | ||
1599 | return NULL; | ||
1600 | 3061 | ||
1601 | strlcpy(dir->theent.d_name, ce->d_name, MAX_PATH); | 3062 | buffer_unlock(); |
1602 | /* Can't do `dir->theent = *ce` | 3063 | dircache_unlock(); |
1603 | because that modifies the d_name pointer. */ | ||
1604 | dir->theent.startcluster = ce->startcluster; | ||
1605 | dir->theent.info = ce->info; | ||
1606 | dir->internal_entry = ce - dircache_root; | ||
1607 | 3064 | ||
1608 | //logf("-> %s", ce->d_name); | 3065 | error_nolock: |
1609 | return &dir->theent; | 3066 | if (rc < 0 && handle > 0) |
3067 | core_free(handle); | ||
3068 | |||
3069 | if (fd >= 0) | ||
3070 | close(fd); | ||
3071 | |||
3072 | remove_dircache_file(); | ||
3073 | return rc; | ||
1610 | } | 3074 | } |
1611 | 3075 | ||
1612 | int closedir_cached(DIR_CACHED* dir) | 3076 | /** |
3077 | * function to save the internal cache stucture to disk for fast loading | ||
3078 | * on boot | ||
3079 | */ | ||
3080 | int dircache_save(void) | ||
1613 | { | 3081 | { |
1614 | if (!dir->busy) | 3082 | logf("Saving directory cache"); |
3083 | |||
3084 | int fd = open_dircache_file(O_WRONLY|O_CREAT|O_TRUNC|O_APPEND); | ||
3085 | if (fd < 0) | ||
1615 | return -1; | 3086 | return -1; |
1616 | |||
1617 | dir->busy=false; | ||
1618 | if (dir->regulardir != NULL) | ||
1619 | return closedir_uncached(dir->regulardir); | ||
1620 | |||
1621 | return 0; | ||
1622 | } | ||
1623 | 3087 | ||
1624 | int mkdir_cached(const char *name) | 3088 | dircache_lock(); |
1625 | { | 3089 | buffer_lock(); |
1626 | int rc=mkdir_uncached(name); | 3090 | |
1627 | if (rc >= 0) | 3091 | int rc = -1; |
1628 | dircache_mkdir(name); | 3092 | |
1629 | return(rc); | 3093 | if (!dircache_is_clean(true)) |
3094 | goto error; | ||
3095 | |||
3096 | /* save the header structure along with the cache metadata */ | ||
3097 | ssize_t size; | ||
3098 | uint32_t crc; | ||
3099 | struct dircache_maindata maindata = | ||
3100 | { | ||
3101 | .magic = DIRCACHE_MAGIC, | ||
3102 | .dircache = dircache, | ||
3103 | }; | ||
3104 | |||
3105 | /* store the size since it better detects an invalid header */ | ||
3106 | maindata.dircache.sizeentries = maindata.dircache.numentries * ENTRYSIZE; | ||
3107 | |||
3108 | /* write the template header */ | ||
3109 | size = sizeof (maindata); | ||
3110 | if (write(fd, &maindata, size) != size) | ||
3111 | { | ||
3112 | logf("dircache: write failed #1"); | ||
3113 | goto error; | ||
3114 | } | ||
3115 | |||
3116 | /* write the dircache entries */ | ||
3117 | size = maindata.dircache.sizeentries; | ||
3118 | if (write(fd, dircache_runinfo.pentry + 1, size) != size) | ||
3119 | { | ||
3120 | logf("dircache: write failed #2"); | ||
3121 | goto error; | ||
3122 | } | ||
3123 | |||
3124 | crc = crc_32(dircache_runinfo.pentry + 1, size, 0xffffffff); | ||
3125 | |||
3126 | /* continue with the names */ | ||
3127 | size = maindata.dircache.sizenames; | ||
3128 | if (write(fd, get_name(dircache.names), size) != size) | ||
3129 | { | ||
3130 | logf("dircache: write failed #3"); | ||
3131 | goto error; | ||
3132 | } | ||
3133 | |||
3134 | crc = crc_32(get_name(dircache.names), size, crc); | ||
3135 | maindata.datacrc = crc; | ||
3136 | |||
3137 | /* rewrite the header with CRC info */ | ||
3138 | if (lseek(fd, 0, SEEK_SET) != 0) | ||
3139 | { | ||
3140 | logf("dircache: seek failed"); | ||
3141 | goto error; | ||
3142 | } | ||
3143 | |||
3144 | crc = crc_32(&maindata, offsetof(struct dircache_maindata, hdrcrc), | ||
3145 | 0xffffffff); | ||
3146 | maindata.hdrcrc = crc; | ||
3147 | |||
3148 | if (write(fd, &maindata, sizeof (maindata)) != sizeof (maindata)) | ||
3149 | { | ||
3150 | logf("dircache: write failed #4"); | ||
3151 | goto error; | ||
3152 | } | ||
3153 | |||
3154 | /* as of now, no changes to the volumes should be allowed at all since | ||
3155 | that makes what was saved completely invalid */ | ||
3156 | rc = 0; | ||
3157 | error: | ||
3158 | buffer_unlock(); | ||
3159 | dircache_unlock(); | ||
3160 | |||
3161 | if (rc < 0) | ||
3162 | remove_dircache_file(); | ||
3163 | |||
3164 | close(fd); | ||
3165 | return rc; | ||
1630 | } | 3166 | } |
3167 | #endif /* HAVE_EEPROM_SETTINGS */ | ||
1631 | 3168 | ||
1632 | int rmdir_cached(const char* name) | 3169 | /** |
3170 | * main one-time initialization function that must be called before any other | ||
3171 | * operations within the dircache | ||
3172 | */ | ||
3173 | void dircache_init(size_t last_size) | ||
1633 | { | 3174 | { |
1634 | int rc=rmdir_uncached(name); | 3175 | queue_init(&dircache_queue, false); |
1635 | if(rc >= 0) | 3176 | |
1636 | dircache_rmdir(name); | 3177 | dircache.last_size = MIN(last_size, DIRCACHE_LIMIT); |
1637 | return(rc); | 3178 | |
3179 | struct dircache_runinfo *dcrip = &dircache_runinfo; | ||
3180 | dcrip->suspended = 1; | ||
3181 | dcrip->thread_done = true; | ||
3182 | dcrip->ops.move_callback = move_callback; | ||
1638 | } | 3183 | } |
diff --git a/firmware/common/disk.c b/firmware/common/disk.c index 5a55a3b6ac..3a2d27e0d7 100644 --- a/firmware/common/disk.c +++ b/firmware/common/disk.c | |||
@@ -19,14 +19,25 @@ | |||
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include <stdio.h> | 21 | #include <stdio.h> |
22 | #include <string.h> | ||
23 | #include "config.h" | ||
22 | #include "kernel.h" | 24 | #include "kernel.h" |
23 | #include "storage.h" | 25 | #include "storage.h" |
24 | #include "debug.h" | 26 | #include "debug.h" |
25 | #include "fat.h" | 27 | #include "disk_cache.h" |
26 | #include "dir.h" /* for release_dirs() */ | 28 | #include "fileobj_mgr.h" |
27 | #include "file.h" /* for release_files() */ | 29 | #include "dir.h" |
30 | #include "dircache_redirect.h" | ||
28 | #include "disk.h" | 31 | #include "disk.h" |
29 | #include <string.h> | 32 | |
33 | #ifndef CONFIG_DEFAULT_PARTNUM | ||
34 | #define CONFIG_DEFAULT_PARTNUM 0 | ||
35 | #endif | ||
36 | |||
37 | #define disk_reader_lock() file_internal_lock_READER() | ||
38 | #define disk_reader_unlock() file_internal_unlock_READER() | ||
39 | #define disk_writer_lock() file_internal_lock_WRITER() | ||
40 | #define disk_writer_unlock() file_internal_unlock_WRITER() | ||
30 | 41 | ||
31 | /* Partition table entry layout: | 42 | /* Partition table entry layout: |
32 | ----------------------- | 43 | ----------------------- |
@@ -42,11 +53,18 @@ | |||
42 | 12-15: nr of sectors in partition | 53 | 12-15: nr of sectors in partition |
43 | */ | 54 | */ |
44 | 55 | ||
45 | #define BYTES2INT32(array,pos) \ | 56 | #define BYTES2INT32(array, pos) \ |
46 | ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ | 57 | (((uint32_t)array[pos+0] << 0) | \ |
47 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | 58 | ((uint32_t)array[pos+1] << 8) | \ |
59 | ((uint32_t)array[pos+2] << 16) | \ | ||
60 | ((uint32_t)array[pos+3] << 24)) | ||
61 | |||
62 | #define BYTES2INT16(array, pos) \ | ||
63 | (((uint32_t)array[pos+0] << 0) | \ | ||
64 | ((uint32_t)array[pos+1] << 8)) | ||
48 | 65 | ||
49 | static const unsigned char fat_partition_types[] = { | 66 | static const unsigned char fat_partition_types[] = |
67 | { | ||
50 | 0x0b, 0x1b, /* FAT32 + hidden variant */ | 68 | 0x0b, 0x1b, /* FAT32 + hidden variant */ |
51 | 0x0c, 0x1c, /* FAT32 (LBA) + hidden variant */ | 69 | 0x0c, 0x1c, /* FAT32 (LBA) + hidden variant */ |
52 | #ifdef HAVE_FAT16SUPPORT | 70 | #ifdef HAVE_FAT16SUPPORT |
@@ -56,159 +74,135 @@ static const unsigned char fat_partition_types[] = { | |||
56 | #endif | 74 | #endif |
57 | }; | 75 | }; |
58 | 76 | ||
59 | static struct partinfo part[NUM_DRIVES*4]; /* space for 4 partitions on 2 drives */ | 77 | /* space for 4 partitions on 2 drives */ |
60 | static int vol_drive[NUM_VOLUMES]; /* mounted to which drive (-1 if none) */ | 78 | static struct partinfo part[NUM_DRIVES*4]; |
61 | static struct mutex disk_mutex; | 79 | /* mounted to which drive (-1 if none) */ |
80 | static int vol_drive[NUM_VOLUMES]; | ||
81 | |||
82 | static int get_free_volume(void) | ||
83 | { | ||
84 | for (int i = 0; i < NUM_VOLUMES; i++) | ||
85 | { | ||
86 | if (vol_drive[i] == -1) /* unassigned? */ | ||
87 | return i; | ||
88 | } | ||
89 | |||
90 | return -1; /* none found */ | ||
91 | } | ||
62 | 92 | ||
63 | #ifdef MAX_LOG_SECTOR_SIZE | 93 | #ifdef MAX_LOG_SECTOR_SIZE |
64 | static int disk_sector_multiplier[NUM_DRIVES] = {[0 ... NUM_DRIVES-1] = 1}; | 94 | static int disk_sector_multiplier[NUM_DRIVES] = |
95 | { [0 ... NUM_DRIVES-1] = 1 }; | ||
65 | 96 | ||
66 | int disk_get_sector_multiplier(IF_MD_NONVOID(int drive)) | 97 | int disk_get_sector_multiplier(IF_MD_NONVOID(int drive)) |
67 | { | 98 | { |
68 | #ifdef HAVE_MULTIDRIVE | 99 | if (!CHECK_DRV(drive)) |
69 | return disk_sector_multiplier[drive]; | 100 | return 0; |
70 | #else | 101 | |
71 | return disk_sector_multiplier[0]; | 102 | disk_reader_lock(); |
72 | #endif | 103 | int multiplier = disk_sector_multiplier[IF_MD_DRV(drive)]; |
104 | disk_reader_unlock(); | ||
105 | return multiplier; | ||
73 | } | 106 | } |
74 | #endif | 107 | #endif /* MAX_LOG_SECTOR_SIZE */ |
75 | 108 | ||
76 | struct partinfo* disk_init(IF_MD_NONVOID(int drive)) | 109 | bool disk_init(IF_MD_NONVOID(int drive)) |
77 | { | 110 | { |
78 | int i; | 111 | if (!CHECK_DRV(drive)) |
79 | #ifdef HAVE_MULTIDRIVE | 112 | return false; /* out of space in table */ |
80 | /* For each drive, start at a different position, in order not to destroy | ||
81 | the first entry of drive 0. | ||
82 | That one is needed to calculate config sector position. */ | ||
83 | struct partinfo* pinfo = &part[drive*4]; | ||
84 | if ((size_t)drive >= sizeof(part)/sizeof(*part)/4) | ||
85 | return NULL; /* out of space in table */ | ||
86 | #else | ||
87 | struct partinfo* pinfo = part; | ||
88 | const int drive = 0; | ||
89 | (void)drive; | ||
90 | #endif | ||
91 | 113 | ||
92 | unsigned char* sector = fat_get_sector_buffer(); | 114 | unsigned char *sector = dc_get_buffer(); |
93 | storage_read_sectors(IF_MD(drive,) 0,1, sector); | 115 | if (!sector) |
94 | /* check that the boot sector is initialized */ | 116 | return false; |
95 | if ( (sector[510] != 0x55) || | ||
96 | (sector[511] != 0xaa)) { | ||
97 | fat_release_sector_buffer(); | ||
98 | DEBUGF("Bad boot sector signature\n"); | ||
99 | return NULL; | ||
100 | } | ||
101 | 117 | ||
102 | /* parse partitions */ | 118 | memset(sector, 0, SECTOR_SIZE); |
103 | for ( i=0; i<4; i++ ) { | 119 | storage_read_sectors(IF_MD(drive,) 0, 1, sector); |
104 | unsigned char* ptr = sector + 0x1be + 16*i; | ||
105 | pinfo[i].type = ptr[4]; | ||
106 | pinfo[i].start = BYTES2INT32(ptr, 8); | ||
107 | pinfo[i].size = BYTES2INT32(ptr, 12); | ||
108 | 120 | ||
109 | DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n", | 121 | bool init = false; |
110 | i,pinfo[i].type,pinfo[i].start,pinfo[i].size); | ||
111 | 122 | ||
112 | /* extended? */ | 123 | /* check that the boot sector is initialized */ |
113 | if ( pinfo[i].type == 5 ) { | 124 | if (BYTES2INT16(sector, 510) == 0xaa55) |
114 | /* not handled yet */ | 125 | { |
115 | } | 126 | /* For each drive, start at a different position, in order not to |
116 | } | 127 | destroy the first entry of drive 0. That one is needed to calculate |
117 | fat_release_sector_buffer(); | 128 | config sector position. */ |
118 | return pinfo; | 129 | struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4]; |
119 | } | ||
120 | 130 | ||
121 | struct partinfo* disk_partinfo(int partition) | 131 | disk_writer_lock(); |
122 | { | ||
123 | return &part[partition]; | ||
124 | } | ||
125 | 132 | ||
126 | void disk_init_subsystem(void) | 133 | /* parse partitions */ |
127 | { | 134 | for (int i = 0; i < 4; i++) |
128 | mutex_init(&disk_mutex); | 135 | { |
129 | } | 136 | unsigned char* ptr = sector + 0x1be + 16*i; |
137 | pinfo[i].type = ptr[4]; | ||
138 | pinfo[i].start = BYTES2INT32(ptr, 8); | ||
139 | pinfo[i].size = BYTES2INT32(ptr, 12); | ||
130 | 140 | ||
131 | int disk_mount_all(void) | 141 | DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n", |
132 | { | 142 | i,pinfo[i].type,pinfo[i].start,pinfo[i].size); |
133 | int mounted=0; | ||
134 | int i; | ||
135 | |||
136 | #ifdef HAVE_HOTSWAP | ||
137 | mutex_lock(&disk_mutex); | ||
138 | #endif | ||
139 | 143 | ||
140 | fat_init(); /* reset all mounted partitions */ | 144 | /* extended? */ |
141 | for (i=0; i<NUM_VOLUMES; i++) | 145 | if ( pinfo[i].type == 5 ) |
142 | vol_drive[i] = -1; /* mark all as unassigned */ | 146 | { |
147 | /* not handled yet */ | ||
148 | } | ||
149 | } | ||
143 | 150 | ||
144 | #ifndef HAVE_MULTIDRIVE | 151 | disk_writer_unlock(); |
145 | mounted = disk_mount(0); | 152 | |
146 | #else | 153 | init = true; |
147 | for(i=0;i<NUM_DRIVES;i++) | 154 | } |
155 | else | ||
148 | { | 156 | { |
149 | #ifdef HAVE_HOTSWAP | 157 | DEBUGF("Bad boot sector signature\n"); |
150 | if (storage_present(i)) | ||
151 | #endif | ||
152 | mounted += disk_mount(i); | ||
153 | } | 158 | } |
154 | #endif | ||
155 | 159 | ||
156 | #ifdef HAVE_HOTSWAP | 160 | dc_release_buffer(sector); |
157 | mutex_unlock(&disk_mutex); | 161 | return init; |
158 | #endif | ||
159 | return mounted; | ||
160 | } | 162 | } |
161 | 163 | ||
162 | static int get_free_volume(void) | 164 | bool disk_partinfo(int partition, struct partinfo *info) |
163 | { | 165 | { |
164 | int i; | 166 | if (partition < 0 || partition >= (int)ARRAYLEN(part) || !info) |
165 | for (i=0; i<NUM_VOLUMES; i++) | 167 | return false; |
166 | { | ||
167 | if (vol_drive[i] == -1) /* unassigned? */ | ||
168 | return i; | ||
169 | } | ||
170 | 168 | ||
171 | return -1; /* none found */ | 169 | disk_reader_lock(); |
170 | *info = part[partition]; | ||
171 | disk_reader_unlock(); | ||
172 | return true; | ||
172 | } | 173 | } |
173 | 174 | ||
174 | int disk_mount(int drive) | 175 | int disk_mount(int drive) |
175 | { | 176 | { |
176 | int mounted = 0; /* reset partition-on-drive flag */ | 177 | int mounted = 0; /* reset partition-on-drive flag */ |
177 | int volume; | ||
178 | struct partinfo* pinfo; | ||
179 | 178 | ||
180 | #ifdef HAVE_HOTSWAP | 179 | disk_writer_lock(); |
181 | mutex_lock(&disk_mutex); | ||
182 | #endif | ||
183 | 180 | ||
184 | volume = get_free_volume(); | 181 | int volume = get_free_volume(); |
185 | pinfo = disk_init(IF_MD(drive)); | ||
186 | #ifdef MAX_LOG_SECTOR_SIZE | ||
187 | disk_sector_multiplier[drive] = 1; | ||
188 | #endif | ||
189 | 182 | ||
190 | if (pinfo == NULL) | 183 | if (!disk_init(IF_MD(drive))) |
191 | { | 184 | { |
192 | #ifdef HAVE_HOTSWAP | 185 | disk_writer_unlock(); |
193 | mutex_unlock(&disk_mutex); | ||
194 | #endif | ||
195 | return 0; | 186 | return 0; |
196 | } | 187 | } |
197 | #if defined(TOSHIBA_GIGABEAT_S) | 188 | |
198 | int i = 1; /* For the Gigabeat S, we mount the second partition */ | 189 | struct partinfo *pinfo = &part[IF_MD_DRV(drive)*4]; |
199 | #else | 190 | #ifdef MAX_LOG_SECTOR_SIZE |
200 | int i = 0; | 191 | disk_sector_multiplier[IF_MD_DRV(drive)] = 1; |
201 | #endif | 192 | #endif |
202 | for (; volume != -1 && i<4 && mounted<NUM_VOLUMES_PER_DRIVE; i++) | 193 | |
194 | for (int i = CONFIG_DEFAULT_PARTNUM; | ||
195 | volume != -1 && i < 4 && mounted < NUM_VOLUMES_PER_DRIVE; | ||
196 | i++) | ||
203 | { | 197 | { |
204 | if (memchr(fat_partition_types, pinfo[i].type, | 198 | if (memchr(fat_partition_types, pinfo[i].type, |
205 | sizeof(fat_partition_types)) == NULL) | 199 | sizeof(fat_partition_types)) == NULL) |
206 | continue; /* not an accepted partition type */ | 200 | continue; /* not an accepted partition type */ |
207 | 201 | ||
208 | #ifdef MAX_LOG_SECTOR_SIZE | 202 | bool success = false; |
209 | int j; | 203 | |
210 | 204 | #ifdef MAX_LOG_SECTOR_SIZE | |
211 | for (j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1) | 205 | for (int j = 1; j <= (MAX_LOG_SECTOR_SIZE/SECTOR_SIZE); j <<= 1) |
212 | { | 206 | { |
213 | if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start * j)) | 207 | if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start * j)) |
214 | { | 208 | { |
@@ -218,93 +212,242 @@ int disk_mount(int drive) | |||
218 | vol_drive[volume] = drive; /* remember the drive for this volume */ | 212 | vol_drive[volume] = drive; /* remember the drive for this volume */ |
219 | volume = get_free_volume(); /* prepare next entry */ | 213 | volume = get_free_volume(); /* prepare next entry */ |
220 | disk_sector_multiplier[drive] = j; | 214 | disk_sector_multiplier[drive] = j; |
215 | success = true; | ||
221 | break; | 216 | break; |
222 | } | 217 | } |
223 | } | 218 | } |
224 | #else | 219 | #else /* ndef MAX_LOG_SECTOR_SIZE */ |
225 | if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start)) | 220 | if (!fat_mount(IF_MV(volume,) IF_MD(drive,) pinfo[i].start)) |
226 | { | 221 | { |
227 | mounted++; | 222 | mounted++; |
228 | vol_drive[volume] = drive; /* remember the drive for this volume */ | 223 | vol_drive[volume] = drive; /* remember the drive for this volume */ |
229 | volume = get_free_volume(); /* prepare next entry */ | 224 | volume = get_free_volume(); /* prepare next entry */ |
225 | success = true; | ||
230 | } | 226 | } |
231 | #endif | 227 | #endif /* MAX_LOG_SECTOR_SIZE */ |
228 | |||
229 | if (success) | ||
230 | volume_onmount_internal(IF_MV(volume)); | ||
232 | } | 231 | } |
233 | 232 | ||
234 | if (mounted == 0 && volume != -1) /* none of the 4 entries worked? */ | 233 | if (mounted == 0 && volume != -1) /* none of the 4 entries worked? */ |
235 | { /* try "superfloppy" mode */ | 234 | { /* try "superfloppy" mode */ |
236 | DEBUGF("No partition found, trying to mount sector 0.\n"); | 235 | DEBUGF("No partition found, trying to mount sector 0.\n"); |
236 | |||
237 | if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0)) | 237 | if (!fat_mount(IF_MV(volume,) IF_MD(drive,) 0)) |
238 | { | 238 | { |
239 | #ifdef MAX_LOG_SECTOR_SIZE | 239 | #ifdef MAX_LOG_SECTOR_SIZE |
240 | disk_sector_multiplier[drive] = fat_get_bytes_per_sector(IF_MV(volume))/SECTOR_SIZE; | 240 | disk_sector_multiplier[drive] = |
241 | #endif | 241 | fat_get_bytes_per_sector(IF_MV(volume)) / SECTOR_SIZE; |
242 | #endif | ||
242 | mounted = 1; | 243 | mounted = 1; |
243 | vol_drive[volume] = drive; /* remember the drive for this volume */ | 244 | vol_drive[volume] = drive; /* remember the drive for this volume */ |
245 | volume_onmount_internal(IF_MV(volume)); | ||
244 | } | 246 | } |
245 | } | 247 | } |
246 | #ifdef HAVE_HOTSWAP | 248 | |
247 | mutex_unlock(&disk_mutex); | 249 | disk_writer_unlock(); |
248 | #endif | 250 | return mounted; |
251 | } | ||
252 | |||
253 | int disk_mount_all(void) | ||
254 | { | ||
255 | int mounted = 0; | ||
256 | |||
257 | disk_writer_lock(); | ||
258 | |||
259 | /* reset all mounted partitions */ | ||
260 | volume_onunmount_internal(IF_MV(-1)); | ||
261 | fat_init(); | ||
262 | |||
263 | for (int i = 0; i < NUM_VOLUMES; i++) | ||
264 | vol_drive[i] = -1; /* mark all as unassigned */ | ||
265 | |||
266 | for (int i = 0; i < NUM_DRIVES; i++) | ||
267 | { | ||
268 | #ifdef HAVE_HOTSWAP | ||
269 | if (storage_present(i)) | ||
270 | #endif | ||
271 | mounted += disk_mount(i); | ||
272 | } | ||
273 | |||
274 | disk_writer_unlock(); | ||
249 | return mounted; | 275 | return mounted; |
250 | } | 276 | } |
251 | 277 | ||
252 | int disk_unmount(int drive) | 278 | int disk_unmount(int drive) |
253 | { | 279 | { |
280 | if (!CHECK_DRV(drive)) | ||
281 | return 0; | ||
282 | |||
254 | int unmounted = 0; | 283 | int unmounted = 0; |
255 | int i; | 284 | |
256 | #ifdef HAVE_HOTSWAP | 285 | disk_writer_lock(); |
257 | mutex_lock(&disk_mutex); | 286 | |
258 | #endif | 287 | for (int i = 0; i < NUM_VOLUMES; i++) |
259 | for (i=0; i<NUM_VOLUMES; i++) | ||
260 | { | 288 | { |
261 | if (vol_drive[i] == drive) | 289 | if (vol_drive[i] == drive) |
262 | { /* force releasing resources */ | 290 | { /* force releasing resources */ |
263 | vol_drive[i] = -1; /* mark unused */ | 291 | vol_drive[i] = -1; /* mark unused */ |
292 | |||
293 | volume_onunmount_internal(IF_MV(i)); | ||
294 | fat_unmount(IF_MV(i)); | ||
295 | |||
264 | unmounted++; | 296 | unmounted++; |
265 | release_files(i); | ||
266 | release_dirs(i); | ||
267 | fat_unmount(i, false); | ||
268 | } | 297 | } |
269 | } | 298 | } |
270 | #ifdef HAVE_HOTSWAP | ||
271 | mutex_unlock(&disk_mutex); | ||
272 | #endif | ||
273 | 299 | ||
300 | disk_writer_unlock(); | ||
274 | return unmounted; | 301 | return unmounted; |
275 | } | 302 | } |
276 | 303 | ||
277 | int disk_unmount_all(void) | 304 | int disk_unmount_all(void) |
278 | { | 305 | { |
279 | #ifndef HAVE_MULTIDRIVE | ||
280 | return disk_unmount(0); | ||
281 | #else /* HAVE_MULTIDRIVE */ | ||
282 | int unmounted = 0; | 306 | int unmounted = 0; |
283 | int i; | 307 | |
284 | for (i = 0; i < NUM_DRIVES; i++) | 308 | disk_writer_lock(); |
309 | |||
310 | volume_onunmount_internal(IF_MV(-1)); | ||
311 | |||
312 | for (int i = 0; i < NUM_DRIVES; i++) | ||
285 | { | 313 | { |
286 | #ifdef HAVE_HOTSWAP | 314 | #ifdef HAVE_HOTSWAP |
287 | if (storage_present(i)) | 315 | if (storage_present(i)) |
288 | #endif | 316 | #endif |
289 | unmounted += disk_unmount(i); | 317 | unmounted += disk_unmount(i); |
290 | } | 318 | } |
291 | 319 | ||
320 | disk_writer_unlock(); | ||
292 | return unmounted; | 321 | return unmounted; |
293 | #endif /* HAVE_MULTIDRIVE */ | 322 | } |
323 | |||
324 | bool disk_present(IF_MD_NONVOID(int drive)) | ||
325 | { | ||
326 | int rc = -1; | ||
327 | |||
328 | if (CHECK_DRV(drive)) | ||
329 | { | ||
330 | void *sector = dc_get_buffer(); | ||
331 | if (sector) | ||
332 | { | ||
333 | rc = storage_read_sectors(IF_MD(drive,) 0, 1, sector); | ||
334 | dc_release_buffer(sector); | ||
335 | } | ||
336 | } | ||
337 | |||
338 | return rc == 0; | ||
339 | } | ||
340 | |||
341 | |||
342 | /** Volume-centric functions **/ | ||
343 | |||
344 | void volume_recalc_free(IF_MV_NONVOID(int volume)) | ||
345 | { | ||
346 | if (!CHECK_VOL(volume)) | ||
347 | return; | ||
348 | |||
349 | /* FIXME: this is crummy but the only way to ensure a correct freecount | ||
350 | if other threads are writing and changing the fsinfo; it is possible | ||
351 | to get multiple threads calling here and also writing and get correct | ||
352 | freespace counts, however a bit complicated to do; if thou desireth I | ||
353 | shall implement the concurrent version -- jethead71 */ | ||
354 | disk_writer_lock(); | ||
355 | fat_recalc_free(IF_MV(volume)); | ||
356 | disk_writer_unlock(); | ||
357 | } | ||
358 | |||
359 | unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume)) | ||
360 | { | ||
361 | if (!CHECK_VOL(volume)) | ||
362 | return 0; | ||
363 | |||
364 | disk_reader_lock(); | ||
365 | unsigned int clustersize = fat_get_cluster_size(IF_MV(volume)); | ||
366 | disk_reader_unlock(); | ||
367 | return clustersize; | ||
368 | } | ||
369 | |||
370 | void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep) | ||
371 | { | ||
372 | disk_reader_lock(); | ||
373 | |||
374 | if (!CHECK_VOL(volume) || !fat_size(IF_MV(volume,) sizep, freep)) | ||
375 | { | ||
376 | if (freep) *sizep = 0; | ||
377 | if (freep) *freep = 0; | ||
378 | } | ||
379 | |||
380 | disk_reader_unlock(); | ||
381 | } | ||
382 | |||
383 | #if defined (HAVE_HOTSWAP) || defined (HAVE_MULTIDRIVE) \ | ||
384 | || defined (HAVE_DIRCACHE) | ||
385 | enum volume_info_type | ||
386 | { | ||
387 | #ifdef HAVE_HOTSWAP | ||
388 | VP_REMOVABLE, | ||
389 | VP_PRESENT, | ||
390 | #endif | ||
391 | #if defined (HAVE_MULTIDRIVE) || defined (HAVE_DIRCACHE) | ||
392 | VP_DRIVE, | ||
393 | #endif | ||
394 | }; | ||
395 | |||
396 | static int volume_properties(int volume, enum volume_info_type infotype) | ||
397 | { | ||
398 | int res = -1; | ||
399 | |||
400 | disk_reader_lock(); | ||
401 | |||
402 | if (CHECK_VOL(volume)) | ||
403 | { | ||
404 | int vd = vol_drive[volume]; | ||
405 | switch (infotype) | ||
406 | { | ||
407 | #ifdef HAVE_HOTSWAP | ||
408 | case VP_REMOVABLE: | ||
409 | res = storage_removable(vd) ? 1 : 0; | ||
410 | break; | ||
411 | case VP_PRESENT: | ||
412 | res = storage_present(vd) ? 1 : 0; | ||
413 | break; | ||
414 | #endif | ||
415 | #if defined(HAVE_MULTIDRIVE) || defined(HAVE_DIRCACHE) | ||
416 | case VP_DRIVE: | ||
417 | res = vd; | ||
418 | break; | ||
419 | #endif | ||
420 | } | ||
421 | } | ||
422 | |||
423 | disk_reader_unlock(); | ||
424 | return res; | ||
294 | } | 425 | } |
295 | 426 | ||
296 | #ifdef HAVE_HOTSWAP | 427 | #ifdef HAVE_HOTSWAP |
297 | bool volume_removable(int volume) | 428 | bool volume_removable(int volume) |
298 | { | 429 | { |
299 | if(vol_drive[volume] == -1) | 430 | return volume_properties(volume, VP_REMOVABLE) > 0; |
300 | return false; | ||
301 | return storage_removable(vol_drive[volume]); | ||
302 | } | 431 | } |
303 | 432 | ||
304 | bool volume_present(int volume) | 433 | bool volume_present(int volume) |
305 | { | 434 | { |
306 | if(vol_drive[volume] == -1) | 435 | return volume_properties(volume, VP_PRESENT) > 0; |
307 | return false; | ||
308 | return storage_present(vol_drive[volume]); | ||
309 | } | 436 | } |
310 | #endif | 437 | #endif /* HAVE_HOTSWAP */ |
438 | |||
439 | #ifdef HAVE_MULTIDRIVE | ||
440 | int volume_drive(int volume) | ||
441 | { | ||
442 | return volume_properties(volume, VP_DRIVE); | ||
443 | } | ||
444 | #endif /* HAVE_MULTIDRIVE */ | ||
445 | |||
446 | #ifdef HAVE_DIRCACHE | ||
447 | bool volume_ismounted(IF_MV_NONVOID(int volume)) | ||
448 | { | ||
449 | return volume_properties(IF_MV_VOL(volume), VP_DRIVE) >= 0; | ||
450 | } | ||
451 | #endif /* HAVE_DIRCACHE */ | ||
452 | |||
453 | #endif /* HAVE_HOTSWAP || HAVE_MULTIDRIVE || HAVE_DIRCACHE */ | ||
diff --git a/firmware/common/disk_cache.c b/firmware/common/disk_cache.c new file mode 100644 index 0000000000..0e842e7796 --- /dev/null +++ b/firmware/common/disk_cache.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "debug.h" | ||
23 | #include "system.h" | ||
24 | #include "linked_list.h" | ||
25 | #include "disk_cache.h" | ||
26 | #include "fat.h" /* for SECTOR_SIZE */ | ||
27 | #include "bitarray.h" | ||
28 | |||
29 | /* Cache: LRU cache with separately-chained hashtable | ||
30 | * | ||
31 | * Each entry of the map is the mapped location of the hashed sector value | ||
32 | * where each bit in each map entry indicates which corresponding cache | ||
33 | * entries are occupied by sector values that collide in that map entry. | ||
34 | * | ||
35 | * Each volume is given its own bit map. | ||
36 | * | ||
37 | * To probe for a specific key, each bit in the map entry must be examined, | ||
38 | * its position used as an index into the cache_entry array and the actual | ||
39 | * sector information compared for that cache entry. If the search exhausts | ||
40 | * all bits, the sector is not cached. | ||
41 | * | ||
42 | * To avoid long chains, the map entry count should be much greater than the | ||
43 | * number of cache entries. Since the cache is an LRU design, no buffer entry | ||
44 | * in the array is intrinsically associated with any particular sector number | ||
45 | * or volume. | ||
46 | * | ||
47 | * Example 6-sector cache with 8-entry map: | ||
48 | * cache entry 543210 | ||
49 | * cache map 100000 <- sector number hashes into map | ||
50 | * 000000 | ||
51 | * 000100 | ||
52 | * 000000 | ||
53 | * 010000 | ||
54 | * 000000 | ||
55 | * 001001 <- collision | ||
56 | * 000000 | ||
57 | * volume map 111101 <- entry usage by the volume (OR of all map entries) | ||
58 | */ | ||
59 | |||
60 | enum dce_flags /* flags for each cache entry */ | ||
61 | { | ||
62 | DCE_INUSE = 0x01, /* entry in use and valid */ | ||
63 | DCE_DIRTY = 0x02, /* entry is dirty in need of writeback */ | ||
64 | DCE_BUF = 0x04, /* entry is being used as a general buffer */ | ||
65 | }; | ||
66 | |||
67 | struct disk_cache_entry | ||
68 | { | ||
69 | struct lldc_node node; /* LRU list links */ | ||
70 | unsigned char flags; /* entry flags */ | ||
71 | #ifdef HAVE_MULTIVOLUME | ||
72 | unsigned char volume; /* volume of sector */ | ||
73 | #endif | ||
74 | unsigned long sector; /* cached disk sector number */ | ||
75 | }; | ||
76 | |||
77 | BITARRAY_TYPE_DECLARE(cache_map_entry_t, cache_map, DC_NUM_ENTRIES) | ||
78 | |||
79 | static inline unsigned int map_sector(unsigned long sector) | ||
80 | { | ||
81 | /* keep sector hash simple for now */ | ||
82 | return sector % DC_MAP_NUM_ENTRIES; | ||
83 | } | ||
84 | |||
85 | static struct lldc_head cache_lru; /* LRU cache list (head = LRU item) */ | ||
86 | static struct disk_cache_entry cache_entry[DC_NUM_ENTRIES]; | ||
87 | static cache_map_entry_t cache_map_entry[NUM_VOLUMES][DC_MAP_NUM_ENTRIES]; | ||
88 | static cache_map_entry_t cache_vol_map[NUM_VOLUMES] IBSS_ATTR; | ||
89 | static uint8_t cache_buffer[DC_NUM_ENTRIES][DC_CACHE_BUFSIZE] CACHEALIGN_ATTR; | ||
90 | struct mutex disk_cache_mutex SHAREDBSS_ATTR; | ||
91 | |||
92 | #define CACHE_MAP_ENTRY(volume, mapnum) \ | ||
93 | cache_map_entry[IF_MV_VOL(volume)][mapnum] | ||
94 | #define CACHE_VOL_MAP(volume) \ | ||
95 | cache_vol_map[IF_MV_VOL(volume)] | ||
96 | |||
97 | #define DCE_LRU() ((struct disk_cache_entry *)cache_lru.head) | ||
98 | #define DCE_NEXT(fce) ((struct disk_cache_entry *)(fce)->node.next) | ||
99 | #define NODE_DCE(node) ((struct disk_cache_entry *)(node)) | ||
100 | |||
101 | /* get the cache index from a pointer to a buffer */ | ||
102 | #define DCIDX_FROM_BUF(buf) \ | ||
103 | ((uint8_t (*)[DC_CACHE_BUFSIZE])(buf) - cache_buffer) | ||
104 | |||
105 | #define DCIDX_FROM_DCE(dce) \ | ||
106 | ((dce) - cache_entry) | ||
107 | |||
108 | /* set the in-use bit in the map */ | ||
109 | static inline void cache_bitmap_set_bit(int volume, unsigned int mapnum, | ||
110 | unsigned int bitnum) | ||
111 | { | ||
112 | cache_map_set_bit(&CACHE_MAP_ENTRY(volume, mapnum), bitnum); | ||
113 | cache_map_set_bit(&CACHE_VOL_MAP(volume), bitnum); | ||
114 | (void)volume; | ||
115 | } | ||
116 | |||
117 | /* clear the in-use bit in the map */ | ||
118 | static inline void cache_bitmap_clear_bit(int volume, unsigned int mapnum, | ||
119 | unsigned int bitnum) | ||
120 | { | ||
121 | cache_map_clear_bit(&CACHE_MAP_ENTRY(volume, mapnum), bitnum); | ||
122 | cache_map_clear_bit(&CACHE_VOL_MAP(volume), bitnum); | ||
123 | (void)volume; | ||
124 | } | ||
125 | |||
126 | /* make entry MRU by moving it to the list tail */ | ||
127 | static inline void touch_cache_entry(struct disk_cache_entry *which) | ||
128 | { | ||
129 | struct lldc_node *lru = cache_lru.head; | ||
130 | struct lldc_node *node = &which->node; | ||
131 | |||
132 | if (node == lru->prev) /* already MRU */ | ||
133 | ; /**/ | ||
134 | else if (node == lru) /* is the LRU? just rotate list */ | ||
135 | cache_lru.head = lru->next; | ||
136 | else /* somewhere else; move it */ | ||
137 | { | ||
138 | lldc_remove(&cache_lru, node); | ||
139 | lldc_insert_last(&cache_lru, node); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | /* remove LRU entry from the cache list to use as a buffer */ | ||
144 | static struct disk_cache_entry * cache_remove_lru_entry(void) | ||
145 | { | ||
146 | struct lldc_node *lru = cache_lru.head; | ||
147 | |||
148 | /* at least one is reserved for client */ | ||
149 | if (lru == lru->next) | ||
150 | return NULL; | ||
151 | |||
152 | /* remove it; next-LRU becomes the LRU */ | ||
153 | lldc_remove(&cache_lru, lru); | ||
154 | return NODE_DCE(lru); | ||
155 | } | ||
156 | |||
157 | /* return entry to the cache list and set it LRU */ | ||
158 | static void cache_return_lru_entry(struct disk_cache_entry *fce) | ||
159 | { | ||
160 | lldc_insert_first(&cache_lru, &fce->node); | ||
161 | } | ||
162 | |||
163 | /* discard the entry's data and mark it unused */ | ||
164 | static inline void cache_discard_entry(struct disk_cache_entry *dce, | ||
165 | unsigned int index) | ||
166 | { | ||
167 | cache_bitmap_clear_bit(IF_MV_VOL(dce->volume), map_sector(dce->sector), | ||
168 | index); | ||
169 | dce->flags = 0; | ||
170 | } | ||
171 | |||
172 | /* search the cache for the specified sector, returning a buffer, either | ||
173 | to the specified sector, if it exists, or a new/evicted entry that must | ||
174 | be filled */ | ||
175 | void * dc_cache_probe(IF_MV(int volume,) unsigned long sector, | ||
176 | unsigned int *flagsp) | ||
177 | { | ||
178 | unsigned int mapnum = map_sector(sector); | ||
179 | |||
180 | FOR_EACH_BITARRAY_SET_BIT(&CACHE_MAP_ENTRY(volume, mapnum), index) | ||
181 | { | ||
182 | struct disk_cache_entry *dce = &cache_entry[index]; | ||
183 | |||
184 | if (dce->sector == sector) | ||
185 | { | ||
186 | *flagsp = DCE_INUSE; | ||
187 | touch_cache_entry(dce); | ||
188 | return cache_buffer[index]; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /* sector not found so the LRU is the victim */ | ||
193 | struct disk_cache_entry *dce = DCE_LRU(); | ||
194 | cache_lru.head = dce->node.next; | ||
195 | |||
196 | unsigned int index = DCIDX_FROM_DCE(dce); | ||
197 | void *buf = cache_buffer[index]; | ||
198 | unsigned int old_flags = dce->flags; | ||
199 | |||
200 | if (old_flags) | ||
201 | { | ||
202 | int old_volume = IF_MV_VOL(dce->volume); | ||
203 | unsigned long sector = dce->sector; | ||
204 | unsigned int old_mapnum = map_sector(sector); | ||
205 | |||
206 | if (old_flags & DCE_DIRTY) | ||
207 | dc_writeback_callback(IF_MV(old_volume,) sector, buf); | ||
208 | |||
209 | if (mapnum == old_mapnum IF_MV( && volume == old_volume )) | ||
210 | goto finish_setup; | ||
211 | |||
212 | cache_bitmap_clear_bit(old_volume, old_mapnum, index); | ||
213 | } | ||
214 | |||
215 | cache_bitmap_set_bit(IF_MV_VOL(volume), mapnum, index); | ||
216 | |||
217 | finish_setup: | ||
218 | dce->flags = DCE_INUSE; | ||
219 | #ifdef HAVE_MULTIVOLUME | ||
220 | dce->volume = volume; | ||
221 | #endif | ||
222 | dce->sector = sector; | ||
223 | |||
224 | *flagsp = 0; | ||
225 | return buf; | ||
226 | } | ||
227 | |||
228 | /* mark in-use cache entry as dirty by buffer */ | ||
229 | void dc_dirty_buf(void *buf) | ||
230 | { | ||
231 | unsigned int index = DCIDX_FROM_BUF(buf); | ||
232 | |||
233 | if (index >= DC_NUM_ENTRIES) | ||
234 | return; | ||
235 | |||
236 | /* dirt remains, sticky until flushed */ | ||
237 | struct disk_cache_entry *fce = &cache_entry[index]; | ||
238 | if (fce->flags & DCE_INUSE) | ||
239 | fce->flags |= DCE_DIRTY; | ||
240 | } | ||
241 | |||
242 | /* discard in-use cache entry by buffer */ | ||
243 | void dc_discard_buf(void *buf) | ||
244 | { | ||
245 | unsigned int index = DCIDX_FROM_BUF(buf); | ||
246 | |||
247 | if (index >= DC_NUM_ENTRIES) | ||
248 | return; | ||
249 | |||
250 | struct disk_cache_entry *dce = &cache_entry[index]; | ||
251 | if (dce->flags & DCE_INUSE) | ||
252 | cache_discard_entry(dce, index); | ||
253 | } | ||
254 | |||
255 | /* commit all dirty cache entries to storage for a specified volume */ | ||
256 | void dc_commit_all(IF_MV_NONVOID(int volume)) | ||
257 | { | ||
258 | DEBUGF("dc_commit_all()\n"); | ||
259 | |||
260 | FOR_EACH_BITARRAY_SET_BIT(&CACHE_VOL_MAP(volume), index) | ||
261 | { | ||
262 | struct disk_cache_entry *dce = &cache_entry[index]; | ||
263 | unsigned int flags = dce->flags; | ||
264 | |||
265 | if (flags & DCE_DIRTY) | ||
266 | { | ||
267 | dc_writeback_callback(IF_MV(volume,) dce->sector, | ||
268 | cache_buffer[index]); | ||
269 | dce->flags = flags & ~DCE_DIRTY; | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* discard all cache entries from the specified volume */ | ||
275 | void dc_discard_all(IF_MV_NONVOID(int volume)) | ||
276 | { | ||
277 | DEBUGF("dc_discard_all()\n"); | ||
278 | |||
279 | FOR_EACH_BITARRAY_SET_BIT(&CACHE_VOL_MAP(volume), index) | ||
280 | cache_discard_entry(&cache_entry[index], index); | ||
281 | } | ||
282 | |||
283 | /* expropriate a buffer from the cache */ | ||
284 | void * dc_get_buffer(void) | ||
285 | { | ||
286 | dc_lock_cache(); | ||
287 | |||
288 | void *buf = NULL; | ||
289 | struct disk_cache_entry *dce = cache_remove_lru_entry(); | ||
290 | |||
291 | if (dce) | ||
292 | { | ||
293 | unsigned int index = DCIDX_FROM_DCE(dce); | ||
294 | unsigned int flags = dce->flags; | ||
295 | |||
296 | buf = cache_buffer[index]; | ||
297 | |||
298 | if (flags) | ||
299 | { | ||
300 | /* must first commit this sector if dirty */ | ||
301 | if (flags & DCE_DIRTY) | ||
302 | dc_writeback_callback(IF_MV(dce->volume,) dce->sector, buf); | ||
303 | |||
304 | cache_discard_entry(dce, index); | ||
305 | } | ||
306 | |||
307 | dce->flags = DCE_BUF; | ||
308 | } | ||
309 | /* cache is out of buffers */ | ||
310 | |||
311 | dc_unlock_cache(); | ||
312 | return buf; | ||
313 | } | ||
314 | |||
315 | /* return buffer to the cache by buffer */ | ||
316 | void dc_release_buffer(void *buf) | ||
317 | { | ||
318 | unsigned int index = DCIDX_FROM_BUF(buf); | ||
319 | |||
320 | if (index >= DC_NUM_ENTRIES) | ||
321 | return; | ||
322 | |||
323 | dc_lock_cache(); | ||
324 | |||
325 | struct disk_cache_entry *dce = &cache_entry[index]; | ||
326 | |||
327 | if (dce->flags & DCE_BUF) | ||
328 | { | ||
329 | dce->flags = 0; | ||
330 | cache_return_lru_entry(dce); | ||
331 | } | ||
332 | |||
333 | dc_unlock_cache(); | ||
334 | } | ||
335 | |||
336 | /* one-time init at startup */ | ||
337 | void dc_init(void) | ||
338 | { | ||
339 | mutex_init(&disk_cache_mutex); | ||
340 | lldc_init(&cache_lru); | ||
341 | for (unsigned int i = 0; i < DC_NUM_ENTRIES; i++) | ||
342 | lldc_insert_last(&cache_lru, &cache_entry[i].node); | ||
343 | } | ||
diff --git a/firmware/common/file.c b/firmware/common/file.c index 920eada84e..7d3b5092ae 100644 --- a/firmware/common/file.c +++ b/firmware/common/file.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2002 by Björn Stenberg | 10 | * Copyright (C) 2002 by Björn Stenberg |
11 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -18,819 +19,1201 @@ | |||
18 | * KIND, either express or implied. | 19 | * KIND, either express or implied. |
19 | * | 20 | * |
20 | ****************************************************************************/ | 21 | ****************************************************************************/ |
22 | #define RB_FILESYSTEM_OS | ||
23 | #include "config.h" | ||
24 | #include "system.h" | ||
21 | #include <string.h> | 25 | #include <string.h> |
22 | #include <errno.h> | 26 | #include <errno.h> |
23 | #include <stdbool.h> | ||
24 | #include "file.h" | ||
25 | #include "fat.h" | ||
26 | #include "dir_uncached.h" | ||
27 | #include "debug.h" | 27 | #include "debug.h" |
28 | #include "dircache.h" | 28 | #include "file.h" |
29 | #include "filefuncs.h" | 29 | #include "fileobj_mgr.h" |
30 | #include "system.h" | 30 | #include "disk_cache.h" |
31 | #include "dircache_redirect.h" | ||
32 | #include "string-extra.h" | ||
33 | |||
34 | /** | ||
35 | * These functions provide a roughly POSIX-compatible file I/O API. | ||
36 | */ | ||
37 | |||
38 | /* structure used for open file descriptors */ | ||
39 | static struct filestr_desc | ||
40 | { | ||
41 | struct filestr_base stream; /* basic stream info (first!) */ | ||
42 | file_size_t offset; /* current offset for stream */ | ||
43 | file_size_t *sizep; /* shortcut to file size in fileobj */ | ||
44 | } open_streams[MAX_OPEN_FILES]; | ||
31 | 45 | ||
32 | /* | 46 | /* check and return a struct filestr_desc* from a file descriptor number */ |
33 | These functions provide a roughly POSIX-compatible file IO API. | 47 | static struct filestr_desc * get_filestr(int fildes) |
48 | { | ||
49 | struct filestr_desc *file = &open_streams[fildes]; | ||
34 | 50 | ||
35 | Since the fat32 driver only manages sectors, we maintain a one-sector | 51 | if ((unsigned int)fildes >= MAX_OPEN_FILES) |
36 | cache for each open file. This way we can provide byte access without | 52 | file = NULL; |
37 | having to re-read the sector each time. | 53 | else if (file->stream.flags & FDO_BUSY) |
38 | The penalty is the RAM used for the cache and slightly more complex code. | 54 | return file; |
39 | */ | ||
40 | 55 | ||
41 | struct filedesc { | 56 | DEBUGF("fildes %d: bad file number\n", fildes); |
42 | unsigned char cache[SECTOR_SIZE] CACHEALIGN_ATTR; | 57 | errno = (file && file->stream.flags == FV_NONEXIST) ? ENXIO : EBADF; |
43 | int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */ | 58 | return NULL; |
44 | long fileoffset; | 59 | } |
45 | long size; | ||
46 | int attr; | ||
47 | struct fat_file fatfile; | ||
48 | bool busy; | ||
49 | bool write; | ||
50 | bool dirty; | ||
51 | bool trunc; | ||
52 | } CACHEALIGN_ATTR; | ||
53 | 60 | ||
54 | static struct filedesc openfiles[MAX_OPEN_FILES] CACHEALIGN_ATTR; | 61 | #define GET_FILESTR(type, fildes) \ |
62 | ({ \ | ||
63 | file_internal_lock_##type(); \ | ||
64 | struct filestr_desc * _file = get_filestr(fildes); \ | ||
65 | if (_file) \ | ||
66 | FILESTR_LOCK(type, &_file->stream); \ | ||
67 | else \ | ||
68 | file_internal_unlock_##type(); \ | ||
69 | _file; \ | ||
70 | }) | ||
71 | |||
72 | /* release the lock on the filestr_desc* */ | ||
73 | #define RELEASE_FILESTR(type, file) \ | ||
74 | ({ \ | ||
75 | FILESTR_UNLOCK(type, &(file)->stream); \ | ||
76 | file_internal_unlock_##type(); \ | ||
77 | }) | ||
78 | |||
79 | /* find a free file descriptor */ | ||
80 | static int alloc_filestr(struct filestr_desc **filep) | ||
81 | { | ||
82 | for (int fildes = 0; fildes < MAX_OPEN_FILES; fildes++) | ||
83 | { | ||
84 | struct filestr_desc *file = &open_streams[fildes]; | ||
85 | if (!file->stream.flags) | ||
86 | { | ||
87 | *filep = file; | ||
88 | return fildes; | ||
89 | } | ||
90 | } | ||
55 | 91 | ||
56 | static int flush_cache(int fd); | 92 | DEBUGF("Too many files open\n"); |
93 | return -1; | ||
94 | } | ||
57 | 95 | ||
58 | int file_creat(const char *pathname) | 96 | /* return the file size in sectors */ |
97 | static inline unsigned long filesize_sectors(file_size_t size) | ||
59 | { | 98 | { |
60 | return open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666); | 99 | /* overflow proof whereas "(x + y - 1) / y" is not */ |
100 | unsigned long numsectors = size / SECTOR_SIZE; | ||
101 | |||
102 | if (size % SECTOR_SIZE) | ||
103 | numsectors++; | ||
104 | |||
105 | return numsectors; | ||
61 | } | 106 | } |
62 | 107 | ||
63 | static int open_internal(const char* pathname, int flags, bool use_cache) | 108 | /* flush a dirty cache buffer */ |
109 | static int flush_cache(struct filestr_desc *file) | ||
64 | { | 110 | { |
65 | DIR_UNCACHED* dir; | ||
66 | struct dirent_uncached* entry; | ||
67 | int fd; | ||
68 | int pathnamesize = strlen(pathname) + 1; | ||
69 | char pathnamecopy[pathnamesize]; | ||
70 | char* name; | ||
71 | struct filedesc* file = NULL; | ||
72 | int rc; | 111 | int rc; |
73 | #ifndef HAVE_DIRCACHE | 112 | struct filestr_cache *cachep = file->stream.cachep; |
74 | (void)use_cache; | ||
75 | #endif | ||
76 | 113 | ||
77 | LDEBUGF("open(\"%s\",%d)\n",pathname,flags); | 114 | DEBUGF("Flushing dirty sector cache (%lu)\n", cachep->sector); |
78 | 115 | ||
79 | if ( pathname[0] != '/' ) { | 116 | if (fat_query_sectornum(&file->stream.fatstr) != cachep->sector) |
80 | DEBUGF("'%s' is not an absolute path.\n",pathname); | 117 | { |
81 | DEBUGF("Only absolute pathnames supported at the moment\n"); | 118 | /* get on the correct sector */ |
82 | errno = EINVAL; | 119 | rc = fat_seek(&file->stream.fatstr, cachep->sector); |
83 | return -1; | 120 | if (rc < 0) |
121 | FILE_ERROR(EIO, rc * 10 - 1); | ||
84 | } | 122 | } |
85 | 123 | ||
86 | /* find a free file descriptor */ | 124 | rc = fat_readwrite(&file->stream.fatstr, 1, cachep->buffer, true); |
87 | for ( fd=0; fd<MAX_OPEN_FILES; fd++ ) | 125 | if (rc < 0) |
88 | if ( !openfiles[fd].busy ) | 126 | { |
89 | break; | 127 | if (rc == FAT_RC_ENOSPC) |
90 | 128 | FILE_ERROR(ENOSPC, RC); | |
91 | if ( fd == MAX_OPEN_FILES ) { | 129 | else |
92 | DEBUGF("Too many files open\n"); | 130 | FILE_ERROR(EIO, rc * 10 - 2); |
93 | errno = EMFILE; | ||
94 | return -2; | ||
95 | } | 131 | } |
96 | 132 | ||
97 | file = &openfiles[fd]; | 133 | cachep->flags = 0; |
98 | memset(file, 0, sizeof(struct filedesc)); | 134 | return 1; |
135 | file_error: | ||
136 | DEBUGF("Failed flushing cache: %d\n", rc); | ||
137 | return rc; | ||
138 | } | ||
139 | |||
140 | static void discard_cache(struct filestr_desc *file) | ||
141 | { | ||
142 | struct filestr_cache *const cachep = file->stream.cachep; | ||
143 | cachep->flags = 0; | ||
144 | } | ||
99 | 145 | ||
100 | if (flags & (O_RDWR | O_WRONLY)) { | 146 | /* set the file pointer */ |
101 | file->write = true; | 147 | static off_t lseek_internal(struct filestr_desc *file, off_t offset, |
148 | int whence) | ||
149 | { | ||
150 | off_t rc; | ||
151 | file_size_t pos; | ||
102 | 152 | ||
103 | if (flags & O_TRUNC) | 153 | file_size_t size = MIN(*file->sizep, FILE_SIZE_MAX); |
104 | file->trunc = true; | ||
105 | } | ||
106 | file->busy = true; | ||
107 | 154 | ||
108 | #ifdef HAVE_DIRCACHE | 155 | switch (whence) |
109 | if (dircache_is_enabled() && !file->write && use_cache) | ||
110 | { | 156 | { |
111 | # ifdef HAVE_MULTIVOLUME | 157 | case SEEK_SET: |
112 | int volume = strip_volume(pathname, pathnamecopy); | 158 | if (offset < 0 || (file_size_t)offset > size) |
113 | # endif | 159 | FILE_ERROR(EINVAL, -1); |
114 | 160 | ||
115 | int ce = dircache_get_entry_id(pathname); | 161 | pos = offset; |
116 | if (ce < 0) | 162 | break; |
117 | { | ||
118 | errno = ENOENT; | ||
119 | file->busy = false; | ||
120 | return -7; | ||
121 | } | ||
122 | 163 | ||
123 | long startcluster = _dircache_get_entry_startcluster(ce); | 164 | case SEEK_CUR: |
124 | fat_open(IF_MV(volume,) | 165 | if ((offset < 0 && (file_size_t)-offset > file->offset) || |
125 | startcluster, | 166 | (offset > 0 && (file_size_t)offset > size - file->offset)) |
126 | &(file->fatfile), | 167 | FILE_ERROR(EINVAL, -1); |
127 | NULL); | ||
128 | struct dirinfo *info = _dircache_get_entry_dirinfo(ce); | ||
129 | file->size = info->size; | ||
130 | file->attr = info->attribute; | ||
131 | file->cacheoffset = -1; | ||
132 | file->fileoffset = 0; | ||
133 | 168 | ||
134 | return fd; | 169 | pos = file->offset + offset; |
135 | } | 170 | break; |
136 | #endif | ||
137 | 171 | ||
138 | strlcpy(pathnamecopy, pathname, pathnamesize); | 172 | case SEEK_END: |
173 | if (offset > 0 || (file_size_t)-offset > size) | ||
174 | FILE_ERROR(EINVAL, -1); | ||
139 | 175 | ||
140 | /* locate filename */ | 176 | pos = size + offset; |
141 | name=strrchr(pathnamecopy+1,'/'); | 177 | break; |
142 | if ( name ) { | 178 | |
143 | *name = 0; | 179 | default: |
144 | dir = opendir_uncached(pathnamecopy); | 180 | FILE_ERROR(EINVAL, -1); |
145 | *name = '/'; | ||
146 | name++; | ||
147 | } | ||
148 | else { | ||
149 | dir = opendir_uncached("/"); | ||
150 | name = pathnamecopy+1; | ||
151 | } | ||
152 | if (!dir) { | ||
153 | DEBUGF("Failed opening dir\n"); | ||
154 | errno = EIO; | ||
155 | file->busy = false; | ||
156 | return -4; | ||
157 | } | ||
158 | |||
159 | if(name[0] == 0) { | ||
160 | DEBUGF("Empty file name\n"); | ||
161 | errno = EINVAL; | ||
162 | file->busy = false; | ||
163 | closedir_uncached(dir); | ||
164 | return -5; | ||
165 | } | ||
166 | |||
167 | /* scan dir for name */ | ||
168 | while ((entry = readdir_uncached(dir))) { | ||
169 | if ( !strcasecmp(name, entry->d_name) ) { | ||
170 | fat_open(IF_MV(dir->fatdir.file.volume,) | ||
171 | entry->startcluster, | ||
172 | &(file->fatfile), | ||
173 | &(dir->fatdir)); | ||
174 | file->size = file->trunc ? 0 : entry->info.size; | ||
175 | file->attr = entry->info.attribute; | ||
176 | break; | ||
177 | } | ||
178 | } | 181 | } |
179 | 182 | ||
180 | if ( !entry ) { | 183 | file->offset = pos; |
181 | LDEBUGF("Didn't find file %s\n",name); | 184 | |
182 | if ( file->write && (flags & O_CREAT) ) { | 185 | return pos; |
183 | rc = fat_create_file(name, | 186 | file_error: |
184 | &(file->fatfile), | 187 | return rc; |
185 | &(dir->fatdir)); | 188 | } |
186 | if (rc < 0) { | 189 | |
187 | DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy); | 190 | /* callback for each file stream to make sure all data is in sync with new |
188 | errno = EIO; | 191 | size */ |
189 | file->busy = false; | 192 | void ftruncate_internal_callback(struct filestr_base *stream, |
190 | closedir_uncached(dir); | 193 | struct filestr_base *s) |
191 | return rc * 10 - 6; | 194 | { |
195 | struct filestr_desc *file = (struct filestr_desc *)s; | ||
196 | file_size_t size = *file->sizep; | ||
197 | |||
198 | /* caches with data beyond new extents are invalid */ | ||
199 | unsigned long sector = file->stream.cachep->sector; | ||
200 | if (sector != INVALID_SECNUM && sector >= filesize_sectors(size)) | ||
201 | filestr_discard_cache(&file->stream); | ||
202 | |||
203 | /* keep all positions within bounds */ | ||
204 | if (file->offset > size) | ||
205 | file->offset = size; | ||
206 | |||
207 | (void)stream; | ||
208 | } | ||
209 | |||
210 | /* truncate the file to the specified length */ | ||
211 | static int ftruncate_internal(struct filestr_desc *file, file_size_t size, | ||
212 | bool write_now) | ||
213 | { | ||
214 | int rc = 0, rc2 = 1; | ||
215 | |||
216 | file_size_t cursize = *file->sizep; | ||
217 | file_size_t truncsize = MIN(size, cursize); | ||
218 | |||
219 | if (write_now) | ||
220 | { | ||
221 | unsigned long sector = filesize_sectors(truncsize); | ||
222 | struct filestr_cache *const cachep = file->stream.cachep; | ||
223 | |||
224 | if (cachep->flags == (FSC_NEW|FSC_DIRTY) && | ||
225 | cachep->sector + 1 == sector) | ||
226 | { | ||
227 | /* sector created but may have never been added to the cluster | ||
228 | chain; flush it now or the subsequent may fail */ | ||
229 | rc2 = flush_cache(file); | ||
230 | if (rc2 == FAT_RC_ENOSPC) | ||
231 | { | ||
232 | /* no space left on device; further truncation needed */ | ||
233 | discard_cache(file); | ||
234 | truncsize = ALIGN_DOWN(truncsize - 1, SECTOR_SIZE); | ||
235 | sector--; | ||
236 | rc = rc2; | ||
192 | } | 237 | } |
193 | #ifdef HAVE_DIRCACHE | 238 | else if (rc2 < 0) |
194 | dircache_add_file(pathname, file->fatfile.firstcluster); | 239 | FILE_ERROR(ERRNO, rc2 * 10 - 1); |
195 | #endif | ||
196 | file->size = 0; | ||
197 | file->attr = 0; | ||
198 | } | ||
199 | else { | ||
200 | DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy); | ||
201 | errno = ENOENT; | ||
202 | file->busy = false; | ||
203 | closedir_uncached(dir); | ||
204 | return -7; | ||
205 | } | ||
206 | } else { | ||
207 | if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) { | ||
208 | errno = EISDIR; | ||
209 | file->busy = false; | ||
210 | closedir_uncached(dir); | ||
211 | return -8; | ||
212 | } | 240 | } |
213 | } | ||
214 | closedir_uncached(dir); | ||
215 | 241 | ||
216 | file->cacheoffset = -1; | 242 | rc2 = fat_seek(&file->stream.fatstr, sector); |
217 | file->fileoffset = 0; | 243 | if (rc2 < 0) |
244 | FILE_ERROR(EIO, rc2 * 10 - 2); | ||
218 | 245 | ||
219 | if (file->write && (flags & O_APPEND)) { | 246 | rc2 = fat_truncate(&file->stream.fatstr); |
220 | rc = lseek(fd,0,SEEK_END); | 247 | if (rc2 < 0) |
221 | if (rc < 0 ) | 248 | FILE_ERROR(EIO, rc2 * 10 - 3); |
222 | return rc * 10 - 9; | ||
223 | } | 249 | } |
250 | /* else just change the cached file size */ | ||
224 | 251 | ||
225 | #ifdef HAVE_DIRCACHE | 252 | if (truncsize < cursize) |
226 | if (file->write) | 253 | { |
227 | dircache_bind(fd, pathname); | 254 | *file->sizep = truncsize; |
228 | #endif | 255 | fileop_ontruncate_internal(&file->stream); |
256 | } | ||
229 | 257 | ||
230 | return fd; | 258 | /* if truncation was partially successful, it effectively destroyed |
231 | } | 259 | everything after the truncation point; still, indicate failure |
260 | after adjusting size */ | ||
261 | if (rc2 == 0) | ||
262 | FILE_ERROR(EIO, -4); | ||
263 | else if (rc2 < 0) | ||
264 | FILE_ERROR(ERRNO, rc2); | ||
232 | 265 | ||
233 | int file_open(const char* pathname, int flags) | 266 | file_error: |
234 | { | 267 | return rc; |
235 | /* By default, use the dircache if available. */ | ||
236 | return open_internal(pathname, flags, true); | ||
237 | } | 268 | } |
238 | 269 | ||
239 | int close(int fd) | 270 | /* flush back all outstanding writes to the file */ |
271 | static int fsync_internal(struct filestr_desc *file) | ||
240 | { | 272 | { |
241 | struct filedesc* file = &openfiles[fd]; | 273 | /* call only when holding WRITER lock (updates directory entries) */ |
242 | int rc = 0; | 274 | int rc = 0; |
243 | 275 | ||
244 | LDEBUGF("close(%d)\n", fd); | 276 | file_size_t size = *file->sizep; |
277 | unsigned int foflags = fileobj_get_flags(&file->stream); | ||
278 | |||
279 | /* flush sector cache? */ | ||
280 | struct filestr_cache *const cachep = file->stream.cachep; | ||
281 | if (cachep->flags & FSC_DIRTY) | ||
282 | { | ||
283 | int rc2 = flush_cache(file); | ||
284 | if (rc2 == FAT_RC_ENOSPC && (cachep->flags & FSC_NEW)) | ||
285 | { | ||
286 | /* no space left on device so this must be dropped */ | ||
287 | discard_cache(file); | ||
288 | size = ALIGN_DOWN(size - 1, SECTOR_SIZE); | ||
289 | foflags |= FO_TRUNC; | ||
290 | rc = rc2; | ||
291 | } | ||
292 | else if (rc2 < 0) | ||
293 | FILE_ERROR(ERRNO, rc2 * 10 - 1); | ||
294 | } | ||
295 | |||
296 | /* truncate? */ | ||
297 | if (foflags & FO_TRUNC) | ||
298 | { | ||
299 | int rc2 = ftruncate_internal(file, size, rc == 0); | ||
300 | if (rc2 < 0) | ||
301 | FILE_ERROR(ERRNO, rc2 * 10 - 2); | ||
245 | 302 | ||
246 | if (fd < 0 || fd > MAX_OPEN_FILES-1) { | 303 | /* never needs to be done this way again since any data beyond the |
247 | errno = EINVAL; | 304 | cached size is now gone */ |
248 | return -1; | 305 | fileobj_change_flags(&file->stream, 0, FO_TRUNC); |
249 | } | 306 | } |
250 | if (!file->busy) { | 307 | |
251 | errno = EBADF; | 308 | file_error:; |
252 | return -2; | 309 | /* tie up all loose ends (try to close the file even if failing) */ |
310 | int rc2 = fat_closewrite(&file->stream.fatstr, size, | ||
311 | get_dir_fatent_dircache()); | ||
312 | if (rc2 >= 0) | ||
313 | fileop_onsync_internal(&file->stream); /* dir_fatent is implicit arg */ | ||
314 | |||
315 | if (rc2 < 0 && rc >= 0) | ||
316 | { | ||
317 | errno = EIO; | ||
318 | rc = rc2 * 10 - 3; | ||
253 | } | 319 | } |
254 | if (file->write) { | 320 | |
255 | rc = fsync(fd); | 321 | return rc; |
322 | } | ||
323 | |||
324 | /* finish with the file and free resources */ | ||
325 | static int close_internal(struct filestr_desc *file) | ||
326 | { | ||
327 | /* call only when holding WRITER lock (updates directory entries) */ | ||
328 | int rc; | ||
329 | |||
330 | if ((file->stream.flags & FD_WRITE) && | ||
331 | !(fileobj_get_flags(&file->stream) & FO_REMOVED)) | ||
332 | { | ||
333 | rc = fsync_internal(file); | ||
256 | if (rc < 0) | 334 | if (rc < 0) |
257 | return rc * 10 - 3; | 335 | FILE_ERROR(ERRNO, rc * 10 - 1); |
258 | #ifdef HAVE_DIRCACHE | ||
259 | dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); | ||
260 | dircache_update_filetime(fd); | ||
261 | #endif | ||
262 | } | 336 | } |
263 | 337 | ||
264 | file->busy = false; | 338 | rc = 0; |
265 | return 0; | 339 | file_error:; |
340 | int rc2 = close_stream_internal(&file->stream); | ||
341 | if (rc2 < 0 && rc >= 0) | ||
342 | rc = rc2 * 10 - 2; | ||
343 | return rc; | ||
266 | } | 344 | } |
267 | 345 | ||
268 | int fsync(int fd) | 346 | /* actually do the open gruntwork */ |
347 | static int open_internal_inner2(const char *path, | ||
348 | struct filestr_desc *file, | ||
349 | unsigned int callflags) | ||
269 | { | 350 | { |
270 | struct filedesc* file = &openfiles[fd]; | 351 | int rc; |
271 | int rc = 0; | ||
272 | |||
273 | LDEBUGF("fsync(%d)\n", fd); | ||
274 | 352 | ||
275 | if (fd < 0 || fd > MAX_OPEN_FILES-1) { | 353 | struct path_component_info compinfo; |
276 | errno = EINVAL; | 354 | rc = open_stream_internal(path, callflags, &file->stream, &compinfo); |
277 | return -1; | 355 | if (rc < 0) |
278 | } | 356 | { |
279 | if (!file->busy) { | 357 | DEBUGF("Open failed: %d\n", rc); |
280 | errno = EBADF; | 358 | FILE_ERROR_RETURN(ERRNO, rc * 10 - 1); |
281 | return -2; | ||
282 | } | 359 | } |
283 | if (file->write) { | 360 | |
284 | /* flush sector cache */ | 361 | bool created = false; |
285 | if ( file->dirty ) { | 362 | |
286 | rc = flush_cache(fd); | 363 | if (rc > 0) |
287 | if (rc < 0) | 364 | { |
288 | { | 365 | if (callflags & FF_EXCL) |
289 | /* when failing, try to close the file anyway */ | 366 | { |
290 | fat_closewrite(&(file->fatfile), file->size, file->attr); | 367 | DEBUGF("File exists\n"); |
291 | return rc * 10 - 3; | 368 | FILE_ERROR(EEXIST, -2); |
292 | } | ||
293 | } | 369 | } |
294 | 370 | ||
295 | /* truncate? */ | 371 | if (compinfo.attr & ATTR_DIRECTORY) |
296 | if (file->trunc) { | 372 | { |
297 | rc = ftruncate(fd, file->size); | 373 | if ((callflags & FD_WRITE) || !(callflags & FF_ANYTYPE)) |
298 | if (rc < 0) | ||
299 | { | 374 | { |
300 | /* when failing, try to close the file anyway */ | 375 | DEBUGF("File is a directory\n"); |
301 | fat_closewrite(&(file->fatfile), file->size, file->attr); | 376 | FILE_ERROR(EISDIR, -3); |
302 | return rc * 10 - 4; | ||
303 | } | 377 | } |
378 | |||
379 | compinfo.filesize = MAX_DIRECTORY_SIZE; /* allow file ops */ | ||
380 | } | ||
381 | } | ||
382 | else if (callflags & FF_CREAT) | ||
383 | { | ||
384 | if (compinfo.attr & ATTR_DIRECTORY) | ||
385 | { | ||
386 | DEBUGF("File is a directory\n"); | ||
387 | FILE_ERROR(EISDIR, -5); | ||
304 | } | 388 | } |
305 | 389 | ||
306 | /* tie up all loose ends */ | 390 | /* not found; try to create it */ |
307 | rc = fat_closewrite(&(file->fatfile), file->size, file->attr); | 391 | |
392 | callflags &= ~FO_TRUNC; | ||
393 | rc = create_stream_internal(&compinfo.parentinfo, compinfo.name, | ||
394 | compinfo.length, ATTR_NEW_FILE, callflags, | ||
395 | &file->stream); | ||
308 | if (rc < 0) | 396 | if (rc < 0) |
309 | return rc * 10 - 5; | 397 | FILE_ERROR(ERRNO, rc * 10 - 6); |
310 | } | ||
311 | return 0; | ||
312 | } | ||
313 | 398 | ||
314 | int remove(const char* name) | 399 | created = true; |
315 | { | ||
316 | int rc; | ||
317 | struct filedesc* file; | ||
318 | /* Can't use dircache now, because we need to access the fat structures. */ | ||
319 | int fd = open_internal(name, O_WRONLY, false); | ||
320 | if ( fd < 0 ) | ||
321 | return fd * 10 - 1; | ||
322 | |||
323 | file = &openfiles[fd]; | ||
324 | #ifdef HAVE_DIRCACHE | ||
325 | dircache_remove(name); | ||
326 | #endif | ||
327 | rc = fat_remove(&(file->fatfile)); | ||
328 | if ( rc < 0 ) { | ||
329 | DEBUGF("Failed removing file: %d\n", rc); | ||
330 | errno = EIO; | ||
331 | return rc * 10 - 3; | ||
332 | } | 400 | } |
401 | else | ||
402 | { | ||
403 | DEBUGF("File not found\n"); | ||
404 | FILE_ERROR(ENOENT, -7); | ||
405 | } | ||
406 | |||
407 | fat_rewind(&file->stream.fatstr); | ||
408 | file->sizep = fileobj_get_sizep(&file->stream); | ||
409 | file->offset = 0; | ||
410 | |||
411 | if (!created) | ||
412 | { | ||
413 | /* size from storage applies to first stream only otherwise it's | ||
414 | already up to date */ | ||
415 | const bool first = fileobj_get_flags(&file->stream) & FO_SINGLE; | ||
416 | if (first) | ||
417 | *file->sizep = compinfo.filesize; | ||
333 | 418 | ||
334 | file->size = 0; | 419 | if (callflags & FO_TRUNC) |
420 | { | ||
421 | /* if the file is kind of "big" then free some space now */ | ||
422 | rc = ftruncate_internal(file, 0, *file->sizep >= O_TRUNC_THRESH); | ||
423 | if (rc < 0) | ||
424 | { | ||
425 | DEBUGF("O_TRUNC failed: %d\n", rc); | ||
426 | FILE_ERROR(ERRNO, rc * 10 - 4); | ||
427 | } | ||
428 | } | ||
429 | } | ||
335 | 430 | ||
336 | rc = close(fd); | 431 | rc = 0; |
337 | if (rc<0) | 432 | file_error: |
338 | return rc * 10 - 4; | 433 | if (rc < 0) |
434 | close_stream_internal(&file->stream); | ||
339 | 435 | ||
340 | return 0; | 436 | return rc; |
341 | } | 437 | } |
342 | 438 | ||
343 | int rename(const char* path, const char* newpath) | 439 | /* allocate a file descriptor, if needed, assemble stream flags and open |
440 | a new stream */ | ||
441 | static int open_internal_inner1(const char *path, int oflag, | ||
442 | unsigned int callflags) | ||
344 | { | 443 | { |
345 | int rc, fd; | 444 | DEBUGF("%s(path=\"%s\",oflag=%X,callflags=%X)\n", __func__, |
346 | DIR_UNCACHED* dir; | 445 | path, oflag, callflags); |
347 | char* nameptr; | ||
348 | char* dirptr; | ||
349 | struct filedesc* file; | ||
350 | char newpath2[MAX_PATH]; | ||
351 | 446 | ||
352 | /* verify new path does not already exist */ | 447 | int rc; |
353 | /* If it is a directory, errno == EISDIR if the name exists */ | ||
354 | fd = open(newpath, O_RDONLY); | ||
355 | if ( fd >= 0 || errno == EISDIR) { | ||
356 | close(fd); | ||
357 | errno = EBUSY; | ||
358 | return -1; | ||
359 | } | ||
360 | close(fd); | ||
361 | 448 | ||
362 | fd = open_internal(path, O_RDONLY, false); | 449 | struct filestr_desc *file; |
363 | if ( fd < 0 ) { | 450 | int fildes = alloc_filestr(&file); |
364 | errno = EIO; | 451 | if (fildes < 0) |
365 | return fd * 10 - 2; | 452 | FILE_ERROR(EMFILE, -1); |
366 | } | ||
367 | 453 | ||
368 | /* extract new file name */ | 454 | callflags &= ~FDO_MASK; |
369 | nameptr = strrchr(newpath,'/'); | ||
370 | if (nameptr) | ||
371 | nameptr++; | ||
372 | else { | ||
373 | close(fd); | ||
374 | return - 3; | ||
375 | } | ||
376 | 455 | ||
377 | /* Extract new path */ | 456 | if (oflag & O_ACCMODE) |
378 | strcpy(newpath2, newpath); | 457 | { |
458 | callflags |= FD_WRITE; | ||
379 | 459 | ||
380 | dirptr = strrchr(newpath2,'/'); | 460 | if ((oflag & O_ACCMODE) == O_WRONLY) |
381 | if(dirptr) | 461 | callflags |= FD_WRONLY; |
382 | *dirptr = 0; | ||
383 | else { | ||
384 | close(fd); | ||
385 | return - 4; | ||
386 | } | ||
387 | 462 | ||
388 | dirptr = newpath2; | 463 | if (oflag & O_APPEND) |
464 | callflags |= FD_APPEND; | ||
389 | 465 | ||
390 | if(strlen(dirptr) == 0) { | 466 | if (oflag & O_TRUNC) |
391 | dirptr = "/"; | 467 | callflags |= FO_TRUNC; |
392 | } | 468 | } |
393 | 469 | else if (oflag & O_TRUNC) | |
394 | dir = opendir_uncached(dirptr); | 470 | { |
395 | if(!dir) { | 471 | /* O_TRUNC requires write mode */ |
396 | close(fd); | 472 | DEBUGF("No write mode but have O_TRUNC\n"); |
397 | return - 5; | 473 | FILE_ERROR(EINVAL, -2); |
398 | } | 474 | } |
399 | 475 | ||
400 | file = &openfiles[fd]; | 476 | /* O_CREAT and O_APPEND are fine without write mode |
477 | * for the former, an empty file is created but no data may be written | ||
478 | * for the latter, no append will be allowed anyway */ | ||
479 | if (oflag & O_CREAT) | ||
480 | { | ||
481 | callflags |= FF_CREAT; | ||
401 | 482 | ||
402 | rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr, | 483 | if (oflag & O_EXCL) |
403 | file->size, file->attr); | 484 | callflags |= FF_EXCL; |
404 | #ifdef HAVE_MULTIVOLUME | ||
405 | if ( rc == -1) { | ||
406 | close(fd); | ||
407 | closedir_uncached(dir); | ||
408 | DEBUGF("Failed renaming file across volumnes: %d\n", rc); | ||
409 | errno = EXDEV; | ||
410 | return -6; | ||
411 | } | ||
412 | #endif | ||
413 | if ( rc < 0 ) { | ||
414 | close(fd); | ||
415 | closedir_uncached(dir); | ||
416 | DEBUGF("Failed renaming file: %d\n", rc); | ||
417 | errno = EIO; | ||
418 | return rc * 10 - 7; | ||
419 | } | 485 | } |
420 | 486 | ||
421 | #ifdef HAVE_DIRCACHE | 487 | rc = open_internal_inner2(path, file, callflags); |
422 | dircache_rename(path, newpath); | 488 | if (rc < 0) |
423 | #endif | 489 | FILE_ERROR(ERRNO, rc * 10 - 3); |
424 | 490 | ||
425 | rc = close(fd); | 491 | return fildes; |
426 | if (rc<0) { | ||
427 | closedir_uncached(dir); | ||
428 | errno = EIO; | ||
429 | return rc * 10 - 8; | ||
430 | } | ||
431 | 492 | ||
432 | rc = closedir_uncached(dir); | 493 | file_error: |
433 | if (rc<0) { | 494 | return rc; |
434 | errno = EIO; | 495 | } |
435 | return rc * 10 - 9; | ||
436 | } | ||
437 | 496 | ||
438 | return 0; | 497 | static int open_internal_locked(const char *path, int oflag, |
498 | unsigned int callflags) | ||
499 | { | ||
500 | file_internal_lock_WRITER(); | ||
501 | int rc = open_internal_inner1(path, oflag, callflags); | ||
502 | file_internal_unlock_WRITER(); | ||
503 | return rc; | ||
439 | } | 504 | } |
440 | 505 | ||
441 | int ftruncate(int fd, off_t size) | 506 | /* fill a cache buffer with a new sector */ |
507 | static int readwrite_fill_cache(struct filestr_desc *file, unsigned long sector, | ||
508 | unsigned long filesectors, bool write) | ||
442 | { | 509 | { |
443 | int rc, sector; | 510 | /* sector != cachep->sector should have been checked by now */ |
444 | struct filedesc* file = &openfiles[fd]; | ||
445 | 511 | ||
446 | sector = size / SECTOR_SIZE; | 512 | int rc; |
447 | if (size % SECTOR_SIZE) | 513 | struct filestr_cache *cachep = filestr_get_cache(&file->stream); |
448 | sector++; | ||
449 | 514 | ||
450 | rc = fat_seek(&(file->fatfile), sector); | 515 | if (cachep->flags & FSC_DIRTY) |
451 | if (rc < 0) { | 516 | { |
452 | errno = EIO; | 517 | rc = flush_cache(file); |
453 | return rc * 10 - 1; | 518 | if (rc < 0) |
519 | FILE_ERROR(ERRNO, rc * 10 - 1); | ||
454 | } | 520 | } |
455 | 521 | ||
456 | rc = fat_truncate(&(file->fatfile)); | 522 | if (fat_query_sectornum(&file->stream.fatstr) != sector) |
457 | if (rc < 0) { | 523 | { |
458 | errno = EIO; | 524 | /* get on the correct sector */ |
459 | return rc * 10 - 2; | 525 | rc = fat_seek(&file->stream.fatstr, sector); |
526 | if (rc < 0) | ||
527 | FILE_ERROR(EIO, rc * 10 - 2); | ||
460 | } | 528 | } |
461 | 529 | ||
462 | file->size = size; | 530 | if (!write || sector < filesectors) |
463 | #ifdef HAVE_DIRCACHE | 531 | { |
464 | dircache_update_filesize(fd, size, file->fatfile.firstcluster); | 532 | /* only reading or this sector would have been flushed if the cache |
465 | #endif | 533 | was previously needed for a different sector */ |
534 | rc = fat_readwrite(&file->stream.fatstr, 1, cachep->buffer, false); | ||
535 | if (rc < 0) | ||
536 | FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 3); | ||
537 | } | ||
538 | else | ||
539 | { | ||
540 | /* create a fresh, shiny, new sector with that new sector smell */ | ||
541 | cachep->flags = FSC_NEW; | ||
542 | } | ||
466 | 543 | ||
467 | return 0; | 544 | cachep->sector = sector; |
545 | return 1; | ||
546 | file_error: | ||
547 | DEBUGF("Failed caching sector: %d\n", rc); | ||
548 | return rc; | ||
468 | } | 549 | } |
469 | 550 | ||
470 | static int flush_cache(int fd) | 551 | /* read or write to part or all of the cache buffer */ |
552 | static inline void readwrite_cache(struct filestr_cache *cachep, void *buf, | ||
553 | unsigned long secoffset, size_t nbyte, | ||
554 | bool write) | ||
471 | { | 555 | { |
472 | int rc; | 556 | void *dst, *cbufp = cachep->buffer + secoffset; |
473 | struct filedesc* file = &openfiles[fd]; | ||
474 | long sector = file->fileoffset / SECTOR_SIZE; | ||
475 | |||
476 | DEBUGF("Flushing dirty sector cache\n"); | ||
477 | 557 | ||
478 | /* make sure we are on correct sector */ | 558 | if (write) |
479 | rc = fat_seek(&(file->fatfile), sector); | 559 | { |
480 | if ( rc < 0 ) | 560 | dst = cbufp; |
481 | return rc * 10 - 3; | 561 | cachep->flags |= FSC_DIRTY; |
482 | 562 | } | |
483 | rc = fat_readwrite(&(file->fatfile), 1, file->cache, true ); | 563 | else |
564 | { | ||
565 | dst = buf; | ||
566 | buf = cbufp; | ||
567 | } | ||
484 | 568 | ||
485 | if ( rc < 0 ) { | 569 | memcpy(dst, buf, nbyte); |
486 | if(file->fatfile.eof) | 570 | } |
487 | errno = ENOSPC; | ||
488 | 571 | ||
489 | return rc * 10 - 2; | 572 | /* read or write a partial sector using the file's cache */ |
573 | static inline ssize_t readwrite_partial(struct filestr_desc *file, | ||
574 | struct filestr_cache *cachep, | ||
575 | unsigned long sector, | ||
576 | unsigned long secoffset, | ||
577 | void *buf, | ||
578 | size_t nbyte, | ||
579 | unsigned long filesectors, | ||
580 | unsigned int flags) | ||
581 | { | ||
582 | if (sector != cachep->sector) | ||
583 | { | ||
584 | /* wrong sector in buffer */ | ||
585 | int rc = readwrite_fill_cache(file, sector, filesectors, flags); | ||
586 | if (rc <= 0) | ||
587 | return rc; | ||
490 | } | 588 | } |
491 | 589 | ||
492 | file->dirty = false; | 590 | readwrite_cache(cachep, buf, secoffset, nbyte, flags); |
493 | 591 | return nbyte; | |
494 | return 0; | ||
495 | } | 592 | } |
496 | 593 | ||
497 | static int readwrite(int fd, void* buf, long count, bool write) | 594 | /* read from or write to the file; back end to read() and write() */ |
595 | static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte, | ||
596 | bool write) | ||
498 | { | 597 | { |
499 | long sectors; | 598 | DEBUGF("readwrite(%p,%lx,%ld,%s)\n", |
500 | long nread=0; | 599 | file, (long)buf, nbyte, write ? "write" : "read"); |
501 | struct filedesc* file; | ||
502 | int rc; | ||
503 | #ifdef STORAGE_NEEDS_ALIGN | ||
504 | long i; | ||
505 | int rc2; | ||
506 | #endif | ||
507 | 600 | ||
508 | if (fd < 0 || fd > MAX_OPEN_FILES-1) { | 601 | const file_size_t size = *file->sizep; |
509 | errno = EINVAL; | 602 | file_size_t filerem; |
510 | return -1; | ||
511 | } | ||
512 | 603 | ||
513 | file = &openfiles[fd]; | 604 | if (write) |
605 | { | ||
606 | /* if opened in append mode, move pointer to end */ | ||
607 | if (file->stream.flags & FD_APPEND) | ||
608 | file->offset = MIN(size, FILE_SIZE_MAX); | ||
514 | 609 | ||
515 | if ( !file->busy ) { | 610 | filerem = FILE_SIZE_MAX - file->offset; |
516 | errno = EBADF; | 611 | } |
517 | return -1; | 612 | else |
613 | { | ||
614 | /* limit to maximum possible offset (EOF or FILE_SIZE_MAX) */ | ||
615 | filerem = MIN(size, FILE_SIZE_MAX) - file->offset; | ||
518 | } | 616 | } |
519 | 617 | ||
520 | if(file->attr & FAT_ATTR_DIRECTORY) { | 618 | if (nbyte > filerem) |
521 | errno = EISDIR; | 619 | { |
522 | return -1; | 620 | nbyte = filerem; |
621 | if (nbyte > 0) | ||
622 | {} | ||
623 | else if (write) | ||
624 | FILE_ERROR_RETURN(EFBIG, -1); /* would get too large */ | ||
625 | else if (file->offset >= FILE_SIZE_MAX) | ||
626 | FILE_ERROR_RETURN(EOVERFLOW, -2); /* can't read here */ | ||
523 | } | 627 | } |
524 | 628 | ||
525 | LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n", | 629 | if (nbyte == 0) |
526 | fd,(long)buf,count,write?"write":"read"); | 630 | return 0; |
527 | 631 | ||
528 | /* attempt to read past EOF? */ | 632 | int rc = 0; |
529 | if (!write && count > file->size - file->fileoffset) | 633 | |
530 | count = file->size - file->fileoffset; | 634 | struct filestr_cache * const cachep = file->stream.cachep; |
635 | void * const bufstart = buf; | ||
636 | |||
637 | const unsigned long filesectors = filesize_sectors(size); | ||
638 | unsigned long sector = file->offset / SECTOR_SIZE; | ||
639 | unsigned long sectoroffs = file->offset % SECTOR_SIZE; | ||
531 | 640 | ||
532 | /* any head bytes? */ | 641 | /* any head bytes? */ |
533 | if ( file->cacheoffset != -1 ) { | 642 | if (sectoroffs) |
534 | int offs = file->cacheoffset; | 643 | { |
535 | int headbytes = MIN(count, SECTOR_SIZE - offs); | 644 | size_t headbytes = MIN(nbyte, SECTOR_SIZE - sectoroffs); |
645 | rc = readwrite_partial(file, cachep, sector, sectoroffs, buf, headbytes, | ||
646 | filesectors, write); | ||
647 | if (rc <= 0) | ||
648 | { | ||
649 | if (rc < 0) | ||
650 | FILE_ERROR(ERRNO, rc * 10 - 3); | ||
536 | 651 | ||
537 | if (write) { | 652 | nbyte = 0; /* eof, skip the rest */ |
538 | memcpy( file->cache + offs, buf, headbytes ); | ||
539 | file->dirty = true; | ||
540 | } | 653 | } |
541 | else { | 654 | else |
542 | memcpy( buf, file->cache + offs, headbytes ); | 655 | { |
656 | buf += rc; | ||
657 | nbyte -= rc; | ||
658 | sector++; /* if nbyte goes to 0, the rest is skipped anyway */ | ||
543 | } | 659 | } |
660 | } | ||
544 | 661 | ||
545 | if (offs + headbytes == SECTOR_SIZE) { | 662 | /* read/write whole sectors right into/from the supplied buffer */ |
546 | if (file->dirty) { | 663 | unsigned long sectorcount = nbyte / SECTOR_SIZE; |
547 | rc = flush_cache(fd); | ||
548 | if ( rc < 0 ) { | ||
549 | errno = EIO; | ||
550 | return rc * 10 - 2; | ||
551 | } | ||
552 | } | ||
553 | file->cacheoffset = -1; | ||
554 | } | ||
555 | else { | ||
556 | file->cacheoffset += headbytes; | ||
557 | } | ||
558 | 664 | ||
559 | nread = headbytes; | 665 | while (sectorcount) |
560 | count -= headbytes; | 666 | { |
561 | } | 667 | unsigned long runlen = sectorcount; |
562 | 668 | ||
563 | /* If the buffer has been modified, either it has been flushed already | 669 | /* if a cached sector is inside the transfer range, split the transfer |
564 | * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no | 670 | into two parts and use the cache for that sector to keep it coherent |
565 | * more data to follow in this call). Do NOT flush here. */ | 671 | without writeback */ |
672 | if (UNLIKELY(cachep->sector >= sector && | ||
673 | cachep->sector < sector + sectorcount)) | ||
674 | { | ||
675 | runlen = cachep->sector - sector; | ||
676 | } | ||
566 | 677 | ||
567 | /* read/write whole sectors right into/from the supplied buffer */ | 678 | if (runlen) |
568 | sectors = count / SECTOR_SIZE; | 679 | { |
569 | rc = 0; | 680 | if (fat_query_sectornum(&file->stream.fatstr) != sector) |
570 | if ( sectors ) { | ||
571 | #ifdef STORAGE_NEEDS_ALIGN | ||
572 | if (((uint32_t)buf + nread) & (CACHEALIGN_SIZE - 1)) | ||
573 | for (i = 0; i < sectors; i++) | ||
574 | { | 681 | { |
575 | if (write) memcpy(file->cache, buf+nread+i*SECTOR_SIZE, SECTOR_SIZE); | 682 | /* get on the correct sector */ |
576 | rc2 = fat_readwrite(&(file->fatfile), 1, file->cache, write ); | 683 | rc = 0; |
577 | if (rc2 < 0) | 684 | |
685 | /* If the dirty bit isn't set, we're somehow beyond the file | ||
686 | size and you can't explain _that_ */ | ||
687 | if (sector >= filesectors && cachep->flags == (FSC_NEW|FSC_DIRTY)) | ||
578 | { | 688 | { |
579 | rc = rc2; | 689 | rc = flush_cache(file); |
580 | break; | 690 | if (rc < 0) |
691 | FILE_ERROR(ERRNO, rc * 10 - 4); | ||
692 | |||
693 | if (cachep->sector + 1 == sector) | ||
694 | rc = 1; /* if now ok, don't seek */ | ||
695 | } | ||
696 | |||
697 | if (rc == 0) | ||
698 | { | ||
699 | rc = fat_seek(&file->stream.fatstr, sector); | ||
700 | if (rc < 0) | ||
701 | FILE_ERROR(EIO, rc * 10 - 5); | ||
581 | } | 702 | } |
582 | else rc += rc2; | ||
583 | if (!write) memcpy(buf+nread+i*SECTOR_SIZE, file->cache, SECTOR_SIZE); | ||
584 | } | ||
585 | else | ||
586 | #endif | ||
587 | rc = fat_readwrite(&(file->fatfile), sectors, (unsigned char*)buf+nread, write ); | ||
588 | if ( rc < 0 ) { | ||
589 | DEBUGF("Failed read/writing %ld sectors\n",sectors); | ||
590 | errno = EIO; | ||
591 | if(write && file->fatfile.eof) { | ||
592 | DEBUGF("No space left on device\n"); | ||
593 | errno = ENOSPC; | ||
594 | } else { | ||
595 | file->fileoffset += nread; | ||
596 | } | 703 | } |
597 | file->cacheoffset = -1; | 704 | |
598 | /* adjust file size to length written */ | 705 | rc = fat_readwrite(&file->stream.fatstr, runlen, buf, write); |
599 | if ( write && file->fileoffset > file->size ) | 706 | if (rc < 0) |
600 | { | 707 | { |
601 | file->size = file->fileoffset; | 708 | DEBUGF("I/O error %sing %ld sectors\n", sectors, |
602 | #ifdef HAVE_DIRCACHE | 709 | write ? "writ" : "read"); |
603 | dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); | 710 | FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, |
604 | #endif | 711 | rc * 10 - 6); |
605 | } | 712 | } |
606 | return nread ? nread : rc * 10 - 4; | 713 | else |
607 | } | 714 | { |
608 | else { | 715 | buf += rc * SECTOR_SIZE; |
609 | if ( rc > 0 ) { | 716 | nbyte -= rc * SECTOR_SIZE; |
610 | nread += rc * SECTOR_SIZE; | 717 | sector += rc; |
611 | count -= sectors * SECTOR_SIZE; | 718 | sectorcount -= rc; |
612 | 719 | ||
613 | /* if eof, skip tail bytes */ | 720 | /* if eof, skip tail bytes */ |
614 | if ( rc < sectors ) | 721 | if ((unsigned long)rc < runlen) |
615 | count = 0; | 722 | nbyte = 0; |
616 | } | 723 | |
617 | else { | 724 | if (!nbyte) |
618 | /* eof */ | 725 | break; |
619 | count=0; | ||
620 | } | 726 | } |
727 | } | ||
621 | 728 | ||
622 | file->cacheoffset = -1; | 729 | if (UNLIKELY(sectorcount && sector == cachep->sector)) |
730 | { | ||
731 | /* do this one sector with the cache */ | ||
732 | readwrite_cache(cachep, buf, 0, SECTOR_SIZE, write); | ||
733 | buf += SECTOR_SIZE; | ||
734 | nbyte -= SECTOR_SIZE; | ||
735 | sector++; | ||
736 | sectorcount--; | ||
623 | } | 737 | } |
624 | } | 738 | } |
625 | 739 | ||
626 | /* any tail bytes? */ | 740 | /* any tail bytes? */ |
627 | if ( count ) { | 741 | if (nbyte) |
628 | if (write) { | 742 | { |
629 | if ( file->fileoffset + nread < file->size ) { | 743 | /* tail bytes always start at sector offset 0 */ |
630 | /* sector is only partially filled. copy-back from disk */ | 744 | rc = readwrite_partial(file, cachep, sector, 0, buf, nbyte, |
631 | LDEBUGF("Copy-back tail cache\n"); | 745 | filesectors, write); |
632 | rc = fat_readwrite(&(file->fatfile), 1, file->cache, false ); | 746 | if (rc < 0) |
633 | if ( rc < 0 ) { | 747 | FILE_ERROR(ERRNO, rc * 10 - 7); |
634 | DEBUGF("Failed writing\n"); | ||
635 | errno = EIO; | ||
636 | file->fileoffset += nread; | ||
637 | file->cacheoffset = -1; | ||
638 | /* adjust file size to length written */ | ||
639 | if ( file->fileoffset > file->size ) | ||
640 | { | ||
641 | file->size = file->fileoffset; | ||
642 | #ifdef HAVE_DIRCACHE | ||
643 | dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); | ||
644 | #endif | ||
645 | } | ||
646 | return nread ? nread : rc * 10 - 5; | ||
647 | } | ||
648 | /* seek back one sector to put file position right */ | ||
649 | rc = fat_seek(&(file->fatfile), | ||
650 | (file->fileoffset + nread) / | ||
651 | SECTOR_SIZE); | ||
652 | if ( rc < 0 ) { | ||
653 | DEBUGF("fat_seek() failed\n"); | ||
654 | errno = EIO; | ||
655 | file->fileoffset += nread; | ||
656 | file->cacheoffset = -1; | ||
657 | /* adjust file size to length written */ | ||
658 | if ( file->fileoffset > file->size ) | ||
659 | { | ||
660 | file->size = file->fileoffset; | ||
661 | #ifdef HAVE_DIRCACHE | ||
662 | dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); | ||
663 | #endif | ||
664 | } | ||
665 | return nread ? nread : rc * 10 - 6; | ||
666 | } | ||
667 | } | ||
668 | memcpy( file->cache, (unsigned char*)buf + nread, count ); | ||
669 | file->dirty = true; | ||
670 | } | ||
671 | else { | ||
672 | rc = fat_readwrite(&(file->fatfile), 1, file->cache,false); | ||
673 | if (rc < 1 ) { | ||
674 | DEBUGF("Failed caching sector\n"); | ||
675 | errno = EIO; | ||
676 | file->fileoffset += nread; | ||
677 | file->cacheoffset = -1; | ||
678 | return nread ? nread : rc * 10 - 7; | ||
679 | } | ||
680 | memcpy( (unsigned char*)buf + nread, file->cache, count ); | ||
681 | } | ||
682 | 748 | ||
683 | nread += count; | 749 | buf += rc; |
684 | file->cacheoffset = count; | ||
685 | } | 750 | } |
686 | 751 | ||
687 | file->fileoffset += nread; | 752 | file_error:; |
688 | LDEBUGF("fileoffset: %ld\n", file->fileoffset); | 753 | #ifdef DEBUG |
754 | if (errno == ENOSPC) | ||
755 | DEBUGF("No space left on device\n"); | ||
756 | #endif | ||
689 | 757 | ||
690 | /* adjust file size to length written */ | 758 | size_t done = buf - bufstart; |
691 | if ( write && file->fileoffset > file->size ) | 759 | if (done) |
692 | { | 760 | { |
693 | file->size = file->fileoffset; | 761 | /* error or not, update the file offset and size if anything was |
694 | #ifdef HAVE_DIRCACHE | 762 | transferred */ |
695 | dircache_update_filesize(fd, file->size, file->fatfile.firstcluster); | 763 | file->offset += done; |
696 | #endif | 764 | DEBUGF("file offset: %ld\n", file->offset); |
765 | |||
766 | /* adjust file size to length written */ | ||
767 | if (write && file->offset > size) | ||
768 | *file->sizep = file->offset; | ||
769 | |||
770 | if (rc > 0) | ||
771 | return done; | ||
697 | } | 772 | } |
698 | 773 | ||
699 | return nread; | 774 | return rc; |
700 | } | 775 | } |
701 | 776 | ||
702 | ssize_t write(int fd, const void* buf, size_t count) | 777 | |
778 | /** Internal interface **/ | ||
779 | |||
780 | /* open a file without codepage conversion during the directory search; | ||
781 | required to avoid any reentrancy when opening codepages and when scanning | ||
782 | directories internally, which could infinitely recurse and would corrupt | ||
783 | the static data */ | ||
784 | int open_noiso_internal(const char *path, int oflag) | ||
703 | { | 785 | { |
704 | if (!openfiles[fd].write) { | 786 | return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO); |
705 | errno = EACCES; | ||
706 | return -1; | ||
707 | } | ||
708 | return readwrite(fd, (void *)buf, count, true); | ||
709 | } | 787 | } |
710 | 788 | ||
711 | ssize_t read(int fd, void* buf, size_t count) | 789 | |
790 | /** POSIX **/ | ||
791 | |||
792 | /* open a file */ | ||
793 | int open(const char *path, int oflag) | ||
712 | { | 794 | { |
713 | return readwrite(fd, buf, count, false); | 795 | DEBUGF("open(path=\"%s\",oflag=%X)\n", path, (unsigned)oflag); |
796 | return open_internal_locked(path, oflag, FF_ANYTYPE); | ||
714 | } | 797 | } |
715 | 798 | ||
799 | /* create a new file or rewrite an existing one */ | ||
800 | int creat(const char *path) | ||
801 | { | ||
802 | DEBUGF("creat(path=\"%s\")\n", path); | ||
803 | return open_internal_locked(path, O_WRONLY|O_CREAT|O_TRUNC, FF_ANYTYPE); | ||
804 | } | ||
716 | 805 | ||
717 | off_t lseek(int fd, off_t offset, int whence) | 806 | /* close a file descriptor */ |
807 | int close(int fildes) | ||
718 | { | 808 | { |
719 | off_t pos; | 809 | DEBUGF("close(fd=%d)\n", fildes); |
720 | long newsector; | 810 | |
721 | long oldsector; | ||
722 | int sectoroffset; | ||
723 | int rc; | 811 | int rc; |
724 | struct filedesc* file = &openfiles[fd]; | ||
725 | 812 | ||
726 | LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence); | 813 | file_internal_lock_WRITER(); |
727 | 814 | ||
728 | if (fd < 0 || fd > MAX_OPEN_FILES-1) { | 815 | /* needs to work even if marked "nonexistant" */ |
729 | errno = EINVAL; | 816 | struct filestr_desc *file = &open_streams[fildes]; |
730 | return -1; | 817 | if ((unsigned int)fildes >= MAX_OPEN_FILES || !file->stream.flags) |
731 | } | 818 | { |
732 | if ( !file->busy ) { | 819 | DEBUGF("filedes %d not open\n", fildes); |
733 | errno = EBADF; | 820 | FILE_ERROR(EBADF, -2); |
734 | return -1; | ||
735 | } | 821 | } |
736 | 822 | ||
737 | switch ( whence ) { | 823 | rc = close_internal(file); |
738 | case SEEK_SET: | 824 | if (rc < 0) |
739 | pos = offset; | 825 | FILE_ERROR(ERRNO, rc * 10 - 3); |
740 | break; | ||
741 | 826 | ||
742 | case SEEK_CUR: | 827 | file_error: |
743 | pos = file->fileoffset + offset; | 828 | file_internal_unlock_WRITER(); |
744 | break; | 829 | return rc; |
830 | } | ||
831 | |||
832 | /* truncate a file to a specified length */ | ||
833 | int ftruncate(int fildes, off_t length) | ||
834 | { | ||
835 | DEBUGF("ftruncate(fd=%d,len=%ld)\n", fildes, (long)length); | ||
745 | 836 | ||
746 | case SEEK_END: | 837 | struct filestr_desc * const file = GET_FILESTR(READER, fildes); |
747 | pos = file->size + offset; | 838 | if (!file) |
748 | break; | 839 | FILE_ERROR_RETURN(ERRNO, -1); |
749 | 840 | ||
750 | default: | 841 | int rc; |
751 | errno = EINVAL; | 842 | |
752 | return -2; | 843 | if (!(file->stream.flags & FD_WRITE)) |
844 | { | ||
845 | DEBUGF("Descriptor is read-only mode\n"); | ||
846 | FILE_ERROR(EBADF, -2); | ||
753 | } | 847 | } |
754 | if ((pos < 0) || (pos > file->size)) { | 848 | |
755 | errno = EINVAL; | 849 | if (length < 0) |
756 | return -3; | 850 | { |
851 | DEBUGF("Length %ld is invalid\n", (long)length); | ||
852 | FILE_ERROR(EINVAL, -3); | ||
757 | } | 853 | } |
758 | 854 | ||
759 | /* new sector? */ | 855 | rc = ftruncate_internal(file, length, true); |
760 | newsector = pos / SECTOR_SIZE; | 856 | if (rc < 0) |
761 | oldsector = file->fileoffset / SECTOR_SIZE; | 857 | FILE_ERROR(ERRNO, rc * 10 - 4); |
762 | sectoroffset = pos % SECTOR_SIZE; | ||
763 | 858 | ||
764 | if ( (newsector != oldsector) || | 859 | file_error: |
765 | ((file->cacheoffset==-1) && sectoroffset) ) { | 860 | RELEASE_FILESTR(READER, file); |
861 | return rc; | ||
862 | } | ||
766 | 863 | ||
767 | if ( newsector != oldsector ) { | 864 | /* synchronize changes to a file */ |
768 | if (file->dirty) { | 865 | int fsync(int fildes) |
769 | rc = flush_cache(fd); | 866 | { |
770 | if (rc < 0) | 867 | DEBUGF("fsync(fd=%d)\n", fildes); |
771 | return rc * 10 - 5; | ||
772 | } | ||
773 | 868 | ||
774 | rc = fat_seek(&(file->fatfile), newsector); | 869 | struct filestr_desc * const file = GET_FILESTR(WRITER, fildes); |
775 | if ( rc < 0 ) { | 870 | if (!file) |
776 | errno = EIO; | 871 | FILE_ERROR_RETURN(ERRNO, -1); |
777 | return rc * 10 - 4; | 872 | |
778 | } | 873 | int rc; |
779 | } | 874 | |
780 | if ( sectoroffset ) { | 875 | if (!(file->stream.flags & FD_WRITE)) |
781 | rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false); | 876 | { |
782 | if ( rc < 0 ) { | 877 | DEBUGF("Descriptor is read-only mode\n", fd); |
783 | errno = EIO; | 878 | FILE_ERROR(EINVAL, -2); |
784 | return rc * 10 - 6; | ||
785 | } | ||
786 | file->cacheoffset = sectoroffset; | ||
787 | } | ||
788 | else | ||
789 | file->cacheoffset = -1; | ||
790 | } | 879 | } |
791 | else | ||
792 | if ( file->cacheoffset != -1 ) | ||
793 | file->cacheoffset = sectoroffset; | ||
794 | 880 | ||
795 | file->fileoffset = pos; | 881 | rc = fsync_internal(file); |
882 | if (rc < 0) | ||
883 | FILE_ERROR(ERRNO, rc * 10 - 3); | ||
796 | 884 | ||
797 | return pos; | 885 | file_error: |
886 | RELEASE_FILESTR(WRITER, file); | ||
887 | return rc; | ||
798 | } | 888 | } |
799 | 889 | ||
800 | off_t filesize(int fd) | 890 | /* move the read/write file offset */ |
891 | off_t lseek(int fildes, off_t offset, int whence) | ||
801 | { | 892 | { |
802 | struct filedesc* file = &openfiles[fd]; | 893 | DEBUGF("lseek(fd=%d,ofs=%ld,wh=%d)\n", fildes, (long)offset, whence); |
803 | 894 | ||
804 | if (fd < 0 || fd > MAX_OPEN_FILES-1) { | 895 | struct filestr_desc * const file = GET_FILESTR(READER, fildes); |
805 | errno = EINVAL; | 896 | if (!file) |
806 | return -1; | 897 | FILE_ERROR_RETURN(ERRNO, -1); |
898 | |||
899 | off_t rc = lseek_internal(file, offset, whence); | ||
900 | if (rc < 0) | ||
901 | FILE_ERROR(ERRNO, rc * 10 - 2); | ||
902 | |||
903 | file_error: | ||
904 | RELEASE_FILESTR(READER, file); | ||
905 | return rc; | ||
906 | } | ||
907 | |||
908 | /* read from a file */ | ||
909 | ssize_t read(int fildes, void *buf, size_t nbyte) | ||
910 | { | ||
911 | struct filestr_desc * const file = GET_FILESTR(READER, fildes); | ||
912 | if (!file) | ||
913 | FILE_ERROR_RETURN(ERRNO, -1); | ||
914 | |||
915 | ssize_t rc; | ||
916 | |||
917 | if (file->stream.flags & FD_WRONLY) | ||
918 | { | ||
919 | DEBUGF("read(fd=%d,buf=%p,nb=%lu) - " | ||
920 | "descriptor is write-only mode\n", fildes, buf, nbyte); | ||
921 | FILE_ERROR(EBADF, -2); | ||
807 | } | 922 | } |
808 | if ( !file->busy ) { | 923 | |
809 | errno = EBADF; | 924 | rc = readwrite(file, buf, nbyte, false); |
810 | return -1; | 925 | if (rc < 0) |
926 | FILE_ERROR(ERRNO, rc * 10 - 3); | ||
927 | |||
928 | file_error: | ||
929 | RELEASE_FILESTR(READER, file); | ||
930 | return rc; | ||
931 | } | ||
932 | |||
933 | /* write on a file */ | ||
934 | ssize_t write(int fildes, const void *buf, size_t nbyte) | ||
935 | { | ||
936 | struct filestr_desc * const file = GET_FILESTR(READER, fildes); | ||
937 | if (!file) | ||
938 | FILE_ERROR_RETURN(ERRNO, -1); | ||
939 | |||
940 | ssize_t rc; | ||
941 | |||
942 | if (!(file->stream.flags & FD_WRITE)) | ||
943 | { | ||
944 | DEBUGF("write(fd=%d,buf=%p,nb=%lu) - " | ||
945 | "descriptor is read-only mode\n", fildes, buf, nbyte); | ||
946 | FILE_ERROR(EBADF, -2); | ||
811 | } | 947 | } |
812 | 948 | ||
813 | return file->size; | 949 | rc = readwrite(file, (void *)buf, nbyte, true); |
950 | if (rc < 0) | ||
951 | FILE_ERROR(ERRNO, rc * 10 - 3); | ||
952 | |||
953 | file_error: | ||
954 | RELEASE_FILESTR(READER, file); | ||
955 | return rc; | ||
814 | } | 956 | } |
815 | 957 | ||
958 | /* remove a file */ | ||
959 | int remove(const char *path) | ||
960 | { | ||
961 | DEBUGF("remove(path=\"%s\")\n", path); | ||
962 | |||
963 | file_internal_lock_WRITER(); | ||
964 | int rc = remove_stream_internal(path, NULL, FF_FILE); | ||
965 | file_internal_unlock_WRITER(); | ||
966 | return rc; | ||
967 | } | ||
816 | 968 | ||
817 | /* release all file handles on a given volume "by force", to avoid leaks */ | 969 | /* rename a file */ |
818 | int release_files(int volume) | 970 | int rename(const char *old, const char *new) |
819 | { | 971 | { |
820 | struct filedesc* pfile = openfiles; | 972 | DEBUGF("rename(old=\"%s\",new=\"%s\")\n", old, new); |
821 | int fd; | 973 | |
822 | int closed = 0; | 974 | int rc, open1rc = -1, open2rc = -1; |
823 | for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++) | 975 | struct filestr_base oldstr, newstr; |
976 | struct path_component_info oldinfo, newinfo; | ||
977 | |||
978 | file_internal_lock_WRITER(); | ||
979 | |||
980 | /* open 'old'; it must exist */ | ||
981 | open1rc = open_stream_internal(old, FF_ANYTYPE, &oldstr, &oldinfo); | ||
982 | if (open1rc <= 0) | ||
824 | { | 983 | { |
984 | DEBUGF("Failed opening old: %d\n", rc); | ||
985 | if (open1rc == 0) | ||
986 | FILE_ERROR(ENOENT, -1); | ||
987 | else | ||
988 | FILE_ERROR(ERRNO, open1rc * 10 - 1); | ||
989 | } | ||
990 | |||
991 | /* if 'old' is a directory then 'new' is also required to be one if 'new' | ||
992 | is to be overwritten */ | ||
993 | const bool are_dirs = oldinfo.attr & ATTR_DIRECTORY; | ||
994 | |||
995 | /* open new (may or may not exist) */ | ||
996 | unsigned int callflags = FF_FILE; | ||
997 | if (are_dirs) | ||
998 | { | ||
999 | /* if 'old' is found while parsing the new directory components then | ||
1000 | 'new' contains path prefix that names 'old'; if new and old are in | ||
1001 | the same directory, this tests positive but that is checked later */ | ||
1002 | callflags = FF_DIR | FF_CHECKPREFIX; | ||
1003 | newinfo.prefixp = oldstr.infop; | ||
1004 | } | ||
1005 | |||
1006 | open2rc = open_stream_internal(new, callflags, &newstr, &newinfo); | ||
1007 | if (open2rc < 0) | ||
1008 | { | ||
1009 | DEBUGF("Failed opening new file: %d\n", rc); | ||
1010 | FILE_ERROR(ERRNO, open2rc * 10 - 2); | ||
1011 | } | ||
1012 | |||
825 | #ifdef HAVE_MULTIVOLUME | 1013 | #ifdef HAVE_MULTIVOLUME |
826 | if (pfile->fatfile.volume == volume) | 1014 | if (oldinfo.parentinfo.volume != newinfo.parentinfo.volume) |
827 | #else | 1015 | { |
828 | (void)volume; | 1016 | DEBUGF("Cross-device link\n"); |
829 | #endif | 1017 | FILE_ERROR(EXDEV, -3); |
1018 | } | ||
1019 | #endif /* HAVE_MULTIVOLUME */ | ||
1020 | |||
1021 | /* if the parent is changing then this is a move, not a simple rename */ | ||
1022 | const bool is_move = !fat_file_is_same(&oldinfo.parentinfo.fatfile, | ||
1023 | &newinfo.parentinfo.fatfile); | ||
1024 | /* prefix found and moving? */ | ||
1025 | if (is_move && (newinfo.attr & ATTR_PREFIX)) | ||
1026 | { | ||
1027 | DEBUGF("New contains prefix that names old\n"); | ||
1028 | FILE_ERROR(EINVAL, -4); | ||
1029 | } | ||
1030 | |||
1031 | const char * const oldname = strmemdupa(oldinfo.name, oldinfo.length); | ||
1032 | const char * const newname = strmemdupa(newinfo.name, newinfo.length); | ||
1033 | bool is_overwrite = false; | ||
1034 | |||
1035 | if (open2rc > 0) | ||
1036 | { | ||
1037 | /* new name exists in parent; check if 'old' is overwriting 'new'; | ||
1038 | if it's the very same file, then it's just a rename */ | ||
1039 | is_overwrite = oldstr.bindp != newstr.bindp; | ||
1040 | |||
1041 | if (is_overwrite) | ||
1042 | { | ||
1043 | if (are_dirs) | ||
1044 | { | ||
1045 | /* the directory to be overwritten must be empty */ | ||
1046 | rc = test_dir_empty_internal(&newstr); | ||
1047 | if (rc < 0) | ||
1048 | FILE_ERROR(ERRNO, rc * 10 - 5); | ||
1049 | } | ||
1050 | } | ||
1051 | else if (!strcmp(newname, oldname)) /* case-only is ok */ | ||
1052 | { | ||
1053 | DEBUGF("No name change (success)\n"); | ||
1054 | rc = 0; | ||
1055 | FILE_ERROR(ERRNO, RC); | ||
1056 | } | ||
1057 | } | ||
1058 | else if (!are_dirs && (newinfo.attr & ATTR_DIRECTORY)) | ||
1059 | { | ||
1060 | /* even if new doesn't exist, canonical path type must match | ||
1061 | (ie. a directory path such as "/foo/bar/" when old names a file) */ | ||
1062 | DEBUGF("New path is a directory\n"); | ||
1063 | FILE_ERROR(EISDIR, -6); | ||
1064 | } | ||
1065 | |||
1066 | /* first, create the new entry so that there's never a time that the | ||
1067 | victim's data has no reference in the directory tree, that is, until | ||
1068 | everything else first succeeds */ | ||
1069 | struct file_base_info old_fileinfo = *oldstr.infop; | ||
1070 | rc = fat_rename(&newinfo.parentinfo.fatfile, &oldstr.infop->fatfile, | ||
1071 | newname); | ||
1072 | if (rc < 0) | ||
1073 | { | ||
1074 | DEBUGF("I/O error renaming file: %d\n", rc); | ||
1075 | FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 7); | ||
1076 | } | ||
1077 | |||
1078 | if (is_overwrite) | ||
1079 | { | ||
1080 | /* 'new' would have been assigned its own directory entry and | ||
1081 | succeeded so at this point it is treated like a remove() call | ||
1082 | on the victim which preserves data until the last reference is | ||
1083 | closed */ | ||
1084 | rc = remove_stream_internal(NULL, &newstr, callflags); | ||
1085 | if (rc < 0) | ||
1086 | FILE_ERROR(ERRNO, rc * 10 - 8); | ||
1087 | } | ||
1088 | |||
1089 | fileop_onrename_internal(&oldstr, is_move ? &old_fileinfo : NULL, | ||
1090 | &newinfo.parentinfo, newname); | ||
1091 | |||
1092 | file_error: | ||
1093 | /* for now, there is nothing to fail upon closing the old stream */ | ||
1094 | if (open1rc >= 0) | ||
1095 | close_stream_internal(&oldstr); | ||
1096 | |||
1097 | /* the 'new' stream could fail to close cleanly because it became | ||
1098 | impossible to remove its data if this was an overwrite operation */ | ||
1099 | if (open2rc >= 0) | ||
1100 | { | ||
1101 | int rc2 = close_stream_internal(&newstr); | ||
1102 | if (rc2 < 0 && rc >= 0) | ||
830 | { | 1103 | { |
831 | pfile->busy = false; /* mark as available, no further action */ | 1104 | DEBUGF("Success but failed closing new: %d\n", rc2); |
832 | closed++; | 1105 | rc = rc2 * 10 - 9; |
833 | } | 1106 | } |
834 | } | 1107 | } |
835 | return closed; /* return how many we did */ | 1108 | |
1109 | file_internal_unlock_WRITER(); | ||
1110 | return rc; | ||
1111 | } | ||
1112 | |||
1113 | |||
1114 | /** Extensions **/ | ||
1115 | |||
1116 | /* get the binary size of a file (in bytes) */ | ||
1117 | off_t filesize(int fildes) | ||
1118 | { | ||
1119 | struct filestr_desc * const file = GET_FILESTR(READER, fildes); | ||
1120 | if (!file) | ||
1121 | FILE_ERROR_RETURN(ERRNO, -1); | ||
1122 | |||
1123 | off_t rc; | ||
1124 | file_size_t size = *file->sizep; | ||
1125 | |||
1126 | if (size > FILE_SIZE_MAX) | ||
1127 | FILE_ERROR(EOVERFLOW, -2); | ||
1128 | |||
1129 | rc = (off_t)size; | ||
1130 | file_error: | ||
1131 | RELEASE_FILESTR(READER, file); | ||
1132 | return rc; | ||
1133 | } | ||
1134 | |||
1135 | /* test if two file descriptors refer to the same file */ | ||
1136 | int fsamefile(int fildes1, int fildes2) | ||
1137 | { | ||
1138 | struct filestr_desc * const file1 = GET_FILESTR(WRITER, fildes1); | ||
1139 | if (!file1) | ||
1140 | FILE_ERROR_RETURN(ERRNO, -1); | ||
1141 | |||
1142 | int rc = -2; | ||
1143 | |||
1144 | struct filestr_desc * const file2 = get_filestr(fildes2); | ||
1145 | if (file2) | ||
1146 | rc = file1->stream.bindp == file2->stream.bindp ? 1 : 0; | ||
1147 | |||
1148 | RELEASE_FILESTR(WRITER, file1); | ||
1149 | return rc; | ||
1150 | } | ||
1151 | |||
1152 | /* tell the relationship of path1 to path2 */ | ||
1153 | int relate(const char *path1, const char *path2) | ||
1154 | { | ||
1155 | /* this is basically what rename() does but reduced to the relationship | ||
1156 | determination */ | ||
1157 | DEBUGF("relate(path1=\"%s\",path2=\"%s\")\n", path1, path2); | ||
1158 | |||
1159 | int rc, open1rc = -1, open2rc = -1; | ||
1160 | struct filestr_base str1, str2; | ||
1161 | struct path_component_info info1, info2; | ||
1162 | |||
1163 | file_internal_lock_WRITER(); | ||
1164 | |||
1165 | open1rc = open_stream_internal(path1, FF_ANYTYPE, &str1, &info1); | ||
1166 | if (open1rc <= 0) | ||
1167 | { | ||
1168 | DEBUGF("Failed opening path1: %d\n", rc); | ||
1169 | if (open1rc < 0) | ||
1170 | FILE_ERROR(ERRNO, open1rc * 10 - 1); | ||
1171 | else | ||
1172 | FILE_ERROR(ENOENT, -1); | ||
1173 | } | ||
1174 | |||
1175 | info2.prefixp = str1.infop; | ||
1176 | open2rc = open_stream_internal(path2, FF_ANYTYPE | FF_CHECKPREFIX, | ||
1177 | &str2, &info2); | ||
1178 | if (open2rc < 0) | ||
1179 | { | ||
1180 | DEBUGF("Failed opening path2: %d\n", rc); | ||
1181 | FILE_ERROR(ERRNO, open2rc * 10 - 2); | ||
1182 | } | ||
1183 | |||
1184 | rc = RELATE_DIFFERENT; | ||
1185 | |||
1186 | if (open2rc > 0) | ||
1187 | { | ||
1188 | if (str1.bindp == str2.bindp) | ||
1189 | rc = RELATE_SAME; | ||
1190 | else if (info2.attr & ATTR_PREFIX) | ||
1191 | rc = RELATE_PREFIX; | ||
1192 | } | ||
1193 | else /* open2rc == 0 */ | ||
1194 | { | ||
1195 | /* path1 existing and path2's final part not can only be a prefix or | ||
1196 | different */ | ||
1197 | if (info2.attr & ATTR_PREFIX) | ||
1198 | rc = RELATE_PREFIX; | ||
1199 | } | ||
1200 | |||
1201 | file_error: | ||
1202 | if (open1rc >= 0) | ||
1203 | close_stream_internal(&str1); | ||
1204 | |||
1205 | if (open2rc >= 0) | ||
1206 | close_stream_internal(&str2); | ||
1207 | |||
1208 | file_internal_unlock_WRITER(); | ||
1209 | return rc; | ||
1210 | } | ||
1211 | |||
1212 | /* test file or directory existence */ | ||
1213 | bool file_exists(const char *path) | ||
1214 | { | ||
1215 | file_internal_lock_WRITER(); | ||
1216 | bool rc = test_stream_exists_internal(path, FF_ANYTYPE) > 0; | ||
1217 | file_internal_unlock_WRITER(); | ||
1218 | return rc; | ||
836 | } | 1219 | } |
diff --git a/firmware/common/file_internal.c b/firmware/common/file_internal.c new file mode 100644 index 0000000000..ebe77f0c9f --- /dev/null +++ b/firmware/common/file_internal.c | |||
@@ -0,0 +1,776 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include <errno.h> | ||
23 | #include "system.h" | ||
24 | #include "debug.h" | ||
25 | #include "panic.h" | ||
26 | #include "pathfuncs.h" | ||
27 | #include "disk_cache.h" | ||
28 | #include "fileobj_mgr.h" | ||
29 | #include "dir.h" | ||
30 | #include "dircache_redirect.h" | ||
31 | #include "dircache.h" | ||
32 | #include "string-extra.h" | ||
33 | #include "rbunicode.h" | ||
34 | |||
35 | /** Internal common filesystem service functions **/ | ||
36 | |||
37 | /* for internal functions' scanning use to save quite a bit of stack space - | ||
38 | access must be serialized by the writer lock */ | ||
39 | #if defined(CPU_SH) || defined(IAUDIO_M5) | ||
40 | /* otherwise, out of IRAM */ | ||
41 | struct fat_direntry dir_fatent; | ||
42 | #else | ||
43 | struct fat_direntry dir_fatent IBSS_ATTR; | ||
44 | #endif | ||
45 | |||
46 | struct mrsw_lock file_internal_mrsw SHAREDBSS_ATTR; | ||
47 | |||
48 | |||
49 | /** File stream sector caching **/ | ||
50 | |||
51 | /* initialize a new cache structure */ | ||
52 | void file_cache_init(struct filestr_cache *cachep) | ||
53 | { | ||
54 | cachep->buffer = NULL; | ||
55 | cachep->sector = INVALID_SECNUM; | ||
56 | cachep->flags = 0; | ||
57 | } | ||
58 | |||
59 | /* discard and mark the cache buffer as unused */ | ||
60 | void file_cache_reset(struct filestr_cache *cachep) | ||
61 | { | ||
62 | cachep->sector = INVALID_SECNUM; | ||
63 | cachep->flags = 0; | ||
64 | } | ||
65 | |||
66 | /* allocate resources attached to the cache */ | ||
67 | void file_cache_alloc(struct filestr_cache *cachep) | ||
68 | { | ||
69 | /* if this fails, it is a bug; check for leaks and that the cache has | ||
70 | enough buffers for the worst case */ | ||
71 | if (!cachep->buffer && !(cachep->buffer = dc_get_buffer())) | ||
72 | panicf("file_cache_alloc - OOM"); | ||
73 | } | ||
74 | |||
75 | /* free resources attached to the cache */ | ||
76 | void file_cache_free(struct filestr_cache *cachep) | ||
77 | { | ||
78 | if (cachep && cachep->buffer) | ||
79 | { | ||
80 | dc_release_buffer(cachep->buffer); | ||
81 | cachep->buffer = NULL; | ||
82 | } | ||
83 | |||
84 | file_cache_reset(cachep); | ||
85 | } | ||
86 | |||
87 | |||
88 | /** Stream base APIs **/ | ||
89 | |||
90 | static inline void filestr_clear(struct filestr_base *stream) | ||
91 | { | ||
92 | stream->flags = 0; | ||
93 | stream->bindp = NULL; | ||
94 | #if 0 | ||
95 | stream->mtx = NULL; | ||
96 | #endif | ||
97 | } | ||
98 | |||
99 | /* actually late-allocate the assigned cache */ | ||
100 | void filestr_alloc_cache(struct filestr_base *stream) | ||
101 | { | ||
102 | file_cache_alloc(stream->cachep); | ||
103 | } | ||
104 | |||
105 | /* free the stream's cache buffer if it's its own */ | ||
106 | void filestr_free_cache(struct filestr_base *stream) | ||
107 | { | ||
108 | if (stream->cachep == &stream->cache) | ||
109 | file_cache_free(stream->cachep); | ||
110 | } | ||
111 | |||
112 | /* assign a cache to the stream */ | ||
113 | void filestr_assign_cache(struct filestr_base *stream, | ||
114 | struct filestr_cache *cachep) | ||
115 | { | ||
116 | if (cachep) | ||
117 | { | ||
118 | filestr_free_cache(stream); | ||
119 | stream->cachep = cachep; | ||
120 | } | ||
121 | else /* assign own cache */ | ||
122 | { | ||
123 | file_cache_reset(&stream->cache); | ||
124 | stream->cachep = &stream->cache; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | /* duplicate a cache into a stream's local cache */ | ||
129 | void filestr_copy_cache(struct filestr_base *stream, | ||
130 | struct filestr_cache *cachep) | ||
131 | { | ||
132 | stream->cachep = &stream->cache; | ||
133 | stream->cache.sector = cachep->sector; | ||
134 | stream->cache.flags = cachep->flags; | ||
135 | |||
136 | if (cachep->buffer) | ||
137 | { | ||
138 | file_cache_alloc(&stream->cache); | ||
139 | memcpy(stream->cache.buffer, cachep->buffer, DC_CACHE_BUFSIZE); | ||
140 | } | ||
141 | else | ||
142 | { | ||
143 | file_cache_free(&stream->cache); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | /* discard cache contents and invalidate it */ | ||
148 | void filestr_discard_cache(struct filestr_base *stream) | ||
149 | { | ||
150 | file_cache_reset(stream->cachep); | ||
151 | } | ||
152 | |||
153 | /* Initialize the base descriptor */ | ||
154 | void filestr_base_init(struct filestr_base *stream) | ||
155 | { | ||
156 | filestr_clear(stream); | ||
157 | file_cache_init(&stream->cache); | ||
158 | stream->cachep = &stream->cache; | ||
159 | } | ||
160 | |||
161 | /* free base descriptor resources */ | ||
162 | void filestr_base_destroy(struct filestr_base *stream) | ||
163 | { | ||
164 | filestr_clear(stream); | ||
165 | filestr_free_cache(stream); | ||
166 | } | ||
167 | |||
168 | |||
169 | /** Internal directory service functions **/ | ||
170 | |||
171 | /* read the next directory entry and return its FS info */ | ||
172 | int uncached_readdir_internal(struct filestr_base *stream, | ||
173 | struct file_base_info *infop, | ||
174 | struct fat_direntry *fatent) | ||
175 | { | ||
176 | return fat_readdir(&stream->fatstr, &infop->fatfile.e, | ||
177 | filestr_get_cache(stream), fatent); | ||
178 | } | ||
179 | |||
180 | /* rewind the FS directory to the beginning */ | ||
181 | void uncached_rewinddir_internal(struct file_base_info *infop) | ||
182 | { | ||
183 | fat_rewinddir(&infop->fatfile.e); | ||
184 | } | ||
185 | |||
186 | /* check if the directory is empty (ie. only "." and/or ".." entries | ||
187 | exist at most) */ | ||
188 | int test_dir_empty_internal(struct filestr_base *stream) | ||
189 | { | ||
190 | int rc; | ||
191 | |||
192 | struct file_base_info info; | ||
193 | fat_rewind(&stream->fatstr); | ||
194 | rewinddir_internal(&info); | ||
195 | |||
196 | while ((rc = readdir_internal(stream, &info, &dir_fatent)) > 0) | ||
197 | { | ||
198 | /* no OEM decoding is recessary for this simple check */ | ||
199 | if (!is_dotdir_name(dir_fatent.name)) | ||
200 | { | ||
201 | DEBUGF("Directory not empty\n"); | ||
202 | FILE_ERROR_RETURN(ENOTEMPTY, -1); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | if (rc < 0) | ||
207 | { | ||
208 | DEBUGF("I/O error checking directory: %d\n", rc); | ||
209 | FILE_ERROR_RETURN(EIO, rc * 10 - 2); | ||
210 | } | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /* iso decode the name to UTF-8 */ | ||
216 | void iso_decode_d_name(char *d_name) | ||
217 | { | ||
218 | if (is_dotdir_name(d_name)) | ||
219 | return; | ||
220 | |||
221 | char shortname[13]; | ||
222 | size_t len = strlcpy(shortname, d_name, sizeof (shortname)); | ||
223 | /* This MUST be the default codepage thus not something that could be | ||
224 | loaded on call */ | ||
225 | iso_decode(shortname, d_name, -1, len + 1); | ||
226 | } | ||
227 | |||
228 | #ifdef HAVE_DIRCACHE | ||
229 | /* nullify all the fields of the struct dirent */ | ||
230 | void empty_dirent(struct dirent *entry) | ||
231 | { | ||
232 | entry->d_name[0] = '\0'; | ||
233 | entry->info.attr = 0; | ||
234 | entry->info.size = 0; | ||
235 | entry->info.wrtdate = 0; | ||
236 | entry->info.wrttime = 0; | ||
237 | } | ||
238 | |||
239 | /* fill the native dirinfo from the static dir_fatent */ | ||
240 | void fill_dirinfo_native(struct dirinfo_native *dinp) | ||
241 | { | ||
242 | struct fat_direntry *fatent = get_dir_fatent(); | ||
243 | dinp->attr = fatent->attr; | ||
244 | dinp->size = fatent->filesize; | ||
245 | dinp->wrtdate = fatent->wrtdate; | ||
246 | dinp->wrttime = fatent->wrttime; | ||
247 | } | ||
248 | #endif /* HAVE_DIRCACHE */ | ||
249 | |||
250 | int uncached_readdir_dirent(struct filestr_base *stream, | ||
251 | struct dirscan_info *scanp, | ||
252 | struct dirent *entry) | ||
253 | { | ||
254 | struct fat_direntry fatent; | ||
255 | int rc = fat_readdir(&stream->fatstr, &scanp->fatscan, | ||
256 | filestr_get_cache(stream), &fatent); | ||
257 | |||
258 | /* FAT driver clears the struct fat_dirent if nothing is returned */ | ||
259 | strcpy(entry->d_name, fatent.name); | ||
260 | entry->info.attr = fatent.attr; | ||
261 | entry->info.size = fatent.filesize; | ||
262 | entry->info.wrtdate = fatent.wrtdate; | ||
263 | entry->info.wrttime = fatent.wrttime; | ||
264 | |||
265 | return rc; | ||
266 | } | ||
267 | |||
268 | /* rewind the FS directory pointer */ | ||
269 | void uncached_rewinddir_dirent(struct dirscan_info *scanp) | ||
270 | { | ||
271 | fat_rewinddir(&scanp->fatscan); | ||
272 | } | ||
273 | |||
274 | |||
275 | /** open_stream_internal() helpers and types **/ | ||
276 | |||
277 | struct pathwalk | ||
278 | { | ||
279 | const char *path; /* current location in input path */ | ||
280 | unsigned int callflags; /* callflags parameter */ | ||
281 | struct path_component_info *compinfo; /* compinfo parameter */ | ||
282 | file_size_t filesize; /* size of the file */ | ||
283 | }; | ||
284 | |||
285 | struct pathwalk_component | ||
286 | { | ||
287 | struct file_base_info info; /* basic file information */ | ||
288 | const char *name; /* component name location in path */ | ||
289 | uint16_t length; /* length of name of component */ | ||
290 | uint16_t attr; /* attributes of this component */ | ||
291 | struct pathwalk_component *nextp; /* parent if in use else next free */ | ||
292 | }; | ||
293 | |||
294 | #define WALK_RC_NOT_FOUND 0 /* successfully not found */ | ||
295 | #define WALK_RC_FOUND 1 /* found and opened */ | ||
296 | #define WALK_RC_FOUND_ROOT 2 /* found and opened sys/volume root */ | ||
297 | #define WALK_RC_CONT_AT_ROOT 3 /* continue at root level */ | ||
298 | |||
299 | /* return another struct pathwalk_component from the pool, or NULL if the | ||
300 | pool is completely used */ | ||
301 | static void * pathwalk_comp_alloc_(struct pathwalk_component *parentp) | ||
302 | { | ||
303 | /* static pool that goes to a depth of STATIC_COMP_NUM before allocating | ||
304 | elements from the stack */ | ||
305 | static struct pathwalk_component aux_pathwalk[STATIC_PATHCOMP_NUM]; | ||
306 | struct pathwalk_component *compp = NULL; | ||
307 | |||
308 | if (!parentp) | ||
309 | compp = &aux_pathwalk[0]; /* root */ | ||
310 | else if (PTR_IN_ARRAY(aux_pathwalk, parentp, STATIC_PATHCOMP_NUM-1)) | ||
311 | compp = parentp + 1; | ||
312 | |||
313 | return compp; | ||
314 | } | ||
315 | |||
316 | /* allocates components from the pool or stack depending upon the depth */ | ||
317 | #define pathwalk_comp_alloc(parentp) \ | ||
318 | ({ \ | ||
319 | void *__c = pathwalk_comp_alloc_(parentp); \ | ||
320 | if (!__builtin_constant_p(parentp) && !__c) \ | ||
321 | __c = alloca(sizeof (struct pathwalk_component)); \ | ||
322 | (struct pathwalk_component *)__c; \ | ||
323 | }) | ||
324 | |||
325 | /* fill in the details of the struct path_component_info for caller */ | ||
326 | static int fill_path_compinfo(struct pathwalk *walkp, | ||
327 | struct pathwalk_component *compp, | ||
328 | int rc) | ||
329 | { | ||
330 | if (rc == -ENOENT) | ||
331 | { | ||
332 | /* this component wasn't found; see if more of them exist or path | ||
333 | has trailing separators; if it does, this component should be | ||
334 | interpreted as a directory even if it doesn't exist and it's the | ||
335 | final one; also, this has to be the last part or it's an error*/ | ||
336 | const char *p = GOBBLE_PATH_SEPCH(walkp->path); | ||
337 | if (!*p) | ||
338 | { | ||
339 | if (p > walkp->path) | ||
340 | compp->attr |= ATTR_DIRECTORY; | ||
341 | |||
342 | rc = WALK_RC_NOT_FOUND; /* successfully not found */ | ||
343 | } | ||
344 | } | ||
345 | |||
346 | if (rc >= 0) | ||
347 | { | ||
348 | struct path_component_info *compinfo = walkp->compinfo; | ||
349 | compinfo->name = compp->name; | ||
350 | compinfo->length = compp->length; | ||
351 | compinfo->attr = compp->attr; | ||
352 | compinfo->filesize = walkp->filesize; | ||
353 | compinfo->parentinfo = (compp->nextp ?: compp)->info; | ||
354 | } | ||
355 | |||
356 | return rc; | ||
357 | } | ||
358 | |||
359 | /* open the final stream itself, if found */ | ||
360 | static int walk_open_info(struct pathwalk *walkp, | ||
361 | struct pathwalk_component *compp, | ||
362 | int rc, | ||
363 | struct filestr_base *stream) | ||
364 | { | ||
365 | /* this may make adjustments to things; do it first */ | ||
366 | if (walkp->compinfo) | ||
367 | rc = fill_path_compinfo(walkp, compp, rc); | ||
368 | |||
369 | if (rc < 0 || rc == WALK_RC_NOT_FOUND) | ||
370 | return rc; | ||
371 | |||
372 | unsigned int callflags = walkp->callflags; | ||
373 | bool isdir = compp->attr & ATTR_DIRECTORY; | ||
374 | |||
375 | /* type must match what is called for */ | ||
376 | switch (callflags & FF_TYPEMASK) | ||
377 | { | ||
378 | case FF_FILE: | ||
379 | if (!isdir) break; | ||
380 | DEBUGF("File is a directory\n"); | ||
381 | return -EISDIR; | ||
382 | case FF_DIR: | ||
383 | if (isdir) break; | ||
384 | DEBUGF("File is not a directory\n"); | ||
385 | return -ENOTDIR; | ||
386 | /* FF_ANYTYPE: basically, ignore FF_FILE/FF_DIR */ | ||
387 | } | ||
388 | |||
389 | /* FO_DIRECTORY must match type */ | ||
390 | if (isdir) | ||
391 | callflags |= FO_DIRECTORY; | ||
392 | else | ||
393 | callflags &= ~FO_DIRECTORY; | ||
394 | |||
395 | fileop_onopen_internal(stream, &compp->info, callflags); | ||
396 | return compp->nextp ? WALK_RC_FOUND : WALK_RC_FOUND_ROOT; | ||
397 | } | ||
398 | |||
399 | /* check the component against the prefix test info */ | ||
400 | static void walk_check_prefix(struct pathwalk *walkp, | ||
401 | struct pathwalk_component *compp) | ||
402 | { | ||
403 | if (compp->attr & ATTR_PREFIX) | ||
404 | return; | ||
405 | |||
406 | if (!fat_file_is_same(&compp->info.fatfile, | ||
407 | &walkp->compinfo->prefixp->fatfile)) | ||
408 | return; | ||
409 | |||
410 | compp->attr |= ATTR_PREFIX; | ||
411 | } | ||
412 | |||
413 | /* opens the component named by 'comp' in the directory 'parent' */ | ||
414 | static NO_INLINE int open_path_component(struct pathwalk *walkp, | ||
415 | struct pathwalk_component *compp, | ||
416 | struct filestr_base *stream) | ||
417 | { | ||
418 | int rc; | ||
419 | |||
420 | /* create a null-terminated copy of the component name */ | ||
421 | char *compname = strmemdupa(compp->name, compp->length); | ||
422 | |||
423 | unsigned int callflags = walkp->callflags; | ||
424 | struct pathwalk_component *parentp = compp->nextp; | ||
425 | |||
426 | /* children inherit the prefix coloring from the parent */ | ||
427 | compp->attr = parentp->attr & ATTR_PREFIX; | ||
428 | |||
429 | /* most of the next would be abstracted elsewhere if doing other | ||
430 | filesystems */ | ||
431 | |||
432 | /* scan parent for name; stream is converted to this parent */ | ||
433 | file_cache_reset(stream->cachep); | ||
434 | stream->infop = &parentp->info; | ||
435 | fat_filestr_init(&stream->fatstr, &parentp->info.fatfile); | ||
436 | rewinddir_internal(&compp->info); | ||
437 | |||
438 | while ((rc = readdir_internal(stream, &compp->info, &dir_fatent)) > 0) | ||
439 | { | ||
440 | if (rc > 1 && !(callflags & FF_NOISO)) | ||
441 | iso_decode_d_name(dir_fatent.name); | ||
442 | |||
443 | if (!strcasecmp(compname, dir_fatent.name)) | ||
444 | break; | ||
445 | } | ||
446 | |||
447 | if (rc == 0) | ||
448 | { | ||
449 | DEBUGF("File/directory not found\n"); | ||
450 | return -ENOENT; | ||
451 | } | ||
452 | else if (rc < 0) | ||
453 | { | ||
454 | DEBUGF("I/O error reading directory %d\n", rc); | ||
455 | return -EIO; | ||
456 | } | ||
457 | |||
458 | rc = fat_open(stream->fatstr.fatfilep, dir_fatent.firstcluster, | ||
459 | &compp->info.fatfile); | ||
460 | if (rc < 0) | ||
461 | { | ||
462 | DEBUGF("I/O error opening file/directory %s (%d)\n", | ||
463 | compname, rc); | ||
464 | return -EIO; | ||
465 | } | ||
466 | |||
467 | walkp->filesize = dir_fatent.filesize; | ||
468 | compp->attr |= dir_fatent.attr; | ||
469 | |||
470 | if (callflags & FF_CHECKPREFIX) | ||
471 | walk_check_prefix(walkp, compp); | ||
472 | |||
473 | return WALK_RC_FOUND; | ||
474 | } | ||
475 | |||
476 | /* parse a path component, open it and process the next */ | ||
477 | static NO_INLINE int | ||
478 | walk_path(struct pathwalk *walkp, struct pathwalk_component *compp, | ||
479 | struct filestr_base *stream) | ||
480 | { | ||
481 | int rc = WALK_RC_FOUND; | ||
482 | |||
483 | if (walkp->callflags & FF_CHECKPREFIX) | ||
484 | walk_check_prefix(walkp, compp); | ||
485 | |||
486 | /* alloca is used in a loop, but we reuse any blocks previously allocated | ||
487 | if we went up then back down; if the path takes us back to the root, then | ||
488 | everything is cleaned automatically */ | ||
489 | struct pathwalk_component *freep = NULL; | ||
490 | |||
491 | const char *name; | ||
492 | ssize_t len; | ||
493 | |||
494 | while ((len = parse_path_component(&walkp->path, &name))) | ||
495 | { | ||
496 | /* whatever is to be a parent must be a directory */ | ||
497 | if (!(compp->attr & ATTR_DIRECTORY)) | ||
498 | return -ENOTDIR; | ||
499 | |||
500 | switch (len) | ||
501 | { | ||
502 | case 1: | ||
503 | case 2: | ||
504 | /* check for "." and ".." */ | ||
505 | if (name[0] == '.') | ||
506 | { | ||
507 | if (len == 2 && name[1] == '.') | ||
508 | { | ||
509 | struct pathwalk_component *parentp = compp->nextp; | ||
510 | if (!parentp) | ||
511 | return WALK_RC_CONT_AT_ROOT; | ||
512 | |||
513 | compp->nextp = freep; | ||
514 | freep = compp; | ||
515 | compp = parentp; | ||
516 | } | ||
517 | |||
518 | break; | ||
519 | } | ||
520 | |||
521 | /* fallthrough */ | ||
522 | default: | ||
523 | if (len >= MAX_NAME) | ||
524 | return -ENAMETOOLONG; | ||
525 | |||
526 | struct pathwalk_component *newp = freep; | ||
527 | if (!newp) | ||
528 | newp = pathwalk_comp_alloc(compp); | ||
529 | else | ||
530 | freep = freep->nextp; | ||
531 | |||
532 | newp->nextp = compp; | ||
533 | compp = newp; | ||
534 | |||
535 | compp->name = name; | ||
536 | compp->length = len; | ||
537 | |||
538 | rc = open_path_component(walkp, compp, stream); | ||
539 | if (rc < 0) | ||
540 | break; | ||
541 | } | ||
542 | } | ||
543 | |||
544 | return walk_open_info(walkp, compp, rc, stream); | ||
545 | } | ||
546 | |||
547 | /* open a stream given a path to the resource */ | ||
548 | int open_stream_internal(const char *path, unsigned int callflags, | ||
549 | struct filestr_base *stream, | ||
550 | struct path_component_info *compinfo) | ||
551 | { | ||
552 | DEBUGF("%s(path=\"%s\",flg=%X,str=%p,compinfo=%p)\n", path, callflags, | ||
553 | stream, compinfo); | ||
554 | int rc; | ||
555 | |||
556 | filestr_base_init(stream); | ||
557 | |||
558 | if (!path_is_absolute(path)) | ||
559 | { | ||
560 | /* while this supports relative components, there is currently no | ||
561 | current working directory concept at this level by which to | ||
562 | fully qualify the path (though that would not be excessively | ||
563 | difficult to add) */ | ||
564 | DEBUGF("\"%s\" is not an absolute path\n" | ||
565 | "Only absolute paths currently supported.\n", path); | ||
566 | FILE_ERROR(path ? ENOENT : EFAULT, -1); | ||
567 | } | ||
568 | |||
569 | /* if !compinfo, then the result of this check is not visible anyway */ | ||
570 | if (!compinfo) | ||
571 | callflags &= ~FF_CHECKPREFIX; | ||
572 | |||
573 | struct pathwalk walk; | ||
574 | walk.path = path; | ||
575 | walk.callflags = callflags; | ||
576 | walk.compinfo = compinfo; | ||
577 | walk.filesize = 0; | ||
578 | |||
579 | struct pathwalk_component *rootp = pathwalk_comp_alloc(NULL); | ||
580 | rootp->nextp = NULL; | ||
581 | rootp->attr = ATTR_DIRECTORY; | ||
582 | |||
583 | #ifdef HAVE_MULTIVOLUME | ||
584 | int volume = 0, rootrc = WALK_RC_FOUND; | ||
585 | #endif /* HAVE_MULTIVOLUME */ | ||
586 | |||
587 | while (1) | ||
588 | { | ||
589 | const char *pathptr = walk.path; | ||
590 | |||
591 | #ifdef HAVE_MULTIVOLUME | ||
592 | /* this seamlessly integrates secondary filesystems into the | ||
593 | root namespace (e.g. "/<0>/../../<1>/../foo/." :<=> "/foo") */ | ||
594 | const char *p; | ||
595 | volume = path_strip_volume(pathptr, &p, false); | ||
596 | if (!CHECK_VOL(volume)) | ||
597 | { | ||
598 | DEBUGF("No such device or address: %d\n", volume); | ||
599 | FILE_ERROR(ENXIO, -2); | ||
600 | } | ||
601 | |||
602 | /* the root of this subpath is the system root? */ | ||
603 | rootrc = p == pathptr ? WALK_RC_FOUND_ROOT : WALK_RC_FOUND; | ||
604 | walk.path = p; | ||
605 | #endif /* HAVE_MULTIVOLUME */ | ||
606 | |||
607 | /* set name to start at last leading separator; names of volume | ||
608 | specifiers will be returned as "/<fooN>" */ | ||
609 | rootp->name = GOBBLE_PATH_SEPCH(pathptr) - 1; | ||
610 | rootp->length = | ||
611 | IF_MV( rootrc == WALK_RC_FOUND ? p - rootp->name : ) 1; | ||
612 | |||
613 | rc = fat_open_rootdir(IF_MV(volume,) &rootp->info.fatfile); | ||
614 | if (rc < 0) | ||
615 | { | ||
616 | /* not mounted */ | ||
617 | DEBUGF("No such device or address: %d\n", IF_MV_VOL(volume)); | ||
618 | rc = -ENXIO; | ||
619 | break; | ||
620 | } | ||
621 | |||
622 | get_rootinfo_internal(&rootp->info); | ||
623 | rc = walk_path(&walk, rootp, stream); | ||
624 | if (rc != WALK_RC_CONT_AT_ROOT) | ||
625 | break; | ||
626 | } | ||
627 | |||
628 | switch (rc) | ||
629 | { | ||
630 | case WALK_RC_FOUND_ROOT: | ||
631 | IF_MV( rc = rootrc; ) | ||
632 | case WALK_RC_NOT_FOUND: | ||
633 | case WALK_RC_FOUND: | ||
634 | break; | ||
635 | |||
636 | default: /* utter, abject failure :`( */ | ||
637 | DEBUGF("Open failed: rc=%d, errno=%d\n", rc, errno); | ||
638 | filestr_base_destroy(stream); | ||
639 | FILE_ERROR(-rc, -2); | ||
640 | } | ||
641 | |||
642 | file_cache_reset(stream->cachep); | ||
643 | |||
644 | file_error: | ||
645 | return rc; | ||
646 | } | ||
647 | |||
648 | /* close the stream referenced by 'stream' */ | ||
649 | int close_stream_internal(struct filestr_base *stream) | ||
650 | { | ||
651 | int rc; | ||
652 | unsigned int foflags = fileobj_get_flags(stream); | ||
653 | |||
654 | if ((foflags & (FO_SINGLE|FO_REMOVED)) == (FO_SINGLE|FO_REMOVED)) | ||
655 | { | ||
656 | /* nothing is referencing it so now remove the file's data */ | ||
657 | rc = fat_remove(&stream->infop->fatfile, FAT_RM_DATA); | ||
658 | if (rc < 0) | ||
659 | { | ||
660 | DEBUGF("I/O error removing file data: %d\n", rc); | ||
661 | FILE_ERROR(EIO, rc * 10 - 1); | ||
662 | } | ||
663 | } | ||
664 | |||
665 | rc = 0; | ||
666 | file_error: | ||
667 | /* close no matter what */ | ||
668 | fileop_onclose_internal(stream); | ||
669 | return rc; | ||
670 | } | ||
671 | |||
672 | /* create a new stream in the parent directory */ | ||
673 | int create_stream_internal(struct file_base_info *parentinfop, | ||
674 | const char *basename, size_t length, | ||
675 | unsigned int attr, unsigned int callflags, | ||
676 | struct filestr_base *stream) | ||
677 | { | ||
678 | /* assumes an attempt was made beforehand to open *stream with | ||
679 | open_stream_internal() which returned zero (successfully not found), | ||
680 | so does not initialize it here */ | ||
681 | const char * const name = strmemdupa(basename, length); | ||
682 | DEBUGF("Creating \"%s\"\n", name); | ||
683 | |||
684 | struct file_base_info info; | ||
685 | int rc = fat_create_file(&parentinfop->fatfile, name, attr, | ||
686 | &info.fatfile, get_dir_fatent_dircache()); | ||
687 | if (rc < 0) | ||
688 | { | ||
689 | DEBUGF("Create failed: %d\n", rc); | ||
690 | FILE_ERROR(rc == FAT_RC_ENOSPC ? ENOSPC : EIO, rc * 10 - 1); | ||
691 | } | ||
692 | |||
693 | /* dir_fatent is implicit arg */ | ||
694 | fileop_oncreate_internal(stream, &info, callflags, parentinfop, name); | ||
695 | rc = 0; | ||
696 | file_error: | ||
697 | return rc; | ||
698 | } | ||
699 | |||
700 | /* removes files and directories - back-end to remove() and rmdir() */ | ||
701 | int remove_stream_internal(const char *path, struct filestr_base *stream, | ||
702 | unsigned int callflags) | ||
703 | { | ||
704 | /* Only FF_* flags should be in callflags */ | ||
705 | int rc; | ||
706 | |||
707 | struct filestr_base opened_stream; | ||
708 | if (!stream) | ||
709 | stream = &opened_stream; | ||
710 | |||
711 | if (stream == &opened_stream) | ||
712 | { | ||
713 | /* no stream provided so open local one */ | ||
714 | rc = open_stream_internal(path, callflags, stream, NULL); | ||
715 | if (rc < 0) | ||
716 | { | ||
717 | DEBUGF("Failed opening path: %d\n", rc); | ||
718 | FILE_ERROR(ERRNO, rc * 10 - 1); | ||
719 | } | ||
720 | } | ||
721 | /* else ignore the 'path' argument */ | ||
722 | |||
723 | if (callflags & FF_DIR) | ||
724 | { | ||
725 | /* directory to be removed must be empty */ | ||
726 | rc = test_dir_empty_internal(stream); | ||
727 | if (rc < 0) | ||
728 | FILE_ERROR(ERRNO, rc * 10 - 2); | ||
729 | } | ||
730 | |||
731 | /* save old info since fat_remove() will destroy the dir info */ | ||
732 | struct file_base_info oldinfo = *stream->infop; | ||
733 | rc = fat_remove(&stream->infop->fatfile, FAT_RM_DIRENTRIES); | ||
734 | if (rc < 0) | ||
735 | { | ||
736 | DEBUGF("I/O error removing dir entries: %d\n", rc); | ||
737 | FILE_ERROR(EIO, rc * 10 - 3); | ||
738 | } | ||
739 | |||
740 | fileop_onremove_internal(stream, &oldinfo); | ||
741 | |||
742 | rc = 0; | ||
743 | file_error: | ||
744 | if (stream == &opened_stream) | ||
745 | { | ||
746 | /* will do removal of data below if this is the only reference */ | ||
747 | int rc2 = close_stream_internal(stream); | ||
748 | if (rc2 < 0 && rc >= 0) | ||
749 | { | ||
750 | rc = rc2 * 10 - 4; | ||
751 | DEBUGF("Success but failed closing stream: %d\n", rc); | ||
752 | } | ||
753 | } | ||
754 | |||
755 | return rc; | ||
756 | } | ||
757 | |||
758 | /* test file/directory existence with constraints */ | ||
759 | int test_stream_exists_internal(const char *path, unsigned int callflags) | ||
760 | { | ||
761 | /* only FF_* flags should be in callflags */ | ||
762 | struct filestr_base stream; | ||
763 | int rc = open_stream_internal(path, callflags, &stream, NULL); | ||
764 | if (rc > 0) | ||
765 | close_stream_internal(&stream); | ||
766 | |||
767 | return rc; | ||
768 | } | ||
769 | |||
770 | /* one-time init at startup */ | ||
771 | void filesystem_init(void) | ||
772 | { | ||
773 | mrsw_init(&file_internal_mrsw); | ||
774 | dc_init(); | ||
775 | fileobj_mgr_init(); | ||
776 | } | ||
diff --git a/firmware/common/filefuncs.c b/firmware/common/filefuncs.c deleted file mode 100644 index 16f8d88684..0000000000 --- a/firmware/common/filefuncs.c +++ /dev/null | |||
@@ -1,102 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "dir.h" | ||
23 | #include "stdlib.h" | ||
24 | #include "string.h" | ||
25 | #include "debug.h" | ||
26 | #include "file.h" | ||
27 | #include "filefuncs.h" | ||
28 | #include "string-extra.h" | ||
29 | |||
30 | #ifndef __PCTOOL__ | ||
31 | #ifdef HAVE_MULTIVOLUME | ||
32 | |||
33 | /* returns on which volume this is, and copies the reduced name | ||
34 | (sortof a preprocessor for volume-decorated pathnames) */ | ||
35 | int strip_volume(const char* name, char* namecopy) | ||
36 | { | ||
37 | int volume = 0; | ||
38 | const char *temp = name; | ||
39 | |||
40 | while (*temp == '/') /* skip all leading slashes */ | ||
41 | ++temp; | ||
42 | |||
43 | if (*temp && !strncmp(temp, VOL_NAMES, VOL_ENUM_POS)) | ||
44 | { | ||
45 | temp += VOL_ENUM_POS; /* behind special name */ | ||
46 | volume = atoi(temp); /* number is following */ | ||
47 | temp = strchr(temp, '/'); /* search for slash behind */ | ||
48 | if (temp != NULL) | ||
49 | name = temp; /* use the part behind the volume */ | ||
50 | else | ||
51 | name = "/"; /* else this must be the root dir */ | ||
52 | } | ||
53 | |||
54 | strlcpy(namecopy, name, MAX_PATH); | ||
55 | |||
56 | return volume; | ||
57 | } | ||
58 | #endif /* #ifdef HAVE_MULTIVOLUME */ | ||
59 | |||
60 | #endif /* __PCTOOL__ */ | ||
61 | /* Test file existence, using dircache of possible */ | ||
62 | bool file_exists(const char *file) | ||
63 | { | ||
64 | int fd; | ||
65 | |||
66 | #ifdef DEBUG | ||
67 | if (!file || !*file) | ||
68 | { | ||
69 | DEBUGF("%s(%p): Invalid parameter!\n", __func__, file); | ||
70 | return false; | ||
71 | } | ||
72 | #endif | ||
73 | |||
74 | #ifdef HAVE_DIRCACHE | ||
75 | if (dircache_is_enabled()) | ||
76 | return (dircache_get_entry_id(file) >= 0); | ||
77 | #endif | ||
78 | |||
79 | fd = open(file, O_RDONLY); | ||
80 | if (fd < 0) | ||
81 | return false; | ||
82 | close(fd); | ||
83 | return true; | ||
84 | } | ||
85 | |||
86 | bool dir_exists(const char *path) | ||
87 | { | ||
88 | DIR* d = opendir(path); | ||
89 | if (!d) | ||
90 | return false; | ||
91 | closedir(d); | ||
92 | return true; | ||
93 | } | ||
94 | |||
95 | |||
96 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(SIMULATOR) | ||
97 | struct dirinfo dir_get_info(DIR* parent, struct dirent *entry) | ||
98 | { | ||
99 | (void)parent; | ||
100 | return entry->info; | ||
101 | } | ||
102 | #endif | ||
diff --git a/firmware/common/fileobj_mgr.c b/firmware/common/fileobj_mgr.c new file mode 100644 index 0000000000..8e7831d36c --- /dev/null +++ b/firmware/common/fileobj_mgr.c | |||
@@ -0,0 +1,396 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "config.h" | ||
22 | #include "system.h" | ||
23 | #include "debug.h" | ||
24 | #include "file.h" | ||
25 | #include "dir.h" | ||
26 | #include "disk_cache.h" | ||
27 | #include "fileobj_mgr.h" | ||
28 | #include "dircache_redirect.h" | ||
29 | |||
30 | /** | ||
31 | * Manages file and directory streams on all volumes | ||
32 | * | ||
33 | * Intended for internal use by disk, file and directory code | ||
34 | */ | ||
35 | |||
36 | |||
37 | /* there will always be enough of these for all user handles, thus these | ||
38 | functions don't return failure codes */ | ||
39 | #define MAX_FILEOBJS (MAX_OPEN_HANDLES + AUX_FILEOBJS) | ||
40 | |||
41 | /* describes the file as an image on the storage medium */ | ||
42 | static struct fileobj_binding | ||
43 | { | ||
44 | struct file_base_binding bind; /* base info list item (first!) */ | ||
45 | uint16_t flags; /* F(D)(O)_* bits of this file/dir */ | ||
46 | uint16_t writers; /* number of writer streams */ | ||
47 | struct filestr_cache cache; /* write mode shared cache */ | ||
48 | file_size_t size; /* size of this file */ | ||
49 | struct ll_head list; /* open streams for this file/dir */ | ||
50 | } fobindings[MAX_FILEOBJS]; | ||
51 | static struct mutex stream_mutexes[MAX_FILEOBJS] SHAREDBSS_ATTR; | ||
52 | static struct ll_head free_bindings; | ||
53 | static struct ll_head busy_bindings[NUM_VOLUMES]; | ||
54 | |||
55 | #define BUSY_BINDINGS(volume) \ | ||
56 | (&busy_bindings[IF_MV_VOL(volume)]) | ||
57 | |||
58 | #define BASEBINDING_LIST(bindp) \ | ||
59 | (BUSY_BINDINGS(BASEBINDING_VOL(bindp))) | ||
60 | |||
61 | #define FREE_BINDINGS() \ | ||
62 | (&free_bindings) | ||
63 | |||
64 | #define BINDING_FIRST(type, volume...) \ | ||
65 | ((struct fileobj_binding *)type##_BINDINGS(volume)->head) | ||
66 | |||
67 | #define BINDING_NEXT(fobp) \ | ||
68 | ((struct fileobj_binding *)(fobp)->bind.node.next) | ||
69 | |||
70 | #define FOR_EACH_BINDING(volume, fobp) \ | ||
71 | for (struct fileobj_binding *fobp = BINDING_FIRST(BUSY, volume); \ | ||
72 | fobp; fobp = BINDING_NEXT(fobp)) | ||
73 | |||
74 | #define STREAM_FIRST(fobp) \ | ||
75 | ((struct filestr_base *)(fobp)->list.head) | ||
76 | |||
77 | #define STREAM_NEXT(s) \ | ||
78 | ((struct filestr_base *)(s)->node.next) | ||
79 | |||
80 | #define STREAM_THIS(s) \ | ||
81 | (s) | ||
82 | |||
83 | #define FOR_EACH_STREAM(what, start, s) \ | ||
84 | for (struct filestr_base *s = STREAM_##what(start); \ | ||
85 | s; s = STREAM_NEXT(s)) | ||
86 | |||
87 | |||
88 | /* syncs information for the stream's old and new parent directory if any are | ||
89 | currently opened */ | ||
90 | static void fileobj_sync_parent(const struct file_base_info *infop[], | ||
91 | int count) | ||
92 | { | ||
93 | FOR_EACH_BINDING(infop[0]->volume, fobp) | ||
94 | { | ||
95 | if ((fobp->flags & (FO_DIRECTORY|FO_REMOVED)) != FO_DIRECTORY) | ||
96 | continue; /* not directory or removed can't be parent of anything */ | ||
97 | |||
98 | struct filestr_base *parentstrp = STREAM_FIRST(fobp); | ||
99 | struct fat_file *parentfilep = &parentstrp->infop->fatfile; | ||
100 | |||
101 | for (int i = 0; i < count; i++) | ||
102 | { | ||
103 | if (!fat_dir_is_parent(parentfilep, &infop[i]->fatfile)) | ||
104 | continue; | ||
105 | |||
106 | /* discard scan/read caches' parent dir info */ | ||
107 | FOR_EACH_STREAM(THIS, parentstrp, s) | ||
108 | filestr_discard_cache(s); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | /* see if this file has open streams and return that fileobj_binding if so, | ||
114 | else grab a new one from the free list; returns true if this stream is | ||
115 | the only open one */ | ||
116 | static bool binding_assign(const struct file_base_info *srcinfop, | ||
117 | struct fileobj_binding **fobpp) | ||
118 | { | ||
119 | FOR_EACH_BINDING(srcinfop->fatfile.volume, fobp) | ||
120 | { | ||
121 | if (fobp->flags & FO_REMOVED) | ||
122 | continue; | ||
123 | |||
124 | if (fat_file_is_same(&srcinfop->fatfile, &fobp->bind.info.fatfile)) | ||
125 | { | ||
126 | /* already has open streams */ | ||
127 | *fobpp = fobp; | ||
128 | return false; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | /* not found - allocate anew */ | ||
133 | *fobpp = BINDING_FIRST(FREE); | ||
134 | ll_remove_first(FREE_BINDINGS()); | ||
135 | ll_init(&(*fobpp)->list); | ||
136 | return true; | ||
137 | } | ||
138 | |||
139 | /* mark descriptor as unused and return to the free list */ | ||
140 | static void binding_add_to_free_list(struct fileobj_binding *fobp) | ||
141 | { | ||
142 | fobp->flags = 0; | ||
143 | ll_insert_last(FREE_BINDINGS(), &fobp->bind.node); | ||
144 | } | ||
145 | |||
146 | /** File and directory internal interface **/ | ||
147 | |||
148 | void file_binding_insert_last(struct file_base_binding *bindp) | ||
149 | { | ||
150 | ll_insert_last(BASEBINDING_LIST(bindp), &bindp->node); | ||
151 | } | ||
152 | |||
153 | void file_binding_remove(struct file_base_binding *bindp) | ||
154 | { | ||
155 | ll_remove(BASEBINDING_LIST(bindp), &bindp->node); | ||
156 | } | ||
157 | |||
158 | #ifdef HAVE_DIRCACHE | ||
159 | void file_binding_insert_first(struct file_base_binding *bindp) | ||
160 | { | ||
161 | ll_insert_first(BASEBINDING_LIST(bindp), &bindp->node); | ||
162 | } | ||
163 | |||
164 | void file_binding_remove_next(struct file_base_binding *prevp, | ||
165 | struct file_base_binding *bindp) | ||
166 | { | ||
167 | ll_remove_next(BASEBINDING_LIST(bindp), &prevp->node); | ||
168 | (void)bindp; | ||
169 | } | ||
170 | #endif /* HAVE_DIRCACHE */ | ||
171 | |||
172 | /* opens the file object for a new stream and sets up the caches; | ||
173 | * the stream must already be opened at the FS driver level and *stream | ||
174 | * initialized. | ||
175 | * | ||
176 | * NOTE: switches stream->infop to the one kept in common for all streams of | ||
177 | * the same file, making a copy for only the first stream | ||
178 | */ | ||
179 | void fileobj_fileop_open(struct filestr_base *stream, | ||
180 | const struct file_base_info *srcinfop, | ||
181 | unsigned int callflags) | ||
182 | { | ||
183 | struct fileobj_binding *fobp; | ||
184 | bool first = binding_assign(srcinfop, &fobp); | ||
185 | |||
186 | /* add stream to this file's list */ | ||
187 | ll_insert_last(&fobp->list, &stream->node); | ||
188 | |||
189 | /* initiate the new stream into the enclave */ | ||
190 | stream->flags = FDO_BUSY | (callflags & (FD_WRITE|FD_WRONLY|FD_APPEND)); | ||
191 | stream->infop = &fobp->bind.info; | ||
192 | stream->fatstr.fatfilep = &fobp->bind.info.fatfile; | ||
193 | stream->bindp = &fobp->bind; | ||
194 | stream->mtx = &stream_mutexes[fobp - fobindings]; | ||
195 | |||
196 | if (first) | ||
197 | { | ||
198 | /* first stream for file */ | ||
199 | fobp->bind.info = *srcinfop; | ||
200 | fobp->flags = FDO_BUSY | FO_SINGLE | | ||
201 | (callflags & (FO_DIRECTORY|FO_TRUNC)); | ||
202 | fobp->writers = 0; | ||
203 | fobp->size = 0; | ||
204 | |||
205 | if (callflags & FD_WRITE) | ||
206 | { | ||
207 | /* first one is a writer */ | ||
208 | fobp->writers = 1; | ||
209 | file_cache_init(&fobp->cache); | ||
210 | filestr_assign_cache(stream, &fobp->cache); | ||
211 | } | ||
212 | |||
213 | fileobj_bind_file(&fobp->bind); | ||
214 | } | ||
215 | else | ||
216 | { | ||
217 | /* additional stream for file */ | ||
218 | fobp->flags &= ~FO_SINGLE; | ||
219 | fobp->flags |= callflags & FO_TRUNC; | ||
220 | |||
221 | /* once a file/directory, always a file/directory; such a change | ||
222 | is a bug */ | ||
223 | if ((callflags ^ fobp->flags) & FO_DIRECTORY) | ||
224 | { | ||
225 | DEBUGF("%s - FO_DIRECTORY flag does not match: %p %u\n", | ||
226 | __func__, stream, callflags); | ||
227 | } | ||
228 | |||
229 | if (fobp->writers) | ||
230 | { | ||
231 | /* already writers present */ | ||
232 | fobp->writers++; | ||
233 | filestr_assign_cache(stream, &fobp->cache); | ||
234 | } | ||
235 | else if (callflags & FD_WRITE) | ||
236 | { | ||
237 | /* first writer */ | ||
238 | fobp->writers = 1; | ||
239 | file_cache_init(&fobp->cache); | ||
240 | FOR_EACH_STREAM(FIRST, fobp, s) | ||
241 | filestr_assign_cache(s, &fobp->cache); | ||
242 | } | ||
243 | /* else another reader */ | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* close the stream and free associated resources */ | ||
248 | void fileobj_fileop_close(struct filestr_base *stream) | ||
249 | { | ||
250 | switch (stream->flags) | ||
251 | { | ||
252 | case 0: /* not added to manager */ | ||
253 | case FV_NONEXIST: /* forced-closed by unmounting */ | ||
254 | filestr_base_destroy(stream); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp; | ||
259 | unsigned int foflags = fobp->flags; | ||
260 | |||
261 | ll_remove(&fobp->list, &stream->node); | ||
262 | |||
263 | if ((foflags & FO_SINGLE) || fobp->writers == 0) | ||
264 | { | ||
265 | if (foflags & FO_SINGLE) | ||
266 | { | ||
267 | /* last stream for file; close everything */ | ||
268 | fileobj_unbind_file(&fobp->bind); | ||
269 | |||
270 | if (fobp->writers) | ||
271 | file_cache_free(&fobp->cache); | ||
272 | |||
273 | binding_add_to_free_list(fobp); | ||
274 | } | ||
275 | } | ||
276 | else if ((stream->flags & FD_WRITE) && --fobp->writers == 0) | ||
277 | { | ||
278 | /* only readers remain; switch back to stream-local caching */ | ||
279 | FOR_EACH_STREAM(FIRST, fobp, s) | ||
280 | filestr_copy_cache(s, &fobp->cache); | ||
281 | |||
282 | file_cache_free(&fobp->cache); | ||
283 | } | ||
284 | |||
285 | if (!(foflags & FO_SINGLE) && fobp->list.head == fobp->list.tail) | ||
286 | fobp->flags |= FO_SINGLE; /* only one open stream remaining */ | ||
287 | |||
288 | filestr_base_destroy(stream); | ||
289 | } | ||
290 | |||
291 | /* informs manager that file has been created */ | ||
292 | void fileobj_fileop_create(struct filestr_base *stream, | ||
293 | const struct file_base_info *srcinfop, | ||
294 | unsigned int callflags) | ||
295 | { | ||
296 | fileobj_fileop_open(stream, srcinfop, callflags); | ||
297 | fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1); | ||
298 | } | ||
299 | |||
300 | /* informs manager that file has been removed */ | ||
301 | void fileobj_fileop_remove(struct filestr_base *stream, | ||
302 | const struct file_base_info *oldinfop) | ||
303 | { | ||
304 | ((struct fileobj_binding *)stream->bindp)->flags |= FO_REMOVED; | ||
305 | fileobj_sync_parent((const struct file_base_info *[]){ oldinfop }, 1); | ||
306 | } | ||
307 | |||
308 | /* informs manager that file has been renamed */ | ||
309 | void fileobj_fileop_rename(struct filestr_base *stream, | ||
310 | const struct file_base_info *oldinfop) | ||
311 | { | ||
312 | /* if there is old info then this was a move and the old parent has to be | ||
313 | informed */ | ||
314 | int count = oldinfop ? 2 : 1; | ||
315 | fileobj_sync_parent(&(const struct file_base_info *[]) | ||
316 | { oldinfop, stream->infop }[2 - count], | ||
317 | count); | ||
318 | } | ||
319 | |||
320 | /* informs manager than directory entries have been updated */ | ||
321 | void fileobj_fileop_sync(struct filestr_base *stream) | ||
322 | { | ||
323 | fileobj_sync_parent((const struct file_base_info *[]){ stream->infop }, 1); | ||
324 | } | ||
325 | |||
326 | /* inform manager that file has been truncated */ | ||
327 | void fileobj_fileop_truncate(struct filestr_base *stream) | ||
328 | { | ||
329 | /* let caller update internal info */ | ||
330 | FOR_EACH_STREAM(FIRST, (struct fileobj_binding *)stream->bindp, s) | ||
331 | ftruncate_internal_callback(stream, s); | ||
332 | } | ||
333 | |||
334 | /* query for the pointer to the size storage for the file object */ | ||
335 | file_size_t * fileobj_get_sizep(const struct filestr_base *stream) | ||
336 | { | ||
337 | if (!stream->bindp) | ||
338 | return NULL; | ||
339 | |||
340 | return &((struct fileobj_binding *)stream->bindp)->size; | ||
341 | } | ||
342 | |||
343 | /* query manager bitflags for the file object */ | ||
344 | unsigned int fileobj_get_flags(const struct filestr_base *stream) | ||
345 | { | ||
346 | if (!stream->bindp) | ||
347 | return 0; | ||
348 | |||
349 | return ((struct fileobj_binding *)stream->bindp)->flags; | ||
350 | } | ||
351 | |||
352 | /* change manager bitflags for the file object */ | ||
353 | void fileobj_change_flags(struct filestr_base *stream, | ||
354 | unsigned int flags, unsigned int mask) | ||
355 | { | ||
356 | struct fileobj_binding *fobp = (struct fileobj_binding *)stream->bindp; | ||
357 | if (fobp) | ||
358 | fobp->flags = (fobp->flags & ~mask) | (flags & mask); | ||
359 | } | ||
360 | |||
361 | /* mark all open streams on a device as "nonexistant" */ | ||
362 | void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)) | ||
363 | { | ||
364 | /* right now, there is nothing else to be freed when marking a descriptor | ||
365 | as "nonexistant" but a callback could be added if that changes */ | ||
366 | FOR_EACH_VOLUME(volume, v) | ||
367 | { | ||
368 | struct fileobj_binding *fobp; | ||
369 | while ((fobp = BINDING_FIRST(BUSY, v))) | ||
370 | { | ||
371 | struct filestr_base *s; | ||
372 | while ((s = STREAM_FIRST(fobp))) | ||
373 | { | ||
374 | /* keep it "busy" to avoid races; any valid file/directory | ||
375 | descriptor returned by an open call should always be | ||
376 | closed by whomever opened it (of course!) */ | ||
377 | fileop_onclose_internal(s); | ||
378 | s->flags = FV_NONEXIST; | ||
379 | } | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | /* one-time init at startup */ | ||
385 | void fileobj_mgr_init(void) | ||
386 | { | ||
387 | for (unsigned int i = 0; i < NUM_VOLUMES; i++) | ||
388 | ll_init(BUSY_BINDINGS(i)); | ||
389 | |||
390 | ll_init(FREE_BINDINGS()); | ||
391 | for (unsigned int i = 0; i < MAX_FILEOBJS; i++) | ||
392 | { | ||
393 | mutex_init(&stream_mutexes[i]); | ||
394 | binding_add_to_free_list(&fobindings[i]); | ||
395 | } | ||
396 | } | ||
diff --git a/firmware/common/pathfuncs.c b/firmware/common/pathfuncs.c new file mode 100644 index 0000000000..4410275adb --- /dev/null +++ b/firmware/common/pathfuncs.c | |||
@@ -0,0 +1,421 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <string.h> | ||
22 | #include <ctype.h> | ||
23 | #include "system.h" | ||
24 | #include "pathfuncs.h" | ||
25 | #include "string-extra.h" | ||
26 | |||
27 | #ifdef HAVE_MULTIVOLUME | ||
28 | #include <stdio.h> | ||
29 | #include "storage.h" | ||
30 | |||
31 | enum storage_name_dec_indexes | ||
32 | { | ||
33 | #if (CONFIG_STORAGE & STORAGE_ATA) | ||
34 | STORAGE_DEC_IDX_ATA, | ||
35 | #endif | ||
36 | #if (CONFIG_STORAGE & STORAGE_MMC) | ||
37 | STORAGE_DEC_IDX_MMC, | ||
38 | #endif | ||
39 | #if (CONFIG_STORAGE & STORAGE_SD) | ||
40 | STORAGE_DEC_IDX_SD, | ||
41 | #endif | ||
42 | #if (CONFIG_STORAGE & STORAGE_NAND) | ||
43 | STORAGE_DEC_IDX_NAND, | ||
44 | #endif | ||
45 | #if (CONFIG_STORAGE & STORAGE_RAMDISK) | ||
46 | STORAGE_DEC_IDX_RAMDISK, | ||
47 | #endif | ||
48 | #if (CONFIG_STORAGE & STORAGE_HOSTFS) | ||
49 | STORAGE_DEC_IDX_HOSTFS, | ||
50 | #endif | ||
51 | STORAGE_NUM_DEC_IDX, | ||
52 | }; | ||
53 | |||
54 | static const char * const storage_dec_names[STORAGE_NUM_DEC_IDX+1] = | ||
55 | { | ||
56 | #if (CONFIG_STORAGE & STORAGE_ATA) | ||
57 | [STORAGE_DEC_IDX_ATA] = ATA_VOL_DEC, | ||
58 | #endif | ||
59 | #if (CONFIG_STORAGE & STORAGE_MMC) | ||
60 | [STORAGE_DEC_IDX_MMC] = MMC_VOL_DEC, | ||
61 | #endif | ||
62 | #if (CONFIG_STORAGE & STORAGE_SD) | ||
63 | [STORAGE_DEC_IDX_SD] = SD_VOL_DEC, | ||
64 | #endif | ||
65 | #if (CONFIG_STORAGE & STORAGE_NAND) | ||
66 | [STORAGE_DEC_IDX_NAND] = NAND_VOL_DEC, | ||
67 | #endif | ||
68 | #if (CONFIG_STORAGE & STORAGE_RAMDISK) | ||
69 | [STORAGE_DEC_IDX_RAMDISK] = RAMDISK_VOL_DEC, | ||
70 | #endif | ||
71 | #if (CONFIG_STORAGE & STORAGE_HOSTFS) | ||
72 | [STORAGE_DEC_IDX_HOSTFS] = HOSTFS_VOL_DEC, | ||
73 | #endif | ||
74 | [STORAGE_NUM_DEC_IDX] = DEFAULT_VOL_DEC, | ||
75 | }; | ||
76 | |||
77 | static const unsigned char storage_dec_indexes[STORAGE_NUM_TYPES+1] = | ||
78 | { | ||
79 | [0 ... STORAGE_NUM_TYPES] = STORAGE_NUM_DEC_IDX, | ||
80 | #if (CONFIG_STORAGE & STORAGE_ATA) | ||
81 | [STORAGE_ATA_NUM] = STORAGE_DEC_IDX_ATA, | ||
82 | #endif | ||
83 | #if (CONFIG_STORAGE & STORAGE_MMC) | ||
84 | [STORAGE_MMC_NUM] = STORAGE_DEC_IDX_MMC, | ||
85 | #endif | ||
86 | #if (CONFIG_STORAGE & STORAGE_SD) | ||
87 | [STORAGE_SD_NUM] = STORAGE_DEC_IDX_SD, | ||
88 | #endif | ||
89 | #if (CONFIG_STORAGE & STORAGE_NAND) | ||
90 | [STORAGE_NAND_NUM] = STORAGE_DEC_IDX_NAND, | ||
91 | #endif | ||
92 | #if (CONFIG_STORAGE & STORAGE_RAMDISK) | ||
93 | [STORAGE_RAMDISK_NUM] = STORAGE_DEC_IDX_RAMDISK, | ||
94 | #endif | ||
95 | #if (CONFIG_STORAGE & STORAGE_HOSTFS) | ||
96 | [STORAGE_HOSTFS_NUM] = STORAGE_DEC_IDX_HOSTFS, | ||
97 | #endif | ||
98 | }; | ||
99 | |||
100 | /* Returns on which volume this is and sets *nameptr to the portion of the | ||
101 | * path after the volume specifier, which could be the null if the path is | ||
102 | * just a volume root. If *nameptr > name, then a volume specifier was | ||
103 | * found. If 'greedy' is 'true', then it all separators after the volume | ||
104 | * specifier are consumed, if one was found. | ||
105 | */ | ||
106 | int path_strip_volume(const char *name, const char **nameptr, bool greedy) | ||
107 | { | ||
108 | int volume = 0; | ||
109 | const char *t = name; | ||
110 | int c, v = 0; | ||
111 | |||
112 | /* format: "/<xxx##>/foo/bar" | ||
113 | * the "xxx" is pure decoration; only an unbroken trailing string of | ||
114 | * digits within the brackets is parsed as the volume number and of | ||
115 | * those, only the last ones VOL_MUM_MAX allows. | ||
116 | */ | ||
117 | c = *(t = GOBBLE_PATH_SEPCH(t)); /* skip all leading slashes */ | ||
118 | if (c != VOL_START_TOK) /* missing start token? no volume */ | ||
119 | goto volume0; | ||
120 | |||
121 | do | ||
122 | { | ||
123 | switch (c) | ||
124 | { | ||
125 | case '0' ... '9': /* digit; parse volume number */ | ||
126 | v = (v * 10 + c - '0') % VOL_NUM_MAX; | ||
127 | break; | ||
128 | case '\0': | ||
129 | case PATH_SEPCH: /* no closing bracket; no volume */ | ||
130 | goto volume0; | ||
131 | default: /* something else; reset volume */ | ||
132 | v = 0; | ||
133 | } | ||
134 | } | ||
135 | while ((c = *++t) != VOL_END_TOK); /* found end token? */ | ||
136 | |||
137 | if (!(c = *++t)) /* no more path and no '/' is ok */ | ||
138 | ; | ||
139 | else if (c != PATH_SEPCH) /* more path and no separator after end */ | ||
140 | goto volume0; | ||
141 | else if (greedy) | ||
142 | t = GOBBLE_PATH_SEPCH(++t); /* strip remaining separators */ | ||
143 | |||
144 | /* if 'greedy' is true and **nameptr == '\0' then it's only a volume | ||
145 | root whether or not it has trailing separators */ | ||
146 | |||
147 | volume = v; | ||
148 | name = t; | ||
149 | volume0: | ||
150 | if (nameptr) | ||
151 | *nameptr = name; | ||
152 | return volume; | ||
153 | } | ||
154 | |||
155 | /* Returns the volume specifier decorated with the storage type name. | ||
156 | * Assumes the supplied buffer size is at least {VOL_MAX_LEN}+1. | ||
157 | */ | ||
158 | int get_volume_name(int volume, char *buffer) | ||
159 | { | ||
160 | if (volume < 0) | ||
161 | { | ||
162 | *buffer = '\0'; | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | volume %= VOL_NUM_MAX; /* as path parser would have it */ | ||
167 | |||
168 | int type = storage_driver_type(volume_drive(volume)); | ||
169 | if (type < 0 || type > STORAGE_NUM_TYPES) | ||
170 | type = STORAGE_NUM_TYPES; | ||
171 | |||
172 | const char *voldec = storage_dec_names[storage_dec_indexes[type]]; | ||
173 | return snprintf(buffer, VOL_MAX_LEN + 1, "%c%s%d%c", | ||
174 | VOL_START_TOK, voldec, volume, VOL_END_TOK); | ||
175 | } | ||
176 | #endif /* HAVE_MULTIVOLUME */ | ||
177 | |||
178 | /* Just like path_strip_volume() but strips a leading drive specifier and | ||
179 | * returns the drive number (A=0, B=1, etc.). -1 means no drive was found. | ||
180 | * If 'greedy' is 'true', all separators after the volume are consumed. | ||
181 | */ | ||
182 | int path_strip_drive(const char *name, const char **nameptr, bool greedy) | ||
183 | { | ||
184 | int c = toupper(*name); | ||
185 | |||
186 | if (c >= 'A' && c <= 'Z' && name[1] == PATH_DRVSEPCH) | ||
187 | { | ||
188 | name = &name[2]; | ||
189 | if (greedy) | ||
190 | name = GOBBLE_PATH_SEPCH(name); | ||
191 | |||
192 | *nameptr = name; | ||
193 | return c - 'A'; | ||
194 | } | ||
195 | |||
196 | *nameptr = name; | ||
197 | return -1; | ||
198 | } | ||
199 | |||
200 | /* Strips leading and trailing whitespace from a path | ||
201 | * " a/b \txyz" *nameptr->a, len=3: "a/b" | ||
202 | */ | ||
203 | size_t path_trim_whitespace(const char *name, const char **nameptr) | ||
204 | { | ||
205 | int c; | ||
206 | while ((c = *name) <= ' ' && c) | ||
207 | ++name; | ||
208 | |||
209 | const char *first = name; | ||
210 | const char *last = name; | ||
211 | |||
212 | while (1) | ||
213 | { | ||
214 | if (c < ' ') | ||
215 | { | ||
216 | *nameptr = first; | ||
217 | return last - first; | ||
218 | } | ||
219 | |||
220 | while ((c = *++name) > ' '); | ||
221 | last = name; | ||
222 | while (c == ' ') c = *++name; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | /* Strips directory components from the path | ||
227 | * "" *nameptr->NUL, len=0: "" | ||
228 | * "/" *nameptr->/, len=1: "/" | ||
229 | * "//" *nameptr->2nd /, len=1: "/" | ||
230 | * "/a" *nameptr->a, len=1: "a" | ||
231 | * "/a/bc" *nameptr->b, len=2: "bc" | ||
232 | * "d" *nameptr->d, len=1: "d" | ||
233 | * "ef/gh" *nameptr->g, len=2: "gh" | ||
234 | */ | ||
235 | size_t path_basename(const char *name, const char **nameptr) | ||
236 | { | ||
237 | const char *p = name; | ||
238 | const char *q = p; | ||
239 | const char *r = q; | ||
240 | |||
241 | while (*(p = GOBBLE_PATH_SEPCH(p))) | ||
242 | { | ||
243 | q = p; | ||
244 | p = GOBBLE_PATH_COMP(++p); | ||
245 | r = p; | ||
246 | } | ||
247 | |||
248 | if (r == name && p > name) | ||
249 | q = p, r = q--; /* root - return last slash */ | ||
250 | /* else path is an empty string */ | ||
251 | |||
252 | *nameptr = q; | ||
253 | return r - q; | ||
254 | } | ||
255 | |||
256 | /* Strips the trailing component from the path | ||
257 | * "" *nameptr->NUL, len=0: "" | ||
258 | * "/" *nameptr->/, len=1: "/" | ||
259 | * "//" *nameptr->2nd /, len=1: "/" | ||
260 | * "/a" *nameptr->/, len=1: "/" | ||
261 | * "/a/bc" *nameptr->/, len=2: "/a" | ||
262 | * "d" *nameptr->d, len=0: "" | ||
263 | * "ef/gh" *nameptr->e, len=2: "ef" | ||
264 | */ | ||
265 | size_t path_dirname(const char *name, const char **nameptr) | ||
266 | { | ||
267 | const char *p = GOBBLE_PATH_SEPCH(name); | ||
268 | const char *q = name; | ||
269 | const char *r = p; | ||
270 | |||
271 | while (*(p = GOBBLE_PATH_COMP(p))) | ||
272 | { | ||
273 | const char *s = p; | ||
274 | |||
275 | if (!*(p = GOBBLE_PATH_SEPCH(p))) | ||
276 | break; | ||
277 | |||
278 | q = s; | ||
279 | } | ||
280 | |||
281 | if (q == name && r > name) | ||
282 | name = r, q = name--; /* root - return last slash */ | ||
283 | |||
284 | *nameptr = name; | ||
285 | return q - name; | ||
286 | } | ||
287 | |||
288 | /* Removes trailing separators from a path | ||
289 | * "" *nameptr->NUL, len=0: "" | ||
290 | * "/" *nameptr->/, len=1: "/" | ||
291 | * "//" *nameptr->2nd /, len=1: "/" | ||
292 | * "/a/" *nameptr->/, len=2: "/a" | ||
293 | * "//b/" *nameptr->1st /, len=3: "//b" | ||
294 | * "/c/" *nameptr->/, len=2: "/c" | ||
295 | */ | ||
296 | size_t path_strip_trailing_separators(const char *name, const char **nameptr) | ||
297 | { | ||
298 | const char *p; | ||
299 | size_t len = path_basename(name, &p); | ||
300 | |||
301 | if (len == 1 && *p == '/' && p > name) | ||
302 | { | ||
303 | *nameptr = p; | ||
304 | name = p - 1; /* root with multiple separators */ | ||
305 | } | ||
306 | else | ||
307 | { | ||
308 | *nameptr = name; | ||
309 | p += len; /* length to end of basename */ | ||
310 | } | ||
311 | |||
312 | return p - name; | ||
313 | } | ||
314 | |||
315 | /* Transforms "wrong" separators into the correct ones | ||
316 | * "c:\windows\system32" -> "c:/windows/system32" | ||
317 | * | ||
318 | * 'path' and 'dstpath' may either be the same buffer or non-overlapping | ||
319 | */ | ||
320 | void path_correct_separators(char *dstpath, const char *path) | ||
321 | { | ||
322 | char *dstp = dstpath; | ||
323 | const char *p = path; | ||
324 | |||
325 | while (1) | ||
326 | { | ||
327 | const char *next = strchr(p, PATH_BADSEPCH); | ||
328 | if (!next) | ||
329 | break; | ||
330 | |||
331 | size_t size = next - p; | ||
332 | |||
333 | if (dstpath != path) | ||
334 | memcpy(dstp, p, size); /* not in-place */ | ||
335 | |||
336 | dstp += size; | ||
337 | *dstp++ = PATH_SEPCH; | ||
338 | p = next + 1; | ||
339 | } | ||
340 | |||
341 | if (dstpath != path) | ||
342 | strcpy(dstp, p); | ||
343 | } | ||
344 | |||
345 | /* Appends one path to another, adding separators between components if needed. | ||
346 | * Return value and behavior is otherwise as strlcpy so that truncation may be | ||
347 | * detected. | ||
348 | * | ||
349 | * For basepath and component: | ||
350 | * PA_SEP_HARD adds a separator even if the base path is empty | ||
351 | * PA_SEP_SOFT adds a separator only if the base path is not empty | ||
352 | */ | ||
353 | size_t path_append(char *buf, const char *basepath, | ||
354 | const char *component, size_t bufsize) | ||
355 | { | ||
356 | const char *base = basepath && basepath[0] ? basepath : buf; | ||
357 | if (!base) | ||
358 | return bufsize; /* won't work to get lengths from buf */ | ||
359 | |||
360 | if (!buf) | ||
361 | bufsize = 0; | ||
362 | |||
363 | if (path_is_absolute(component)) | ||
364 | { | ||
365 | /* 'component' is absolute; replace all */ | ||
366 | basepath = component; | ||
367 | component = ""; | ||
368 | } | ||
369 | |||
370 | /* if basepath is not null or empty, buffer contents are replaced, | ||
371 | otherwise buf contains the base path */ | ||
372 | size_t len = base == buf ? strlen(buf) : strlcpy(buf, basepath, bufsize); | ||
373 | |||
374 | bool separate = false; | ||
375 | |||
376 | if (!basepath || !component) | ||
377 | separate = !len || base[len-1] != PATH_SEPCH; | ||
378 | else if (component[0]) | ||
379 | separate = len && base[len-1] != PATH_SEPCH; | ||
380 | |||
381 | /* caller might lie about size of buf yet use buf as the base */ | ||
382 | if (base == buf && bufsize && len >= bufsize) | ||
383 | buf[bufsize - 1] = '\0'; | ||
384 | |||
385 | buf += len; | ||
386 | bufsize -= MIN(len, bufsize); | ||
387 | |||
388 | if (separate && (len++, bufsize > 0) && --bufsize > 0) | ||
389 | *buf++ = PATH_SEPCH; | ||
390 | |||
391 | return len + strlcpy(buf, component ?: "", bufsize); | ||
392 | } | ||
393 | |||
394 | /* Returns the location and length of the next path component, consuming the | ||
395 | * input in the process. | ||
396 | * | ||
397 | * "/a/bc/d" breaks into: | ||
398 | * start: *namep->1st / | ||
399 | * call 1: *namep->a, *pathp->2nd / len=1: "a" | ||
400 | * call 2: *namep->b, *pathp->3rd / len=2: "bc" | ||
401 | * call 3: *namep->d, *pathp->NUL, len=1: "d" | ||
402 | * call 4: *namep->NUL, *pathp->NUL, len=0: "" | ||
403 | * | ||
404 | * Returns: 0 if the input has been consumed | ||
405 | * The length of the component otherwise | ||
406 | */ | ||
407 | ssize_t parse_path_component(const char **pathp, const char **namep) | ||
408 | { | ||
409 | /* a component starts at a non-separator and continues until the next | ||
410 | separator or null */ | ||
411 | const char *p = GOBBLE_PATH_SEPCH(*pathp); | ||
412 | const char *name = p; | ||
413 | |||
414 | if (*p) | ||
415 | p = GOBBLE_PATH_COMP(++p); | ||
416 | |||
417 | *pathp = p; | ||
418 | *namep = name; | ||
419 | |||
420 | return p - name; | ||
421 | } | ||
diff --git a/firmware/common/rbpaths.c b/firmware/common/rbpaths.c deleted file mode 100644 index 69543bc3a7..0000000000 --- a/firmware/common/rbpaths.c +++ /dev/null | |||
@@ -1,432 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Thomas Martitz | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | |||
23 | #include <stdio.h> /* snprintf */ | ||
24 | #include <stdlib.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <time.h> | ||
28 | #include <unistd.h> | ||
29 | #include "config.h" | ||
30 | #include "rbpaths.h" | ||
31 | #include "crc32.h" | ||
32 | #include "file.h" /* MAX_PATH */ | ||
33 | #include "logf.h" | ||
34 | #include "gcc_extensions.h" | ||
35 | #include "string-extra.h" | ||
36 | #include "filefuncs.h" | ||
37 | |||
38 | /* In this file we need the actual OS library functions, not the shadowed | ||
39 | * wrapper used within Rockbox' application code (except SDL adds | ||
40 | * another layer) */ | ||
41 | #undef open | ||
42 | #undef creat | ||
43 | #undef remove | ||
44 | #undef rename | ||
45 | #undef opendir | ||
46 | #undef closedir | ||
47 | #undef readdir | ||
48 | #undef mkdir | ||
49 | #undef rmdir | ||
50 | #undef dirent | ||
51 | #undef DIR | ||
52 | #undef readlink | ||
53 | |||
54 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
55 | static const char rbhome[] = "/sdcard"; | ||
56 | #elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) && !defined(__PCTOOL__) | ||
57 | const char *rbhome; | ||
58 | #else | ||
59 | /* YPR0, YPR1 */ | ||
60 | static const char rbhome[] = HOME_DIR; | ||
61 | #endif | ||
62 | |||
63 | #if !(defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) && !defined(__PCTOOL__) | ||
64 | /* Special dirs are user-accessible (and user-writable) dirs which take priority | ||
65 | * over the ones where Rockbox is installed to. Classic example would be | ||
66 | * $HOME/.config/rockbox.org vs /usr/share/rockbox */ | ||
67 | #define HAVE_SPECIAL_DIRS | ||
68 | #endif | ||
69 | |||
70 | /* flags for get_user_file_path() */ | ||
71 | /* whether you need write access to that file/dir, especially true | ||
72 | * for runtime generated files (config.cfg) */ | ||
73 | #define NEED_WRITE (1<<0) | ||
74 | /* file or directory? */ | ||
75 | #define IS_FILE (1<<1) | ||
76 | |||
77 | #ifdef HAVE_MULTIDRIVE | ||
78 | static uint32_t rbhome_hash; | ||
79 | /* A special link is created under e.g. HOME_DIR/<microSD1>, e.g. to access | ||
80 | * external storage in a convinient location, much similar to the mount | ||
81 | * point on our native targets. Here they are treated as symlink (one which | ||
82 | * doesn't actually exist in the filesystem and therefore we have to override | ||
83 | * readlink() */ | ||
84 | static const char *handle_special_links(const char* link, unsigned flags, | ||
85 | char *buf, const size_t bufsize) | ||
86 | { | ||
87 | (void) flags; | ||
88 | char vol_string[VOL_ENUM_POS + 8]; | ||
89 | int len = sprintf(vol_string, VOL_NAMES, 1); | ||
90 | |||
91 | /* link might be passed with or without HOME_DIR expanded. To handle | ||
92 | * both perform substring matching (VOL_NAMES is unique enough) */ | ||
93 | const char *begin = strstr(link, vol_string); | ||
94 | if (begin) | ||
95 | { | ||
96 | /* begin now points to the start of vol_string within link, | ||
97 | * we want to copy the remainder of the paths, prefixed by | ||
98 | * the actual mount point (the remainder might be "") */ | ||
99 | snprintf(buf, bufsize, MULTIDRIVE_DIR"%s", begin + len); | ||
100 | return buf; | ||
101 | } | ||
102 | |||
103 | return link; | ||
104 | } | ||
105 | #endif | ||
106 | |||
107 | void paths_init(void) | ||
108 | { | ||
109 | #ifdef HAVE_SPECIAL_DIRS | ||
110 | /* make sure $HOME/.config/rockbox.org exists, it's needed for config.cfg */ | ||
111 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
112 | mkdir("/sdcard/rockbox", 0777); | ||
113 | mkdir("/sdcard/rockbox/rocks.data", 0777); | ||
114 | #else | ||
115 | char config_dir[MAX_PATH]; | ||
116 | |||
117 | const char *home = getenv("RBROOT"); | ||
118 | if (!home) | ||
119 | { | ||
120 | home = getenv("HOME"); | ||
121 | } | ||
122 | if (!home) | ||
123 | { | ||
124 | logf("HOME environment var not set. Can't write config"); | ||
125 | return; | ||
126 | } | ||
127 | |||
128 | rbhome = home; | ||
129 | snprintf(config_dir, sizeof(config_dir), "%s/.config", home); | ||
130 | mkdir(config_dir, 0777); | ||
131 | snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org", home); | ||
132 | mkdir(config_dir, 0777); | ||
133 | /* Plugin data directory */ | ||
134 | snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org/rocks.data", home); | ||
135 | mkdir(config_dir, 0777); | ||
136 | #endif | ||
137 | #endif /* HAVE_SPECIAL_DIRS */ | ||
138 | |||
139 | #ifdef HAVE_MULTIDRIVE | ||
140 | rbhome_hash = crc_32((const void *) rbhome, strlen(rbhome), 0xffffffff); | ||
141 | #endif /* HAVE_MULTIDRIVE */ | ||
142 | } | ||
143 | |||
144 | #ifdef HAVE_SPECIAL_DIRS | ||
145 | static bool try_path(const char* filename, unsigned flags) | ||
146 | { | ||
147 | if (flags & IS_FILE) | ||
148 | { | ||
149 | if (file_exists(filename)) | ||
150 | return true; | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | if (dir_exists(filename)) | ||
155 | return true; | ||
156 | } | ||
157 | return false; | ||
158 | } | ||
159 | |||
160 | static const char* _get_user_file_path(const char *path, | ||
161 | unsigned flags, | ||
162 | char* buf, | ||
163 | const size_t bufsize) | ||
164 | { | ||
165 | const char *ret = path; | ||
166 | const char *pos = path; | ||
167 | /* replace ROCKBOX_DIR in path with $HOME/.config/rockbox.org */ | ||
168 | pos += ROCKBOX_DIR_LEN; | ||
169 | if (*pos == '/') pos += 1; | ||
170 | |||
171 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
172 | if (snprintf(buf, bufsize, "/sdcard/rockbox/%s", pos) | ||
173 | #else | ||
174 | if (snprintf(buf, bufsize, "%s/.config/rockbox.org/%s", rbhome, pos) | ||
175 | #endif | ||
176 | >= (int)bufsize) | ||
177 | return NULL; | ||
178 | |||
179 | /* always return the replacement buffer (pointing to $HOME) if | ||
180 | * write access is needed */ | ||
181 | if (flags & NEED_WRITE) | ||
182 | ret = buf; | ||
183 | else if (try_path(buf, flags)) | ||
184 | ret = buf; | ||
185 | |||
186 | if (ret != buf) /* not found in $HOME, try ROCKBOX_BASE_DIR, !NEED_WRITE only */ | ||
187 | { | ||
188 | if (snprintf(buf, bufsize, ROCKBOX_SHARE_PATH "/%s", pos) >= (int)bufsize) | ||
189 | return NULL; | ||
190 | |||
191 | if (try_path(buf, flags)) | ||
192 | ret = buf; | ||
193 | } | ||
194 | |||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | #endif | ||
199 | |||
200 | static const char* handle_special_dirs(const char* dir, unsigned flags, | ||
201 | char *buf, const size_t bufsize) | ||
202 | { | ||
203 | (void) flags; (void) buf; (void) bufsize; | ||
204 | #ifdef HAVE_SPECIAL_DIRS | ||
205 | if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN)) | ||
206 | { | ||
207 | const char *p = dir + HOME_DIR_LEN; | ||
208 | while (*p == '/') p++; | ||
209 | snprintf(buf, bufsize, "%s/%s", rbhome, p); | ||
210 | dir = buf; | ||
211 | } | ||
212 | else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN)) | ||
213 | dir = _get_user_file_path(dir, flags, buf, bufsize); | ||
214 | #endif | ||
215 | #ifdef HAVE_MULTIDRIVE | ||
216 | dir = handle_special_links(dir, flags, buf, bufsize); | ||
217 | #endif | ||
218 | return dir; | ||
219 | } | ||
220 | |||
221 | int app_open(const char *name, int o, ...) | ||
222 | { | ||
223 | char realpath[MAX_PATH]; | ||
224 | va_list ap; | ||
225 | int fd; | ||
226 | int flags = IS_FILE; | ||
227 | if (o & (O_CREAT|O_RDWR|O_TRUNC|O_WRONLY)) | ||
228 | flags |= NEED_WRITE; | ||
229 | |||
230 | name = handle_special_dirs(name, flags, realpath, sizeof(realpath)); | ||
231 | |||
232 | va_start(ap, o); | ||
233 | fd = open(name, o, va_arg(ap, unsigned int)); | ||
234 | va_end(ap); | ||
235 | |||
236 | return fd; | ||
237 | } | ||
238 | |||
239 | int app_creat(const char* name, mode_t mode) | ||
240 | { | ||
241 | return app_open(name, O_CREAT|O_WRONLY|O_TRUNC, mode); | ||
242 | } | ||
243 | |||
244 | int app_remove(const char *name) | ||
245 | { | ||
246 | char realpath[MAX_PATH]; | ||
247 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | ||
248 | |||
249 | return remove(fname); | ||
250 | } | ||
251 | |||
252 | int app_rename(const char *old, const char *new) | ||
253 | { | ||
254 | char realpath_old[MAX_PATH], realpath_new[MAX_PATH]; | ||
255 | const char *final_old, *final_new; | ||
256 | |||
257 | final_old = handle_special_dirs(old, NEED_WRITE, realpath_old, sizeof(realpath_old)); | ||
258 | final_new = handle_special_dirs(new, NEED_WRITE, realpath_new, sizeof(realpath_new)); | ||
259 | |||
260 | return rename(final_old, final_new); | ||
261 | } | ||
262 | |||
263 | /* need to wrap around DIR* because we need to save the parent's | ||
264 | * directory path in order to determine dirinfo, required to implement | ||
265 | * get_dir_info() */ | ||
266 | struct __dir { | ||
267 | DIR *dir; | ||
268 | #ifdef HAVE_MULTIDRIVE | ||
269 | int volumes_returned; | ||
270 | /* A crc of rbhome is used to speed op the common case where | ||
271 | * readdir()/get_dir_info() is called on non-rbhome paths, because | ||
272 | * each call needs to check against rbhome for the virtual | ||
273 | * mount point of the external storage */ | ||
274 | uint32_t path_hash; | ||
275 | #endif | ||
276 | char path[]; | ||
277 | }; | ||
278 | |||
279 | struct dirinfo dir_get_info(DIR* _parent, struct dirent *dir) | ||
280 | { | ||
281 | struct __dir *parent = (struct __dir*)_parent; | ||
282 | struct stat s; | ||
283 | struct tm *tm = NULL; | ||
284 | struct dirinfo ret; | ||
285 | char path[MAX_PATH]; | ||
286 | |||
287 | memset(&ret, 0, sizeof(ret)); | ||
288 | |||
289 | #ifdef HAVE_MULTIDRIVE | ||
290 | char vol_string[VOL_ENUM_POS + 8]; | ||
291 | sprintf(vol_string, VOL_NAMES, 1); | ||
292 | if (UNLIKELY(rbhome_hash == parent->path_hash) && | ||
293 | /* compare path anyway because of possible hash collision */ | ||
294 | !strcmp(vol_string, dir->d_name)) | ||
295 | { | ||
296 | ret.attribute = ATTR_LINK; | ||
297 | strcpy(path, MULTIDRIVE_DIR); | ||
298 | } | ||
299 | else | ||
300 | #endif | ||
301 | snprintf(path, sizeof(path), "%s/%s", parent->path, dir->d_name); | ||
302 | |||
303 | |||
304 | if (!lstat(path, &s)) | ||
305 | { | ||
306 | int err = 0; | ||
307 | if (S_ISLNK(s.st_mode)) | ||
308 | { | ||
309 | ret.attribute |= ATTR_LINK; | ||
310 | err = stat(path, &s); | ||
311 | } | ||
312 | if (!err) | ||
313 | { | ||
314 | if (S_ISDIR(s.st_mode)) | ||
315 | ret.attribute |= ATTR_DIRECTORY; | ||
316 | |||
317 | ret.size = s.st_size; | ||
318 | if ((tm = localtime(&(s.st_mtime)))) | ||
319 | { | ||
320 | ret.wrtdate = ((tm->tm_year - 80) << 9) | | ||
321 | ((tm->tm_mon + 1) << 5) | | ||
322 | tm->tm_mday; | ||
323 | ret.wrttime = (tm->tm_hour << 11) | | ||
324 | (tm->tm_min << 5) | | ||
325 | (tm->tm_sec >> 1); | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | |||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | DIR* app_opendir(const char *_name) | ||
334 | { | ||
335 | size_t name_len; | ||
336 | char realpath[MAX_PATH]; | ||
337 | const char *name = handle_special_dirs(_name, 0, realpath, sizeof(realpath)); | ||
338 | name_len = strlen(name); | ||
339 | char *buf = malloc(sizeof(struct __dir) + name_len+1); | ||
340 | if (!buf) | ||
341 | return NULL; | ||
342 | |||
343 | struct __dir *this = (struct __dir*)buf; | ||
344 | /* carefully remove any trailing slash from the input, so that | ||
345 | * hash/path matching in readdir() works properly */ | ||
346 | while (name[name_len-1] == '/' && name_len > 1) | ||
347 | name_len -= 1; | ||
348 | /* strcpy cannot be used because of trailing slashes */ | ||
349 | memcpy(this->path, name, name_len); | ||
350 | this->path[name_len] = 0; | ||
351 | this->dir = opendir(this->path); | ||
352 | |||
353 | if (!this->dir) | ||
354 | { | ||
355 | free(buf); | ||
356 | return NULL; | ||
357 | } | ||
358 | #ifdef HAVE_MULTIDRIVE | ||
359 | this->volumes_returned = 0; | ||
360 | this->path_hash = crc_32((const void *)this->path, name_len, 0xffffffff); | ||
361 | #endif | ||
362 | |||
363 | return (DIR*)this; | ||
364 | } | ||
365 | |||
366 | int app_closedir(DIR *dir) | ||
367 | { | ||
368 | struct __dir *this = (struct __dir*)dir; | ||
369 | int ret = closedir(this->dir); | ||
370 | free(this); | ||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | |||
375 | struct dirent* app_readdir(DIR* dir) | ||
376 | { | ||
377 | struct __dir *d = (struct __dir*)dir; | ||
378 | #ifdef HAVE_MULTIDRIVE | ||
379 | /* this is not MT-safe but OK according to man readdir */ | ||
380 | static struct dirent voldir; | ||
381 | if (UNLIKELY(rbhome_hash == d->path_hash) | ||
382 | && d->volumes_returned < (NUM_VOLUMES-1) | ||
383 | && volume_present(d->volumes_returned+1) | ||
384 | /* compare path anyway because of possible hash collision */ | ||
385 | && !strcmp(d->path, rbhome)) | ||
386 | { | ||
387 | d->volumes_returned += 1; | ||
388 | sprintf(voldir.d_name, VOL_NAMES, d->volumes_returned); | ||
389 | return &voldir; | ||
390 | } | ||
391 | #endif | ||
392 | return readdir(d->dir); | ||
393 | } | ||
394 | |||
395 | |||
396 | int app_mkdir(const char* name) | ||
397 | { | ||
398 | char realpath[MAX_PATH]; | ||
399 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | ||
400 | return mkdir(fname, 0777); | ||
401 | } | ||
402 | |||
403 | |||
404 | int app_rmdir(const char* name) | ||
405 | { | ||
406 | char realpath[MAX_PATH]; | ||
407 | const char *fname = handle_special_dirs(name, NEED_WRITE, realpath, sizeof(realpath)); | ||
408 | return rmdir(fname); | ||
409 | } | ||
410 | |||
411 | |||
412 | /* On MD we create a virtual symlink for the external drive, | ||
413 | * for this we need to override readlink(). */ | ||
414 | ssize_t app_readlink(const char *path, char *buf, size_t bufsiz) | ||
415 | { | ||
416 | char _buf[MAX_PATH]; | ||
417 | (void) path; (void) buf; (void) bufsiz; | ||
418 | path = handle_special_dirs(path, 0, _buf, sizeof(_buf)); | ||
419 | #ifdef HAVE_MULTIDRIVE | ||
420 | /* if path == _buf then we can be sure handle_special_dir() did something | ||
421 | * and path is not an ordinary directory */ | ||
422 | if (path == _buf && !strncmp(path, MULTIDRIVE_DIR, sizeof(MULTIDRIVE_DIR)-1)) | ||
423 | { | ||
424 | /* copying NUL is not required as per readlink specification */ | ||
425 | ssize_t len = strlen(path); | ||
426 | memcpy(buf, path, len); | ||
427 | return len; | ||
428 | } | ||
429 | #endif | ||
430 | /* does not append NUL !! */ | ||
431 | return readlink(path, buf, bufsiz); | ||
432 | } | ||
diff --git a/firmware/common/unicode.c b/firmware/common/unicode.c index 3ff1814c4b..954ad47e1d 100644 --- a/firmware/common/unicode.c +++ b/firmware/common/unicode.c | |||
@@ -28,161 +28,227 @@ | |||
28 | 28 | ||
29 | #include <stdio.h> | 29 | #include <stdio.h> |
30 | #include "config.h" | 30 | #include "config.h" |
31 | #include "system.h" | ||
32 | #include "thread.h" | ||
31 | #include "file.h" | 33 | #include "file.h" |
32 | #include "debug.h" | 34 | #include "debug.h" |
33 | #include "rbunicode.h" | 35 | #include "rbunicode.h" |
34 | #include "rbpaths.h" | 36 | #include "rbpaths.h" |
37 | #include "pathfuncs.h" | ||
38 | #include "core_alloc.h" | ||
35 | 39 | ||
36 | #ifndef O_BINARY | 40 | #ifndef O_BINARY |
37 | #define O_BINARY 0 | 41 | #define O_BINARY 0 |
38 | #endif | 42 | #endif |
43 | #ifndef O_NOISODECODE | ||
44 | #define O_NOISODECODE 0 | ||
45 | #endif | ||
39 | 46 | ||
40 | static int default_codepage = 0; | 47 | #define getle16(p) (p[0] | (p[1] >> 8)) |
41 | static int loaded_cp_table = 0; | 48 | #define getbe16(p) ((p[1] << 8) | p[0]) |
42 | |||
43 | #ifdef HAVE_LCD_BITMAP | ||
44 | 49 | ||
45 | #define MAX_CP_TABLE_SIZE 32768 | 50 | #if !defined (__PCTOOL__) && (CONFIG_PLATFORM & PLATFORM_NATIVE) |
46 | #define NUM_TABLES 5 | 51 | /* Because file scanning uses the default CP table when matching entries, |
52 | on-demand loading is not feasible; we also must use the filesystem lock */ | ||
53 | #include "file_internal.h" | ||
54 | #else /* APPLICATION */ | ||
55 | #ifdef __PCTOOL__ | ||
56 | #define yield() | ||
57 | #endif | ||
58 | #define open_noiso_internal open | ||
59 | #endif /* !APPLICATION */ | ||
60 | |||
61 | #if 0 /* not needed just now (will probably end up a spinlock) */ | ||
62 | #include "mutex.h" | ||
63 | static struct mutex cp_mutex SHAREDBSS_ATTR; | ||
64 | #define cp_lock_init() mutex_init(&cp_mutex) | ||
65 | #define cp_lock_enter() mutex_lock(&cp_mutex) | ||
66 | #define cp_lock_leave() mutex_unlock(&cp_mutex) | ||
67 | #else | ||
68 | #define cp_lock_init() do {} while (0) | ||
69 | #define cp_lock_enter() asm volatile ("") | ||
70 | #define cp_lock_leave() asm volatile ("") | ||
71 | #endif | ||
47 | 72 | ||
48 | static const char * const filename[NUM_TABLES] = | 73 | enum cp_tid |
49 | { | 74 | { |
50 | CODEPAGE_DIR"/iso.cp", | 75 | CP_TID_NONE = -1, |
51 | CODEPAGE_DIR"/932.cp", /* SJIS */ | 76 | CP_TID_ISO, |
52 | CODEPAGE_DIR"/936.cp", /* GB2312 */ | 77 | CP_TID_932, |
53 | CODEPAGE_DIR"/949.cp", /* KSX1001 */ | 78 | CP_TID_936, |
54 | CODEPAGE_DIR"/950.cp" /* BIG5 */ | 79 | CP_TID_949, |
80 | CP_TID_950, | ||
55 | }; | 81 | }; |
56 | 82 | ||
57 | static const char cp_2_table[NUM_CODEPAGES] = | 83 | struct cp_info |
58 | { | 84 | { |
59 | 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5, 0 | 85 | int8_t tid; |
86 | const char *filename; | ||
87 | const char *name; | ||
60 | }; | 88 | }; |
61 | 89 | ||
62 | static const char * const name_codepages[NUM_CODEPAGES+1] = | 90 | #ifdef HAVE_LCD_BITMAP |
63 | { | ||
64 | "ISO-8859-1", | ||
65 | "ISO-8859-7", | ||
66 | "ISO-8859-8", | ||
67 | "CP1251", | ||
68 | "ISO-8859-11", | ||
69 | "CP1256", | ||
70 | "ISO-8859-9", | ||
71 | "ISO-8859-2", | ||
72 | "CP1250", | ||
73 | "CP1252", | ||
74 | "SJIS", | ||
75 | "GB-2312", | ||
76 | "KSX-1001", | ||
77 | "BIG5", | ||
78 | "UTF-8", | ||
79 | "unknown" | ||
80 | }; | ||
81 | 91 | ||
82 | #if defined(APPLICATION) && defined(__linux__) | 92 | #define MAX_CP_TABLE_SIZE 32768 |
83 | static const char * const name_codepages_linux[NUM_CODEPAGES+1] = | ||
84 | { | ||
85 | /* "ISO-8859-1" */ "iso8859-1", | ||
86 | /* "ISO-8859-7" */ "iso8859-7", | ||
87 | /* "ISO-8859-8" */ "iso8859-8", | ||
88 | /* "CP1251" */ "cp1251", | ||
89 | /* "ISO-8859-11"*/ "iso8859-11", | ||
90 | /* "CP1256" */ "cp1256", | ||
91 | /* "ISO-8859-9" */ "iso8859-9", | ||
92 | /* "ISO-8859-2" */ "iso8859-2", | ||
93 | /* "CP1250" */ "cp1250", | ||
94 | /* "CP1252" */ "iso8859-15", /* closest, linux doesnt have a codepage named cp1252 */ | ||
95 | /* "SJIS" */ "cp932", | ||
96 | /* "GB-2312" */ "cp936", | ||
97 | /* "KSX-1001" */ "cp949", | ||
98 | /* "BIG5" */ "cp950", | ||
99 | /* "UTF-8" */ "utf8", | ||
100 | /* "unknown" */ "cp437" | ||
101 | }; | ||
102 | 93 | ||
103 | const char *get_current_codepage_name_linux(void) | 94 | #define CPF_ISO "iso.cp" |
95 | #define CPF_932 "932.cp" /* SJIS */ | ||
96 | #define CPF_936 "936.cp" /* GB2312 */ | ||
97 | #define CPF_949 "949.cp" /* KSX1001 */ | ||
98 | #define CPF_950 "950.cp" /* BIG5 */ | ||
99 | |||
100 | static const struct cp_info cp_info[NUM_CODEPAGES+1] = | ||
104 | { | 101 | { |
105 | if (default_codepage < 0 || default_codepage >= NUM_CODEPAGES) | 102 | [0 ... NUM_CODEPAGES] = { CP_TID_NONE, NULL , "unknown" }, |
106 | return name_codepages_linux[NUM_CODEPAGES]; | 103 | [ISO_8859_1] = { CP_TID_NONE, NULL , "ISO-8859-1" }, |
107 | return name_codepages_linux[default_codepage]; | 104 | [ISO_8859_7] = { CP_TID_ISO , CPF_ISO, "ISO-8859-7" }, |
108 | } | 105 | [ISO_8859_8] = { CP_TID_ISO , CPF_ISO, "ISO-8859-8" }, |
109 | #endif | 106 | [WIN_1251] = { CP_TID_ISO , CPF_ISO, "CP1251" }, |
107 | [ISO_8859_11] = { CP_TID_ISO , CPF_ISO, "ISO-8859-11" }, | ||
108 | [WIN_1256] = { CP_TID_ISO , CPF_ISO, "CP1256" }, | ||
109 | [ISO_8859_9] = { CP_TID_ISO , CPF_ISO, "ISO-8859-9" }, | ||
110 | [ISO_8859_2] = { CP_TID_ISO , CPF_ISO, "ISO-8859-2" }, | ||
111 | [WIN_1250] = { CP_TID_ISO , CPF_ISO, "CP1250" }, | ||
112 | [WIN_1252] = { CP_TID_ISO , CPF_ISO, "CP1252" }, | ||
113 | [SJIS] = { CP_TID_932 , CPF_932, "SJIS" }, | ||
114 | [GB_2312] = { CP_TID_936 , CPF_936, "GB-2312" }, | ||
115 | [KSX_1001] = { CP_TID_949 , CPF_949, "KSX-1001" }, | ||
116 | [BIG_5] = { CP_TID_950 , CPF_950, "BIG5" }, | ||
117 | [UTF_8] = { CP_TID_NONE, NULL , "UTF-8" }, | ||
118 | }; | ||
110 | 119 | ||
111 | #else /* !HAVE_LCD_BITMAP, reduced support */ | 120 | #else /* !HAVE_LCD_BITMAP, reduced support */ |
112 | 121 | ||
113 | #define MAX_CP_TABLE_SIZE 768 | 122 | #define MAX_CP_TABLE_SIZE 768 |
114 | #define NUM_TABLES 1 | ||
115 | 123 | ||
116 | static const char * const filename[NUM_TABLES] = { | 124 | #define CPF_ISOMINI "isomini.cp" |
117 | CODEPAGE_DIR"/isomini.cp" | ||
118 | }; | ||
119 | 125 | ||
120 | static const char cp_2_table[NUM_CODEPAGES] = | 126 | static const struct cp_info cp_info[NUM_CODEPAGES+1] = |
121 | { | 127 | { |
122 | 0, 1, 1, 1, 1, 1, 1, 0 | 128 | [0 ... NUM_CODEPAGES] = { CP_TID_NONE, NULL , "unknown" }, |
129 | [ISO_8859_1] = { CP_TID_NONE, NULL , "ISO-8859-1" }, | ||
130 | [ISO_8859_7] = { CP_TID_ISO , CPF_ISOMINI, "ISO-8859-7" }, | ||
131 | [WIN_1251] = { CP_TID_ISO , CPF_ISOMINI, "CP1251" }, | ||
132 | [ISO_8859_9] = { CP_TID_ISO , CPF_ISOMINI, "ISO-8859-9" }, | ||
133 | [ISO_8859_2] = { CP_TID_ISO , CPF_ISOMINI, "ISO-8859-2" }, | ||
134 | [WIN_1250] = { CP_TID_ISO , CPF_ISOMINI, "CP1250" }, | ||
135 | [WIN_1252] = { CP_TID_ISO , CPF_ISOMINI, "CP1252" }, | ||
136 | [UTF_8] = { CP_TID_ISO , NULL , "UTF-8" }, | ||
123 | }; | 137 | }; |
124 | 138 | ||
125 | static const char * const name_codepages[NUM_CODEPAGES+1] = | 139 | #endif /* HAVE_LCD_BITMAP */ |
140 | |||
141 | static int default_cp = INIT_CODEPAGE; | ||
142 | static int default_cp_tid = CP_TID_NONE; | ||
143 | static int default_cp_handle = 0; | ||
144 | static int volatile default_cp_table_ref = 0; | ||
145 | |||
146 | static int loaded_cp_tid = CP_TID_NONE; | ||
147 | static int volatile cp_table_ref = 0; | ||
148 | #define CP_LOADING BIT_N(sizeof(int)*8-1) /* guard against multi loaders */ | ||
149 | |||
150 | /* non-default codepage table buffer (cannot be bufalloced! playback itself | ||
151 | may be making the load request) */ | ||
152 | static unsigned short codepage_table[MAX_CP_TABLE_SIZE+1]; | ||
153 | |||
154 | #if defined(APPLICATION) && defined(__linux__) | ||
155 | static const char * const name_codepages_linux[NUM_CODEPAGES+1] = | ||
126 | { | 156 | { |
127 | "ISO-8859-1", | 157 | [0 ... NUM_CODEPAGES] = "unknown", |
128 | "ISO-8859-7", | 158 | [ISO_8859_1] = "iso8859-1", |
129 | "CP1251", | 159 | [ISO_8859_7] = "iso8859-7", |
130 | "ISO-8859-9", | 160 | [ISO_8859_8] = "iso8859-8", |
131 | "ISO-8859-2", | 161 | [WIN_1251] = "cp1251", |
132 | "CP1250", | 162 | [ISO_8859_11] = "iso8859-11", |
133 | "CP1252", | 163 | [WIN_1256] = "cp1256", |
134 | "UTF-8", | 164 | [ISO_8859_9] = "iso8859-9", |
135 | "unknown" | 165 | [ISO_8859_2] = "iso8859-2", |
166 | [WIN_1250] = "cp1250", | ||
167 | /* iso8859-15 is closest, linux doesnt have a codepage named cp1252 */ | ||
168 | [WIN_1252] = "iso8859-15", | ||
169 | [SJIS] = "cp932", | ||
170 | [GB_2312] = "cp936", | ||
171 | [KSX_1001] = "cp949", | ||
172 | [BIG_5] = "cp950", | ||
173 | [UTF_8] = "utf8", | ||
136 | }; | 174 | }; |
137 | 175 | ||
138 | #endif | 176 | const char *get_current_codepage_name_linux(void) |
139 | 177 | { | |
140 | static unsigned short codepage_table[MAX_CP_TABLE_SIZE]; | 178 | int cp = default_cp; |
179 | if (cp < 0 || cp>= NUM_CODEPAGES) | ||
180 | cp = NUM_CODEPAGES; | ||
181 | return name_codepages_linux[cp]; | ||
182 | } | ||
183 | #endif /* defined(APPLICATION) && defined(__linux__) */ | ||
141 | 184 | ||
142 | static const unsigned char utf8comp[6] = | 185 | static const unsigned char utf8comp[6] = |
143 | { | 186 | { |
144 | 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC | 187 | 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC |
145 | }; | 188 | }; |
146 | 189 | ||
147 | /* Load codepage file into memory */ | 190 | static inline void cptable_tohw16(uint16_t *buf, unsigned int count) |
148 | static int load_cp_table(int cp) | ||
149 | { | 191 | { |
150 | int i = 0; | 192 | #ifdef ROCKBOX_BIG_ENDIAN |
151 | int table = cp_2_table[cp]; | 193 | for (unsigned int i = 0; i < count; i++) |
152 | int file, tablesize; | 194 | buf[i] = letoh16(buf[i]); |
153 | unsigned char tmp[2]; | 195 | #endif |
196 | (void)buf; (void)count; | ||
197 | } | ||
154 | 198 | ||
155 | if (table == 0 || table == loaded_cp_table) | 199 | static int move_callback(int handle, void *current, void *new) |
156 | return 1; | 200 | { |
201 | /* we don't keep a pointer but we have to stop it if this applies to a | ||
202 | buffer not yet swapped-in since it will likely be in use in an I/O | ||
203 | call */ | ||
204 | return (handle != default_cp_handle || default_cp_table_ref != 0) ? | ||
205 | BUFLIB_CB_CANNOT_MOVE : BUFLIB_CB_OK; | ||
206 | (void)current; (void)new; | ||
207 | } | ||
157 | 208 | ||
158 | file = open(filename[table-1], O_RDONLY|O_BINARY); | 209 | static int alloc_and_load_cp_table(int cp, void *buf) |
210 | { | ||
211 | static struct buflib_callbacks ops = | ||
212 | { .move_callback = move_callback }; | ||
159 | 213 | ||
160 | if (file < 0) { | 214 | /* alloc and read only if there is an associated file */ |
161 | DEBUGF("Can't open codepage file: %s.cp\n", filename[table-1]); | 215 | const char *filename = cp_info[cp].filename; |
216 | if (!filename) | ||
162 | return 0; | 217 | return 0; |
218 | |||
219 | char path[MAX_PATH]; | ||
220 | if (path_append(path, CODEPAGE_DIR, filename, sizeof (path)) | ||
221 | >= sizeof (path)) { | ||
222 | return -1; | ||
163 | } | 223 | } |
164 | 224 | ||
165 | tablesize = filesize(file) / 2; | 225 | /* must be opened without a chance of reentering from FS code */ |
226 | int fd = open_noiso_internal(path, O_RDONLY); | ||
227 | if (fd < 0) | ||
228 | return -1; | ||
166 | 229 | ||
167 | if (tablesize > MAX_CP_TABLE_SIZE) { | 230 | off_t size = filesize(fd); |
168 | DEBUGF("Invalid codepage file: %s.cp\n", filename[table-1]); | ||
169 | close(file); | ||
170 | return 0; | ||
171 | } | ||
172 | 231 | ||
173 | while (i < tablesize) { | 232 | if (size > 0 && size <= MAX_CP_TABLE_SIZE*2 && |
174 | if (!read(file, tmp, 2)) { | 233 | !(size % (off_t)sizeof (uint16_t))) { |
175 | DEBUGF("Can't read from codepage file: %s.cp\n", | 234 | |
176 | filename[table-1]); | 235 | /* if the buffer is provided, use that but don't alloc */ |
177 | loaded_cp_table = 0; | 236 | int handle = buf ? 0 : core_alloc_ex(filename, size, &ops); |
178 | return 0; | 237 | if (handle > 0) |
238 | buf = core_get_data(handle); | ||
239 | |||
240 | if (buf && read(fd, buf, size) == size) { | ||
241 | close(fd); | ||
242 | cptable_tohw16(buf, size / sizeof (uint16_t)); | ||
243 | return handle; | ||
179 | } | 244 | } |
180 | codepage_table[i++] = (tmp[1] << 8) | tmp[0]; | 245 | |
246 | if (handle > 0) | ||
247 | core_free(handle); | ||
181 | } | 248 | } |
182 | 249 | ||
183 | loaded_cp_table = table; | 250 | close(fd); |
184 | close(file); | 251 | return -1; |
185 | return 1; | ||
186 | } | 252 | } |
187 | 253 | ||
188 | /* Encode a UCS value as UTF-8 and return a pointer after this UTF-8 char. */ | 254 | /* 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) | |||
205 | unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, | 271 | unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, |
206 | int cp, int count) | 272 | int cp, int count) |
207 | { | 273 | { |
208 | unsigned short ucs, tmp; | 274 | uint16_t *table = NULL; |
275 | |||
276 | cp_lock_enter(); | ||
277 | |||
278 | if (cp < 0 || cp >= NUM_CODEPAGES) | ||
279 | cp = default_cp; | ||
209 | 280 | ||
210 | if (cp == -1) /* use default codepage */ | 281 | int tid = cp_info[cp].tid; |
211 | cp = default_codepage; | ||
212 | 282 | ||
213 | if (!load_cp_table(cp)) cp = 0; | 283 | while (1) { |
284 | if (tid == default_cp_tid) { | ||
285 | /* use default table */ | ||
286 | if (default_cp_handle > 0) { | ||
287 | table = core_get_data(default_cp_handle); | ||
288 | default_cp_table_ref++; | ||
289 | } | ||
290 | |||
291 | break; | ||
292 | } | ||
293 | |||
294 | bool load = false; | ||
295 | |||
296 | if (tid == loaded_cp_tid) { | ||
297 | /* use loaded table */ | ||
298 | if (!(cp_table_ref & CP_LOADING)) { | ||
299 | if (tid != CP_TID_NONE) { | ||
300 | table = codepage_table; | ||
301 | cp_table_ref++; | ||
302 | } | ||
303 | |||
304 | break; | ||
305 | } | ||
306 | } else if (cp_table_ref == 0) { | ||
307 | load = true; | ||
308 | cp_table_ref |= CP_LOADING; | ||
309 | } | ||
310 | |||
311 | /* alloc and load must be done outside the lock */ | ||
312 | cp_lock_leave(); | ||
313 | |||
314 | if (!load) { | ||
315 | yield(); | ||
316 | } else if (alloc_and_load_cp_table(cp, codepage_table) < 0) { | ||
317 | cp = INIT_CODEPAGE; /* table may be clobbered now */ | ||
318 | tid = cp_info[cp].tid; | ||
319 | } | ||
320 | |||
321 | cp_lock_enter(); | ||
322 | |||
323 | if (load) { | ||
324 | loaded_cp_tid = tid; | ||
325 | cp_table_ref &= ~CP_LOADING; | ||
326 | } | ||
327 | } | ||
328 | |||
329 | cp_lock_leave(); | ||
214 | 330 | ||
215 | while (count--) { | 331 | while (count--) { |
332 | unsigned short ucs, tmp; | ||
333 | |||
216 | if (*iso < 128 || cp == UTF_8) /* Already UTF-8 */ | 334 | if (*iso < 128 || cp == UTF_8) /* Already UTF-8 */ |
217 | *utf8++ = *iso++; | 335 | *utf8++ = *iso++; |
218 | 336 | ||
219 | else { | 337 | else { |
220 | 338 | /* tid tells us which table to use and how */ | |
221 | /* cp tells us which codepage to convert from */ | 339 | switch (tid) { |
222 | switch (cp) { | 340 | case CP_TID_ISO: /* Greek */ |
223 | case ISO_8859_7: /* Greek */ | 341 | /* Hebrew */ |
224 | case WIN_1252: /* Western European */ | 342 | /* Cyrillic */ |
225 | case WIN_1251: /* Cyrillic */ | 343 | /* Thai */ |
226 | case ISO_8859_9: /* Turkish */ | 344 | /* Arabic */ |
227 | case ISO_8859_2: /* Latin Extended */ | 345 | /* Turkish */ |
228 | case WIN_1250: /* Central European */ | 346 | /* Latin Extended */ |
229 | #ifdef HAVE_LCD_BITMAP | 347 | /* Central European */ |
230 | case ISO_8859_8: /* Hebrew */ | 348 | /* Western European */ |
231 | case ISO_8859_11: /* Thai */ | ||
232 | case WIN_1256: /* Arabic */ | ||
233 | #endif | ||
234 | tmp = ((cp-1)*128) + (*iso++ - 128); | 349 | tmp = ((cp-1)*128) + (*iso++ - 128); |
235 | ucs = codepage_table[tmp]; | 350 | ucs = table[tmp]; |
236 | break; | 351 | break; |
237 | 352 | ||
238 | #ifdef HAVE_LCD_BITMAP | 353 | #ifdef HAVE_LCD_BITMAP |
239 | case SJIS: /* Japanese */ | 354 | case CP_TID_932: /* Japanese */ |
240 | if (*iso > 0xA0 && *iso < 0xE0) { | 355 | if (*iso > 0xA0 && *iso < 0xE0) { |
241 | tmp = *iso++ | (0xA100 - 0x8000); | 356 | tmp = *iso++ | (0xA100 - 0x8000); |
242 | ucs = codepage_table[tmp]; | 357 | ucs = table[tmp]; |
243 | break; | 358 | break; |
244 | } | 359 | } |
245 | 360 | ||
246 | case GB_2312: /* Simplified Chinese */ | 361 | case CP_TID_936: /* Simplified Chinese */ |
247 | case KSX_1001: /* Korean */ | 362 | case CP_TID_949: /* Korean */ |
248 | case BIG_5: /* Traditional Chinese */ | 363 | case CP_TID_950: /* Traditional Chinese */ |
249 | if (count < 1 || !iso[1]) { | 364 | if (count < 1 || !iso[1]) { |
250 | ucs = *iso++; | 365 | ucs = *iso++; |
251 | break; | 366 | break; |
@@ -256,7 +371,7 @@ unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, | |||
256 | tmp = *iso++ << 8; | 371 | tmp = *iso++ << 8; |
257 | tmp |= *iso++; | 372 | tmp |= *iso++; |
258 | tmp -= 0x8000; | 373 | tmp -= 0x8000; |
259 | ucs = codepage_table[tmp]; | 374 | ucs = table[tmp]; |
260 | count--; | 375 | count--; |
261 | break; | 376 | break; |
262 | #endif /* HAVE_LCD_BITMAP */ | 377 | #endif /* HAVE_LCD_BITMAP */ |
@@ -271,6 +386,17 @@ unsigned char* iso_decode(const unsigned char *iso, unsigned char *utf8, | |||
271 | utf8 = utf8encode(ucs, utf8); | 386 | utf8 = utf8encode(ucs, utf8); |
272 | } | 387 | } |
273 | } | 388 | } |
389 | |||
390 | if (table) { | ||
391 | cp_lock_enter(); | ||
392 | if (table == codepage_table) { | ||
393 | cp_table_ref--; | ||
394 | } else { | ||
395 | default_cp_table_ref--; | ||
396 | } | ||
397 | cp_lock_leave(); | ||
398 | } | ||
399 | |||
274 | return utf8; | 400 | return utf8; |
275 | } | 401 | } |
276 | 402 | ||
@@ -288,7 +414,7 @@ unsigned char* utf16LEdecode(const unsigned char *utf16, unsigned char *utf8, | |||
288 | utf16 += 4; | 414 | utf16 += 4; |
289 | count -= 2; | 415 | count -= 2; |
290 | } else { | 416 | } else { |
291 | ucs = (utf16[0] | (utf16[1] << 8)); | 417 | ucs = getle16(utf16); |
292 | utf16 += 2; | 418 | utf16 += 2; |
293 | count -= 1; | 419 | count -= 1; |
294 | } | 420 | } |
@@ -310,7 +436,7 @@ unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8, | |||
310 | utf16 += 4; | 436 | utf16 += 4; |
311 | count -= 2; | 437 | count -= 2; |
312 | } else { | 438 | } else { |
313 | ucs = (utf16[0] << 8) | utf16[1]; | 439 | ucs = getbe16(utf16); |
314 | utf16 += 2; | 440 | utf16 += 2; |
315 | count -= 1; | 441 | count -= 1; |
316 | } | 442 | } |
@@ -400,8 +526,50 @@ const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs) | |||
400 | 526 | ||
401 | void set_codepage(int cp) | 527 | void set_codepage(int cp) |
402 | { | 528 | { |
403 | default_codepage = cp; | 529 | if (cp < 0 || cp >= NUM_CODEPAGES) |
404 | return; | 530 | cp = NUM_CODEPAGES; |
531 | |||
532 | /* load first then swap if load is successful, else just leave it; if | ||
533 | handle is 0 then we just free the current one; this won't happen often | ||
534 | thus we don't worry about reusing it and consequently avoid possible | ||
535 | clobbering of the existing one */ | ||
536 | |||
537 | int handle = -1; | ||
538 | int tid = cp_info[cp].tid; | ||
539 | |||
540 | while (1) { | ||
541 | cp_lock_enter(); | ||
542 | |||
543 | if (default_cp_tid == tid) | ||
544 | break; | ||
545 | |||
546 | if (handle >= 0 && default_cp_table_ref == 0) { | ||
547 | int hold = default_cp_handle; | ||
548 | default_cp_handle = handle; | ||
549 | handle = hold; | ||
550 | default_cp_tid = tid; | ||
551 | break; | ||
552 | } | ||
553 | |||
554 | /* alloc and load must be done outside the lock */ | ||
555 | cp_lock_leave(); | ||
556 | |||
557 | if (handle < 0 && (handle = alloc_and_load_cp_table(cp, NULL)) < 0) | ||
558 | return; /* OOM; change nothing */ | ||
559 | |||
560 | yield(); | ||
561 | } | ||
562 | |||
563 | default_cp = cp; | ||
564 | cp_lock_leave(); | ||
565 | |||
566 | if (handle > 0) | ||
567 | core_free(handle); | ||
568 | } | ||
569 | |||
570 | int get_codepage(void) | ||
571 | { | ||
572 | return default_cp; | ||
405 | } | 573 | } |
406 | 574 | ||
407 | /* seek to a given char in a utf8 string and | 575 | /* seek to a given char in a utf8 string and |
@@ -418,9 +586,16 @@ int utf8seek(const unsigned char* utf8, int offset) | |||
418 | return pos; | 586 | return pos; |
419 | } | 587 | } |
420 | 588 | ||
421 | const char* get_codepage_name(int cp) | 589 | const char * get_codepage_name(int cp) |
422 | { | 590 | { |
423 | if (cp < 0 || cp>= NUM_CODEPAGES) | 591 | if (cp < 0 || cp >= NUM_CODEPAGES) |
424 | return name_codepages[NUM_CODEPAGES]; | 592 | cp = NUM_CODEPAGES; |
425 | return name_codepages[cp]; | 593 | return cp_info[cp].name; |
426 | } | 594 | } |
595 | |||
596 | #if 0 /* not needed just now */ | ||
597 | void unicode_init(void) | ||
598 | { | ||
599 | cp_lock_init(); | ||
600 | } | ||
601 | #endif | ||
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index fb75355898..44e5ab2f4c 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2002 by Linus Nielsen Feltzing | 10 | * Copyright (C) 2002 by Linus Nielsen Feltzing |
11 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -18,27 +19,44 @@ | |||
18 | * KIND, either express or implied. | 19 | * KIND, either express or implied. |
19 | * | 20 | * |
20 | ****************************************************************************/ | 21 | ****************************************************************************/ |
21 | #include <stdio.h> | 22 | #include "config.h" |
23 | #include "system.h" | ||
24 | #include "sys/types.h" | ||
22 | #include <string.h> | 25 | #include <string.h> |
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | 26 | #include <ctype.h> |
25 | #include <stdbool.h> | 27 | #include <stdlib.h> |
26 | #include "fat.h" | 28 | #include <stdio.h> |
29 | #include "fs_attr.h" | ||
30 | #include "pathfuncs.h" | ||
31 | #include "disk_cache.h" | ||
32 | #include "file_internal.h" /* for struct filestr_cache */ | ||
27 | #include "storage.h" | 33 | #include "storage.h" |
28 | #include "debug.h" | ||
29 | #include "panic.h" | ||
30 | #include "system.h" | ||
31 | #include "timefuncs.h" | 34 | #include "timefuncs.h" |
32 | #include "kernel.h" | ||
33 | #include "rbunicode.h" | 35 | #include "rbunicode.h" |
36 | #include "debug.h" | ||
37 | #include "panic.h" | ||
34 | /*#define LOGF_ENABLE*/ | 38 | /*#define LOGF_ENABLE*/ |
35 | #include "logf.h" | 39 | #include "logf.h" |
36 | 40 | ||
37 | #define BYTES2INT16(array,pos) \ | 41 | #define BYTES2INT32(array, pos) \ |
38 | (array[pos] | (array[pos+1] << 8 )) | 42 | (((uint32_t)array[pos+0] << 0) | \ |
39 | #define BYTES2INT32(array,pos) \ | 43 | ((uint32_t)array[pos+1] << 8) | \ |
40 | ((long)array[pos] | ((long)array[pos+1] << 8 ) | \ | 44 | ((uint32_t)array[pos+2] << 16) | \ |
41 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | 45 | ((uint32_t)array[pos+3] << 24)) |
46 | |||
47 | #define INT322BYTES(array, pos, val) \ | ||
48 | ((array[pos+0] = (uint32_t)(val) >> 0), \ | ||
49 | (array[pos+1] = (uint32_t)(val) >> 8), \ | ||
50 | (array[pos+2] = (uint32_t)(val) >> 16), \ | ||
51 | (array[pos+3] = (uint32_t)(val) >> 24)) | ||
52 | |||
53 | #define BYTES2INT16(array, pos) \ | ||
54 | (((uint32_t)array[pos+0] << 0) | \ | ||
55 | ((uint32_t)array[pos+1] << 8)) | ||
56 | |||
57 | #define INT162BYTES(array, pos, val) \ | ||
58 | ((array[pos+0] = (uint16_t)(val) >> 0), \ | ||
59 | (array[pos+1] = (uint16_t)(val) >> 8)) | ||
42 | 60 | ||
43 | #define FATTYPE_FAT12 0 | 61 | #define FATTYPE_FAT12 0 |
44 | #define FATTYPE_FAT16 1 | 62 | #define FATTYPE_FAT16 1 |
@@ -83,82 +101,129 @@ | |||
83 | 101 | ||
84 | #define BPB_LAST_WORD 510 | 102 | #define BPB_LAST_WORD 510 |
85 | 103 | ||
104 | /* Short and long name directory entry template */ | ||
105 | union raw_dirent | ||
106 | { | ||
107 | struct /* short entry */ | ||
108 | { | ||
109 | uint8_t name[8+3]; /* 0 */ | ||
110 | uint8_t attr; /* 11 */ | ||
111 | uint8_t ntres; /* 12 */ | ||
112 | uint8_t crttimetenth; /* 13 */ | ||
113 | uint16_t crttime; /* 14 */ | ||
114 | uint16_t crtdate; /* 16 */ | ||
115 | uint16_t lstaccdate; /* 18 */ | ||
116 | uint16_t fstclushi; /* 20 */ | ||
117 | uint16_t wrttime; /* 22 */ | ||
118 | uint16_t wrtdate; /* 24 */ | ||
119 | uint16_t fstcluslo; /* 26 */ | ||
120 | uint32_t filesize; /* 28 */ | ||
121 | /* 32 */ | ||
122 | }; | ||
123 | struct /* long entry */ | ||
124 | { | ||
125 | uint8_t ldir_ord; /* 0 */ | ||
126 | uint8_t ldir_name1[10]; /* 1 */ | ||
127 | uint8_t ldir_attr; /* 11 */ | ||
128 | uint8_t ldir_type; /* 12 */ | ||
129 | uint8_t ldir_chksum; /* 13 */ | ||
130 | uint8_t ldir_name2[12]; /* 14 */ | ||
131 | uint16_t ldir_fstcluslo; /* 26 */ | ||
132 | uint8_t ldir_name3[4]; /* 28 */ | ||
133 | /* 32 */ | ||
134 | }; | ||
135 | struct /* raw byte array */ | ||
136 | { | ||
137 | uint8_t data[32]; /* 0 */ | ||
138 | /* 32 */ | ||
139 | }; | ||
140 | }; | ||
141 | |||
142 | |||
143 | /* at most 20 LFN entries */ | ||
144 | #define FATLONG_MAX_ORDER 20 | ||
145 | #define FATLONG_NAME_CHARS 13 | ||
146 | #define FATLONG_ORD_F_LAST 0x40 | ||
86 | 147 | ||
87 | /* attributes */ | 148 | /* attributes */ |
88 | #define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 149 | #define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | \ |
89 | FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID) | 150 | ATTR_SYSTEM | ATTR_VOLUME_ID) |
90 | #define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ | 151 | #define ATTR_LONG_NAME_MASK (ATTR_READ_ONLY | ATTR_HIDDEN | \ |
91 | FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \ | 152 | ATTR_SYSTEM | ATTR_VOLUME_ID | \ |
92 | FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE ) | 153 | ATTR_DIRECTORY | ATTR_ARCHIVE ) |
154 | |||
155 | #define IS_LDIR_ATTR(attr) \ | ||
156 | (((attr) & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) | ||
157 | |||
158 | #define IS_VOL_ID_ATTR(attr) \ | ||
159 | (((attr) & (ATTR_VOLUME_ID | ATTR_DIRECTORY)) == ATTR_VOLUME_ID) | ||
93 | 160 | ||
94 | /* NTRES flags */ | 161 | /* NTRES flags */ |
95 | #define FAT_NTRES_LC_NAME 0x08 | 162 | #define FAT_NTRES_LC_NAME 0x08 |
96 | #define FAT_NTRES_LC_EXT 0x10 | 163 | #define FAT_NTRES_LC_EXT 0x10 |
97 | 164 | ||
98 | #define FATDIR_NAME 0 | 165 | #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) |
99 | #define FATDIR_ATTR 11 | 166 | #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) |
100 | #define FATDIR_NTRES 12 | 167 | #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE) |
101 | #define FATDIR_CRTTIMETENTH 13 | 168 | #define DIR_ENTRY_SIZE 32 |
102 | #define FATDIR_CRTTIME 14 | 169 | #define FAT_BAD_MARK 0x0ffffff7 |
103 | #define FATDIR_CRTDATE 16 | 170 | #define FAT_EOF_MARK 0x0ffffff8 |
104 | #define FATDIR_LSTACCDATE 18 | 171 | #define FAT16_BAD_MARK 0xfff7 |
105 | #define FATDIR_FSTCLUSHI 20 | 172 | #define FAT16_EOF_MARK 0xfff8 |
106 | #define FATDIR_WRTTIME 22 | 173 | |
107 | #define FATDIR_WRTDATE 24 | 174 | struct fsinfo |
108 | #define FATDIR_FSTCLUSLO 26 | 175 | { |
109 | #define FATDIR_FILESIZE 28 | 176 | unsigned long freecount; /* last known free cluster count */ |
110 | 177 | unsigned long nextfree; /* first cluster to start looking for free | |
111 | #define FATLONG_ORDER 0 | 178 | clusters, or 0xffffffff for no hint */ |
112 | #define FATLONG_TYPE 12 | ||
113 | #define FATLONG_CHKSUM 13 | ||
114 | #define FATLONG_LAST_LONG_ENTRY 0x40 | ||
115 | #define FATLONG_NAME_BYTES_PER_ENTRY 26 | ||
116 | /* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */ | ||
117 | #define FATLONG_MAX_ORDER 20 | ||
118 | |||
119 | #define FATLONG_NAME_CHUNKS 3 | ||
120 | static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28}; | ||
121 | static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4}; | ||
122 | |||
123 | #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) | ||
124 | #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) | ||
125 | #define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE) | ||
126 | #define DIR_ENTRY_SIZE 32 | ||
127 | #define NAME_BYTES_PER_ENTRY 13 | ||
128 | #define FAT_BAD_MARK 0x0ffffff7 | ||
129 | #define FAT_EOF_MARK 0x0ffffff8 | ||
130 | #define FAT_LONGNAME_PAD_BYTE 0xff | ||
131 | #define FAT_LONGNAME_PAD_UCS 0xffff | ||
132 | |||
133 | struct fsinfo { | ||
134 | uint32_t freecount; /* last known free cluster count */ | ||
135 | uint32_t nextfree; /* first cluster to start looking for free | ||
136 | clusters, or 0xffffffff for no hint */ | ||
137 | }; | 179 | }; |
138 | /* fsinfo offsets */ | 180 | /* fsinfo offsets */ |
139 | #define FSINFO_FREECOUNT 488 | 181 | #define FSINFO_FREECOUNT 488 |
140 | #define FSINFO_NEXTFREE 492 | 182 | #define FSINFO_NEXTFREE 492 |
141 | 183 | ||
184 | #ifdef HAVE_FAT16SUPPORT | ||
185 | #define BPB_FN_SET16(bpb, fn) (bpb)->fn##__ = fn##16 | ||
186 | #define BPB_FN_SET32(bpb, fn) (bpb)->fn##__ = fn##32 | ||
187 | #define BPB_FN_DECL(fn, args...) (*fn##__)(struct bpb *bpb , ##args) | ||
188 | #define BPB_CALL(fn, bpb, args...) ((bpb)->fn##__(bpb , ##args)) | ||
189 | |||
190 | #define get_next_cluster(bpb, cluster) \ | ||
191 | BPB_CALL(get_next_cluster, (bpb), (cluster)) | ||
192 | #define find_free_cluster(bpb, startcluster) \ | ||
193 | BPB_CALL(find_free_cluster, (bpb), (startcluster)) | ||
194 | #define update_fat_entry(bpb, entry, value) \ | ||
195 | BPB_CALL(update_fat_entry, (bpb), (entry), (value)) | ||
196 | #define fat_recalc_free_internal(bpb) \ | ||
197 | BPB_CALL(fat_recalc_free_internal, (bpb)) | ||
198 | #else /* !HAVE_FAT16SUPPORT */ | ||
199 | #define get_next_cluster get_next_cluster32 | ||
200 | #define find_free_cluster find_free_cluster32 | ||
201 | #define update_fat_entry update_fat_entry32 | ||
202 | #define fat_recalc_free_internal fat_recalc_free_internal32 | ||
203 | #endif /* HAVE_FAT16SUPPORT */ | ||
204 | struct bpb; | ||
205 | static void update_fsinfo32(struct bpb *fat_bpb); | ||
206 | |||
142 | /* Note: This struct doesn't hold the raw values after mounting if | 207 | /* Note: This struct doesn't hold the raw values after mounting if |
143 | * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte | 208 | * bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte |
144 | * physical sectors. */ | 209 | * physical sectors. */ |
145 | struct bpb | 210 | static struct bpb |
146 | { | 211 | { |
147 | int bpb_bytspersec; /* Bytes per sector, typically 512 */ | 212 | unsigned long bpb_bytspersec; /* Bytes per sector, typically 512 */ |
148 | unsigned int bpb_secperclus; /* Sectors per cluster */ | 213 | unsigned long bpb_secperclus; /* Sectors per cluster */ |
149 | int bpb_rsvdseccnt; /* Number of reserved sectors */ | 214 | unsigned long bpb_rsvdseccnt; /* Number of reserved sectors */ |
150 | int bpb_numfats; /* Number of FAT structures, typically 2 */ | 215 | unsigned long bpb_totsec16; /* Number of sectors on volume (old 16-bit) */ |
151 | int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */ | 216 | uint8_t bpb_numfats; /* Number of FAT structures, typically 2 */ |
152 | int bpb_media; /* Media type (typically 0xf0 or 0xf8) */ | 217 | uint8_t bpb_media; /* Media type (typically 0xf0 or 0xf8) */ |
153 | int bpb_fatsz16; /* Number of used sectors per FAT structure */ | 218 | uint16_t bpb_fatsz16; /* Number of used sectors per FAT structure */ |
154 | unsigned long bpb_totsec32; /* Number of sectors on the volume | 219 | unsigned long bpb_totsec32; /* Number of sectors on the volume |
155 | (new 32-bit) */ | 220 | (new 32-bit) */ |
156 | unsigned int last_word; /* 0xAA55 */ | 221 | uint16_t last_word; /* 0xAA55 */ |
222 | long bpb_rootclus; | ||
157 | 223 | ||
158 | /**** FAT32 specific *****/ | 224 | /**** FAT32 specific *****/ |
159 | long bpb_fatsz32; | 225 | unsigned long bpb_fatsz32; |
160 | long bpb_rootclus; | 226 | unsigned long bpb_fsinfo; |
161 | long bpb_fsinfo; | ||
162 | 227 | ||
163 | /* variables for internal use */ | 228 | /* variables for internal use */ |
164 | unsigned long fatsize; | 229 | unsigned long fatsize; |
@@ -167,936 +232,1246 @@ struct bpb | |||
167 | unsigned long firstdatasector; | 232 | unsigned long firstdatasector; |
168 | unsigned long startsector; | 233 | unsigned long startsector; |
169 | unsigned long dataclusters; | 234 | unsigned long dataclusters; |
235 | unsigned long fatrgnstart; | ||
236 | unsigned long fatrgnend; | ||
170 | struct fsinfo fsinfo; | 237 | struct fsinfo fsinfo; |
171 | #ifdef HAVE_FAT16SUPPORT | 238 | #ifdef HAVE_FAT16SUPPORT |
172 | int bpb_rootentcnt; /* Number of dir entries in the root */ | 239 | unsigned int bpb_rootentcnt; /* Number of dir entries in the root */ |
173 | /* internals for FAT16 support */ | 240 | /* internals for FAT16 support */ |
174 | bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ | 241 | unsigned long rootdirsectornum; /* sector offset of root dir relative to start |
175 | unsigned int rootdiroffset; /* sector offset of root dir relative to start | 242 | * of first pseudo cluster */ |
176 | * of first pseudo cluster */ | 243 | #endif /* HAVE_FAT16SUPPORT */ |
177 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 244 | |
178 | #ifdef HAVE_MULTIVOLUME | 245 | /** Additional information kept for each volume **/ |
179 | #ifdef HAVE_MULTIDRIVE | 246 | #ifdef HAVE_FAT16SUPPORT |
180 | int drive; /* on which physical device is this located */ | 247 | uint8_t is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */ |
181 | #endif | 248 | #endif |
182 | bool mounted; /* flag if this volume is mounted */ | 249 | #ifdef HAVE_MULTIDRIVE |
250 | uint8_t drive; /* on which physical device is this located */ | ||
183 | #endif | 251 | #endif |
184 | }; | ||
185 | |||
186 | static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ | ||
187 | static bool initialized = false; | ||
188 | |||
189 | static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)); | ||
190 | static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)); | ||
191 | static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)); | ||
192 | static void *cache_fat_sector(IF_MV(struct bpb* fat_bpb,) | ||
193 | long secnum, bool dirty); | ||
194 | static void create_dos_name(const unsigned char *name, unsigned char *newname); | ||
195 | static void randomize_dos_name(unsigned char *name); | ||
196 | static unsigned long find_free_cluster(IF_MV(struct bpb* fat_bpb,) | ||
197 | unsigned long start); | ||
198 | static int transfer(IF_MV(struct bpb* fat_bpb,) unsigned long start, | ||
199 | long count, char* buf, bool write ); | ||
200 | |||
201 | #define FAT_CACHE_SIZE 0x20 | ||
202 | #define FAT_CACHE_MASK (FAT_CACHE_SIZE-1) | ||
203 | |||
204 | struct fat_cache_entry | ||
205 | { | ||
206 | long secnum; | ||
207 | bool inuse; | ||
208 | bool dirty; | ||
209 | #ifdef HAVE_MULTIVOLUME | 252 | #ifdef HAVE_MULTIVOLUME |
210 | struct bpb* fat_vol ; /* shared cache for all volumes */ | 253 | uint8_t volume; /* on which volume is this located (shortcut) */ |
211 | #endif | 254 | #endif |
255 | uint8_t mounted; /* true if volume is mounted, false otherwise */ | ||
256 | #ifdef HAVE_FAT16SUPPORT | ||
257 | /* some functions are different for different FAT types */ | ||
258 | long BPB_FN_DECL(get_next_cluster, long); | ||
259 | long BPB_FN_DECL(find_free_cluster, long); | ||
260 | int BPB_FN_DECL(update_fat_entry, unsigned long, unsigned long); | ||
261 | void BPB_FN_DECL(fat_recalc_free_internal); | ||
262 | #endif /* HAVE_FAT16SUPPORT */ | ||
263 | |||
264 | } fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ | ||
265 | |||
266 | #define IS_FAT_SECTOR(bpb, sector) \ | ||
267 | (!((sector) >= (bpb)->fatrgnend || (sector) < (bpb)->fatrgnstart)) | ||
268 | |||
269 | /* set error code and jump to routine exit */ | ||
270 | #define FAT_ERROR(_rc) \ | ||
271 | ({ __builtin_constant_p(_rc) ? \ | ||
272 | ({ if (_rc != RC) rc = (_rc); }) : \ | ||
273 | ({ rc = (_rc); }); \ | ||
274 | goto fat_error; }) | ||
275 | |||
276 | #define FAT_BPB(volume) \ | ||
277 | ({ struct bpb * _bpb = &fat_bpbs[IF_MV_VOL(volume)]; \ | ||
278 | if (!_bpb->mounted) \ | ||
279 | { \ | ||
280 | DEBUGF("%s() - volume %d not mounted\n", \ | ||
281 | __func__, IF_MV_VOL(volume)); \ | ||
282 | _bpb = NULL; \ | ||
283 | } \ | ||
284 | _bpb; }) | ||
285 | |||
286 | enum add_dir_entry_flags | ||
287 | { | ||
288 | DIRENT_RETURN = 0x01, /* return the new short entry */ | ||
289 | DIRENT_TEMPL = 0x0e, /* all TEMPL flags */ | ||
290 | DIRENT_TEMPL_CRT = 0x02, /* use template crttime */ | ||
291 | DIRENT_TEMPL_WRT = 0x04, /* use template wrttime */ | ||
292 | DIRENT_TEMPL_ACC = 0x08, /* use template lstacc time */ | ||
293 | DIRENT_TEMPL_TIMES = 0x0e, /* keep all time fields */ | ||
212 | }; | 294 | }; |
213 | 295 | ||
214 | static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE] CACHEALIGN_ATTR; | 296 | struct fatlong_parse_state |
215 | static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE]; | 297 | { |
216 | static struct mutex cache_mutex SHAREDBSS_ATTR; | 298 | int ord_max; |
217 | static struct mutex tempbuf_mutex; | 299 | int ord; |
218 | static char fat_tempbuf[SECTOR_SIZE] CACHEALIGN_ATTR; | 300 | uint8_t chksum; |
219 | static bool tempbuf_locked; | 301 | }; |
220 | 302 | ||
221 | #if defined(HAVE_HOTSWAP) | 303 | static void cache_commit(struct bpb *fat_bpb) |
222 | void fat_lock(void) | ||
223 | { | 304 | { |
224 | mutex_lock(&cache_mutex); | 305 | dc_lock_cache(); |
306 | #ifdef HAVE_FAT16SUPPORT | ||
307 | if (!fat_bpb->is_fat16) | ||
308 | #endif | ||
309 | update_fsinfo32(fat_bpb); | ||
310 | dc_commit_all(IF_MV(fat_bpb->volume)); | ||
311 | dc_unlock_cache(); | ||
225 | } | 312 | } |
226 | 313 | ||
227 | void fat_unlock(void) | 314 | static void cache_discard(IF_MV_NONVOID(struct bpb *fat_bpb)) |
228 | { | 315 | { |
229 | mutex_unlock(&cache_mutex); | 316 | dc_lock_cache(); |
317 | dc_discard_all(IF_MV(fat_bpb->volume)); | ||
318 | dc_unlock_cache(); | ||
230 | } | 319 | } |
231 | #endif | ||
232 | 320 | ||
233 | static long cluster2sec(IF_MV(struct bpb* fat_bpb,) long cluster) | 321 | /* caches a FAT or data area sector */ |
322 | static void * cache_sector(struct bpb *fat_bpb, unsigned long secnum) | ||
234 | { | 323 | { |
235 | #ifndef HAVE_MULTIVOLUME | 324 | unsigned int flags; |
236 | struct bpb* fat_bpb = &fat_bpbs[0]; | 325 | void *buf = dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags); |
237 | #endif | ||
238 | #ifdef HAVE_FAT16SUPPORT | ||
239 | /* negative clusters (FAT16 root dir) don't get the 2 offset */ | ||
240 | int zerocluster = cluster < 0 ? 0 : 2; | ||
241 | #else | ||
242 | const long zerocluster = 2; | ||
243 | #endif | ||
244 | 326 | ||
245 | if (cluster > (long)(fat_bpb->dataclusters + 1)) | 327 | if (!flags) |
246 | { | 328 | { |
247 | DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster); | 329 | int rc = storage_read_sectors(IF_MD(fat_bpb->drive,) |
248 | return -1; | 330 | secnum + fat_bpb->startsector, 1, buf); |
331 | if (UNLIKELY(rc < 0)) | ||
332 | { | ||
333 | DEBUGF("%s() - Could not read sector %ld" | ||
334 | " (error %d)\n", __func__, secnum, rc); | ||
335 | dc_discard_buf(buf); | ||
336 | return NULL; | ||
337 | } | ||
249 | } | 338 | } |
250 | 339 | ||
251 | return (cluster - zerocluster) * fat_bpb->bpb_secperclus | 340 | return buf; |
252 | + fat_bpb->firstdatasector; | ||
253 | } | 341 | } |
254 | 342 | ||
255 | void fat_size(IF_MV(int volume,) unsigned long* size, unsigned long* free) | 343 | /* returns a raw buffer for a sector; buffer counts as INUSE but filesystem |
344 | * contents are NOT loaded before returning - use when completely overwriting | ||
345 | * a sector's contents in order to avoid a fill */ | ||
346 | static void * cache_sector_buffer(IF_MV(struct bpb *fat_bpb,) | ||
347 | unsigned long secnum) | ||
256 | { | 348 | { |
257 | #ifndef HAVE_MULTIVOLUME | 349 | unsigned int flags; |
258 | const int volume = 0; | 350 | return dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags); |
259 | #endif | ||
260 | struct bpb* fat_bpb = &fat_bpbs[volume]; | ||
261 | if (size) | ||
262 | *size = fat_bpb->dataclusters * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | ||
263 | if (free) | ||
264 | *free = fat_bpb->fsinfo.freecount * (fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024); | ||
265 | } | 351 | } |
266 | 352 | ||
267 | void fat_init(void) | 353 | /* flush a cache buffer to storage */ |
354 | void dc_writeback_callback(IF_MV(int volume,) unsigned long sector, void *buf) | ||
268 | { | 355 | { |
269 | unsigned int i; | 356 | struct bpb * const fat_bpb = &fat_bpbs[IF_MV_VOL(volume)]; |
357 | unsigned int copies = !IS_FAT_SECTOR(fat_bpb, sector) ? | ||
358 | 1 : fat_bpb->bpb_numfats; | ||
359 | |||
360 | sector += fat_bpb->startsector; | ||
270 | 361 | ||
271 | if (!initialized) | 362 | while (1) |
272 | { | 363 | { |
273 | initialized = true; | 364 | int rc = storage_write_sectors(IF_MD(fat_bpb->drive,) sector, 1, buf); |
274 | mutex_init(&cache_mutex); | 365 | if (rc < 0) |
275 | mutex_init(&tempbuf_mutex); | 366 | { |
276 | tempbuf_locked = false; | 367 | panicf("%s() - Could not write sector %ld" |
368 | " (error %d)\n", __func__, sector, rc); | ||
369 | } | ||
370 | |||
371 | if (--copies == 0) | ||
372 | break; | ||
373 | |||
374 | /* Update next FAT */ | ||
375 | sector += fat_bpb->fatsize; | ||
277 | } | 376 | } |
377 | } | ||
278 | 378 | ||
279 | #ifdef HAVE_PRIORITY_SCHEDULING | 379 | static void raw_dirent_set_fstclus(union raw_dirent *ent, long fstclus) |
280 | /* Disable this because it is dangerous due to the assumption that | 380 | { |
281 | * mutex_unlock won't yield */ | 381 | ent->fstclushi = htole16(fstclus >> 16); |
282 | mutex_set_preempt(&cache_mutex, false); | 382 | ent->fstcluslo = htole16(fstclus & 0xffff); |
283 | #endif | 383 | } |
284 | 384 | ||
285 | /* mark the FAT cache as unused */ | 385 | static int bpb_is_sane(struct bpb *fat_bpb) |
286 | for(i = 0;i < FAT_CACHE_SIZE;i++) | 386 | { |
387 | if (fat_bpb->bpb_bytspersec % SECTOR_SIZE) | ||
287 | { | 388 | { |
288 | fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */ | 389 | DEBUGF("%s() - Error: sector size is not sane (%lu)\n", |
289 | fat_cache[i].inuse = false; | 390 | __func__, fat_bpb->bpb_bytspersec); |
290 | fat_cache[i].dirty = false; | 391 | return -1; |
291 | #ifdef HAVE_MULTIVOLUME | ||
292 | fat_cache[i].fat_vol = NULL; | ||
293 | #endif | ||
294 | } | 392 | } |
295 | #ifdef HAVE_MULTIVOLUME | 393 | |
296 | /* mark the possible volumes as not mounted */ | 394 | if (fat_bpb->bpb_secperclus * fat_bpb->bpb_bytspersec > 128*1024ul) |
297 | for (i=0; i<NUM_VOLUMES;i++) | ||
298 | { | 395 | { |
299 | fat_bpbs[i].mounted = false; | 396 | DEBUGF("%s() - Error: cluster size is larger than 128K " |
397 | "(%lu * %lu = %lu)\n", __func__, | ||
398 | fat_bpb->bpb_bytspersec, fat_bpb->bpb_secperclus, | ||
399 | fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus); | ||
400 | return -2; | ||
300 | } | 401 | } |
301 | #endif | ||
302 | } | ||
303 | 402 | ||
304 | /* fat_mount_internal is split out of fat_mount() to avoid having both the sector | 403 | if (fat_bpb->bpb_numfats != 2) |
305 | * buffer used here and the sector buffer used by update_fsinfo() on stack */ | 404 | { |
306 | static int fat_mount_internal(IF_MV(int volume,) IF_MD(int drive,) long startsector) | 405 | DEBUGF("%s() - Warning: NumFATS is not 2 (%u)\n", |
307 | { | 406 | __func__, fat_bpb->bpb_numfats); |
308 | #ifndef HAVE_MULTIVOLUME | 407 | } |
309 | const int volume = 0; | ||
310 | #endif | ||
311 | struct bpb* fat_bpb = &fat_bpbs[volume]; | ||
312 | int rc; | ||
313 | int secmult; | ||
314 | long datasec; | ||
315 | #ifdef HAVE_FAT16SUPPORT | ||
316 | int rootdirsectors; | ||
317 | #endif | ||
318 | 408 | ||
319 | unsigned char* buf = fat_get_sector_buffer(); | 409 | if (fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) |
320 | /* Read the sector */ | ||
321 | rc = storage_read_sectors(IF_MD(drive,) startsector,1,buf); | ||
322 | if(rc) | ||
323 | { | 410 | { |
324 | fat_release_sector_buffer(); | 411 | DEBUGF("%s() - Warning: Non-standard media type " |
325 | DEBUGF( "fat_mount() - Couldn't read BPB (error code %d)\n", rc); | 412 | "(0x%02x)\n", __func__, fat_bpb->bpb_media); |
326 | return rc * 10 - 1; | ||
327 | } | 413 | } |
328 | 414 | ||
329 | memset(fat_bpb, 0, sizeof(struct bpb)); | 415 | if (fat_bpb->last_word != 0xaa55) |
330 | fat_bpb->startsector = startsector; | 416 | { |
331 | #ifdef HAVE_MULTIDRIVE | 417 | DEBUGF("%s() - Error: Last word is not " |
332 | fat_bpb->drive = drive; | 418 | "0xaa55 (0x%04x)\n", __func__, fat_bpb->last_word); |
333 | #endif | 419 | return -3; |
420 | } | ||
334 | 421 | ||
335 | fat_bpb->bpb_bytspersec = BYTES2INT16(buf,BPB_BYTSPERSEC); | 422 | if (fat_bpb->fsinfo.freecount > |
336 | secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; | 423 | (fat_bpb->totalsectors - fat_bpb->firstdatasector) / |
337 | /* Sanity check is performed later */ | 424 | fat_bpb->bpb_secperclus) |
425 | { | ||
426 | DEBUGF("%s() - Error: FSInfo.Freecount > disk size " | ||
427 | "(0x%04lx)\n", __func__, | ||
428 | (unsigned long)fat_bpb->fsinfo.freecount); | ||
429 | return -4; | ||
430 | } | ||
338 | 431 | ||
339 | fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; | 432 | return 0; |
340 | fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf,BPB_RSVDSECCNT); | 433 | } |
341 | fat_bpb->bpb_numfats = buf[BPB_NUMFATS]; | ||
342 | fat_bpb->bpb_media = buf[BPB_MEDIA]; | ||
343 | fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf,BPB_FATSZ16); | ||
344 | fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf,BPB_FATSZ32); | ||
345 | fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf,BPB_TOTSEC16); | ||
346 | fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf,BPB_TOTSEC32); | ||
347 | fat_bpb->last_word = BYTES2INT16(buf,BPB_LAST_WORD); | ||
348 | 434 | ||
349 | /* calculate a few commonly used values */ | 435 | static uint8_t shortname_checksum(const unsigned char *shortname) |
350 | if (fat_bpb->bpb_fatsz16 != 0) | 436 | { |
351 | fat_bpb->fatsize = fat_bpb->bpb_fatsz16; | 437 | /* calculate shortname checksum */ |
352 | else | 438 | uint8_t chksum = 0; |
353 | fat_bpb->fatsize = fat_bpb->bpb_fatsz32; | ||
354 | 439 | ||
355 | if (fat_bpb->bpb_totsec16 != 0) | 440 | for (unsigned int i = 0; i < 11; i++) |
356 | fat_bpb->totalsectors = fat_bpb->bpb_totsec16; | 441 | chksum = (chksum << 7) + (chksum >> 1) + shortname[i]; |
357 | else | ||
358 | fat_bpb->totalsectors = fat_bpb->bpb_totsec32; | ||
359 | 442 | ||
360 | #ifdef HAVE_FAT16SUPPORT | 443 | return chksum; |
361 | fat_bpb->bpb_rootentcnt = BYTES2INT16(buf,BPB_ROOTENTCNT); | 444 | } |
362 | if (!fat_bpb->bpb_bytspersec) | ||
363 | { | ||
364 | fat_release_sector_buffer(); | ||
365 | return -2; | ||
366 | } | ||
367 | rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE | ||
368 | + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); | ||
369 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
370 | 445 | ||
371 | fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt | 446 | static void parse_short_direntry(const union raw_dirent *ent, |
372 | #ifdef HAVE_FAT16SUPPORT | 447 | struct fat_direntry *fatent) |
373 | + rootdirsectors | 448 | { |
374 | #endif | 449 | fatent->attr = ent->attr; |
375 | + fat_bpb->bpb_numfats * fat_bpb->fatsize; | 450 | fatent->crttimetenth = ent->crttimetenth; |
451 | fatent->crttime = letoh16(ent->crttime); | ||
452 | fatent->crtdate = letoh16(ent->crtdate); | ||
453 | fatent->lstaccdate = letoh16(ent->lstaccdate); | ||
454 | fatent->wrttime = letoh16(ent->wrttime); | ||
455 | fatent->wrtdate = letoh16(ent->wrtdate); | ||
456 | fatent->filesize = letoh32(ent->filesize); | ||
457 | fatent->firstcluster = ((uint32_t)letoh16(ent->fstcluslo) ) | | ||
458 | ((uint32_t)letoh16(ent->fstclushi) << 16); | ||
376 | 459 | ||
377 | /* Determine FAT type */ | 460 | /* fix the name */ |
378 | datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; | 461 | bool lowercase = ent->ntres & FAT_NTRES_LC_NAME; |
379 | if (fat_bpb->bpb_secperclus) | 462 | unsigned char c = ent->name[0]; |
380 | fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; | 463 | |
381 | else | 464 | if (c == 0x05) /* special kanji char */ |
465 | c = 0xe5; | ||
466 | |||
467 | int j = 0; | ||
468 | |||
469 | for (int i = 0; c != ' '; c = ent->name[i]) | ||
382 | { | 470 | { |
383 | fat_release_sector_buffer(); | 471 | fatent->shortname[j++] = lowercase ? tolower(c) : c; |
384 | return -2; | ||
385 | } | ||
386 | 472 | ||
387 | #ifdef TEST_FAT | 473 | if (++i >= 8) |
388 | /* | 474 | break; |
389 | we are sometimes testing with "illegally small" fat32 images, | ||
390 | so we don't use the proper fat32 test case for test code | ||
391 | */ | ||
392 | if ( fat_bpb->bpb_fatsz16 ) | ||
393 | #else | ||
394 | if ( fat_bpb->dataclusters < 65525 ) | ||
395 | #endif | ||
396 | { /* FAT16 */ | ||
397 | #ifdef HAVE_FAT16SUPPORT | ||
398 | fat_bpb->is_fat16 = true; | ||
399 | if (fat_bpb->dataclusters < 4085) | ||
400 | { /* FAT12 */ | ||
401 | fat_release_sector_buffer(); | ||
402 | DEBUGF("This is FAT12. Go away!\n"); | ||
403 | return -2; | ||
404 | } | ||
405 | #else /* #ifdef HAVE_FAT16SUPPORT */ | ||
406 | fat_release_sector_buffer(); | ||
407 | DEBUGF("This is not FAT32. Go away!\n"); | ||
408 | return -2; | ||
409 | #endif /* #ifndef HAVE_FAT16SUPPORT */ | ||
410 | } | 475 | } |
411 | 476 | ||
412 | #ifdef HAVE_FAT16SUPPORT | 477 | if (ent->name[8] != ' ') |
413 | if (fat_bpb->is_fat16) | 478 | { |
414 | { /* FAT16 specific part of BPB */ | 479 | lowercase = ent->ntres & FAT_NTRES_LC_EXT; |
415 | int dirclusters; | 480 | fatent->shortname[j++] = '.'; |
416 | fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt | 481 | |
417 | + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; | 482 | for (int i = 8; i < 11 && (c = ent->name[i]) != ' '; i++) |
418 | dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) | 483 | fatent->shortname[j++] = lowercase ? tolower(c) : c; |
419 | / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ | ||
420 | /* I assign negative pseudo cluster numbers for the root directory, | ||
421 | their range is counted upward until -1. */ | ||
422 | fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data*/ | ||
423 | fat_bpb->rootdiroffset = dirclusters * fat_bpb->bpb_secperclus | ||
424 | - rootdirsectors; | ||
425 | } | ||
426 | else | ||
427 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
428 | { /* FAT32 specific part of BPB */ | ||
429 | fat_bpb->bpb_rootclus = BYTES2INT32(buf,BPB_ROOTCLUS); | ||
430 | fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf,BPB_FSINFO); | ||
431 | fat_bpb->rootdirsector = cluster2sec(IF_MV(fat_bpb,) | ||
432 | fat_bpb->bpb_rootclus); | ||
433 | } | 484 | } |
434 | 485 | ||
435 | rc = bpb_is_sane(IF_MV(fat_bpb)); | 486 | fatent->shortname[j] = 0; |
436 | if (rc < 0) | 487 | } |
488 | |||
489 | static unsigned char char2dos(unsigned char c, int *np) | ||
490 | { | ||
491 | /* FIXME: needs conversion to OEM charset FIRST but there is currently | ||
492 | no unicode function for that! */ | ||
493 | |||
494 | /* smallest tables with one-step lookup that directly map the lists; | ||
495 | here we're only concerned with what gets through the longname | ||
496 | filter (check_longname will have been called earlier so common | ||
497 | illegal chars are neither in these tables nor checked for) */ | ||
498 | static const unsigned char remove_chars_tbl[3] = | ||
499 | { 0, '.', ' ' }; | ||
500 | |||
501 | static const unsigned char replace_chars_tbl[11] = | ||
502 | { ',', 0, 0, '[', ';', ']', '=', 0, 0, 0, '+' }; | ||
503 | |||
504 | if (remove_chars_tbl[c % 3] == c) | ||
437 | { | 505 | { |
438 | fat_release_sector_buffer(); | 506 | /* Illegal char, remove */ |
439 | DEBUGF( "fat_mount() - BPB is not sane\n"); | 507 | c = 0; |
440 | return rc * 10 - 3; | 508 | *np = 0; |
441 | } | 509 | } |
442 | 510 | else if (c >= 0x80 || replace_chars_tbl[c % 11] == c) | |
443 | #ifdef HAVE_FAT16SUPPORT | ||
444 | if (fat_bpb->is_fat16) | ||
445 | { | 511 | { |
446 | fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc below */ | 512 | /* Illegal char, replace (note: NTFS behavior for extended chars) */ |
447 | fat_bpb->fsinfo.nextfree = 0xffffffff; | 513 | c = '_'; |
514 | *np = 0; | ||
448 | } | 515 | } |
449 | else | 516 | else |
450 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
451 | { | 517 | { |
452 | /* Read the fsinfo sector */ | 518 | c = toupper(c); |
453 | rc = storage_read_sectors(IF_MD(drive,) | ||
454 | startsector + fat_bpb->bpb_fsinfo, 1, buf); | ||
455 | if (rc < 0) | ||
456 | { | ||
457 | fat_release_sector_buffer(); | ||
458 | DEBUGF( "fat_mount() - Couldn't read FSInfo (error code %d)\n", rc); | ||
459 | return rc * 10 - 4; | ||
460 | } | ||
461 | fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); | ||
462 | fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); | ||
463 | } | 519 | } |
464 | fat_release_sector_buffer(); | ||
465 | return 0; | ||
466 | } | ||
467 | 520 | ||
468 | void* fat_get_sector_buffer() | 521 | return c; |
469 | { | ||
470 | mutex_lock(&tempbuf_mutex); | ||
471 | if (tempbuf_locked) | ||
472 | panicf("FAT: Tried to lock temporary sector buffer twice!"); | ||
473 | tempbuf_locked = true; | ||
474 | return fat_tempbuf; | ||
475 | } | 522 | } |
476 | 523 | ||
477 | void fat_release_sector_buffer() | 524 | /* convert long name into dos name, possibly recommending randomization */ |
525 | static void create_dos_name(unsigned char *basisname, | ||
526 | const unsigned char *name, int *np) | ||
478 | { | 527 | { |
479 | tempbuf_locked = false; | 528 | int i; |
480 | mutex_unlock(&tempbuf_mutex); | 529 | |
530 | /* FIXME: needs conversion to OEM charset FIRST but there is currently | ||
531 | no unicode function for that! */ | ||
532 | |||
533 | /* as per FAT spec, set "lossy conversion" flag if any destructive | ||
534 | alterations to the name occur other than case changes */ | ||
535 | *np = -1; | ||
536 | |||
537 | /* find extension part */ | ||
538 | unsigned char *ext = strrchr(name, '.'); | ||
539 | if (ext && (ext == name || strchr(ext, ' '))) | ||
540 | ext = NULL; /* handle .dotnames / extensions cannot have spaces */ | ||
541 | |||
542 | /* name part */ | ||
543 | for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) | ||
544 | { | ||
545 | unsigned char c = char2dos(*name, np); | ||
546 | if (c) | ||
547 | basisname[i++] = c; | ||
548 | } | ||
549 | |||
550 | /* pad both name and extension */ | ||
551 | while (i < 11) | ||
552 | basisname[i++] = ' '; | ||
553 | |||
554 | if (basisname[0] == 0xe5) /* special kanji character */ | ||
555 | basisname[0] = 0x05; | ||
556 | |||
557 | /* extension part */ | ||
558 | if (!ext++) | ||
559 | return; /* no extension */ | ||
560 | |||
561 | for (i = 8; *ext && i < 11; ext++) | ||
562 | { | ||
563 | unsigned char c = char2dos(*ext, np); | ||
564 | if (c) | ||
565 | basisname[i++] = c; | ||
566 | } | ||
567 | |||
568 | if (*ext) | ||
569 | *np = 0; /* extension too long */ | ||
481 | } | 570 | } |
482 | 571 | ||
483 | #ifdef MAX_LOG_SECTOR_SIZE | 572 | static void randomize_dos_name(unsigned char *dosname, |
484 | int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)) | 573 | const unsigned char *basisname, |
574 | int *np) | ||
485 | { | 575 | { |
486 | #ifdef HAVE_MULTIVOLUME | 576 | int n = *np; |
487 | if(!fat_bpbs[volume].mounted) | 577 | |
488 | return 0; | 578 | memcpy(dosname, basisname, 11); |
489 | return fat_bpbs[volume].bpb_bytspersec; | 579 | |
490 | #else | 580 | if (n < 0) |
491 | return fat_bpbs[0].bpb_bytspersec; | 581 | { |
492 | #endif | 582 | /* first one just copies */ |
583 | *np = 0; | ||
584 | return; | ||
585 | } | ||
586 | |||
587 | /* the "~n" string can range from "~1" to "~999999" | ||
588 | of course a directory can have at most 65536 entries which means | ||
589 | the numbers will never be required to get that big in order to map | ||
590 | to a unique name */ | ||
591 | if (++n > 999999) | ||
592 | n = 1; | ||
593 | |||
594 | unsigned char numtail[8]; /* holds "~n" */ | ||
595 | unsigned int numtaillen = snprintf(numtail, 8, "~%d", n); | ||
596 | |||
597 | unsigned int basislen = 0; | ||
598 | while (basislen < 8 && basisname[basislen] != ' ') | ||
599 | basislen++; | ||
600 | |||
601 | memcpy(dosname + MIN(8 - numtaillen, basislen), numtail, numtaillen); | ||
602 | |||
603 | *np = n; | ||
493 | } | 604 | } |
494 | #endif | ||
495 | 605 | ||
496 | int fat_mount(IF_MV(int volume,) IF_MD(int drive,) long startsector) | 606 | /* check long filename for validity */ |
607 | static int check_longname(const unsigned char *name) | ||
497 | { | 608 | { |
498 | #ifndef HAVE_MULTIVOLUME | 609 | /* smallest table with one-step lookup that directly maps the list */ |
499 | const int volume = 0; | 610 | static const unsigned char invalid_chars_tbl[19] = |
500 | #endif | 611 | { |
501 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 612 | 0, ':', 0, '<', '*', '>', '?', 0, 0, |
502 | int rc; | 613 | '/', '|', 0, 0, 0x7f, 0, '"', '\\', 0, 0 |
614 | }; | ||
503 | 615 | ||
504 | rc = fat_mount_internal(IF_MV(volume,) IF_MD(drive,) startsector); | 616 | if (!name) |
617 | return -1; | ||
505 | 618 | ||
506 | if(rc!=0) return rc; | 619 | unsigned int c = *name; |
507 | 620 | ||
508 | /* calculate freecount if unset */ | 621 | do |
509 | if ( fat_bpb->fsinfo.freecount == 0xffffffff ) | ||
510 | { | 622 | { |
511 | fat_recalc_free(IF_MV(volume)); | 623 | if (c < 0x20 || invalid_chars_tbl[c % 19] == c) |
624 | return -2; | ||
512 | } | 625 | } |
626 | while ((c = *++name)); | ||
513 | 627 | ||
514 | LDEBUGF("Freecount: %ld\n",(unsigned long)fat_bpb->fsinfo.freecount); | 628 | /* check trailing space(s) and periods */ |
515 | LDEBUGF("Nextfree: 0x%lx\n",(unsigned long)fat_bpb->fsinfo.nextfree); | 629 | c = *--name; |
516 | LDEBUGF("Cluster count: 0x%lx\n",(unsigned long)fat_bpb->dataclusters); | 630 | if (c == ' ' || c == '.') |
517 | LDEBUGF("Sectors per cluster: %d\n",fat_bpb->bpb_secperclus); | 631 | return -3; |
518 | LDEBUGF("FAT sectors: 0x%lx\n",(unsigned long)fat_bpb->fatsize); | ||
519 | |||
520 | #ifdef HAVE_MULTIVOLUME | ||
521 | fat_bpb->mounted = true; | ||
522 | #endif | ||
523 | 632 | ||
524 | return 0; | 633 | return 0; |
525 | } | 634 | } |
526 | 635 | ||
527 | int fat_unmount(int volume, bool flush) | 636 | /* Get first longname entry name offset */ |
637 | static inline unsigned int longent_char_first(void) | ||
528 | { | 638 | { |
529 | int rc; | 639 | return 1; |
530 | #ifdef HAVE_MULTIVOLUME | 640 | } |
531 | struct bpb* fat_bpb = &fat_bpbs[volume]; | ||
532 | #else | ||
533 | (void)volume; | ||
534 | #endif | ||
535 | 641 | ||
536 | if(flush) | 642 | /* Get the next longname entry offset or 0 if the end is reached */ |
643 | static inline unsigned int longent_char_next(unsigned int i) | ||
644 | { | ||
645 | switch (i += 2) | ||
537 | { | 646 | { |
538 | rc = flush_fat(IF_MV(fat_bpb)); /* the clean way, while still alive */ | 647 | case 26: i -= 1; /* return 28 */ |
648 | case 11: i += 3; /* return 14 */ | ||
539 | } | 649 | } |
540 | else | 650 | |
541 | { /* volume is not accessible any more, e.g. MMC removed */ | 651 | return i < 32 ? i : 0; |
542 | int i; | ||
543 | mutex_lock(&cache_mutex); | ||
544 | for(i = 0;i < FAT_CACHE_SIZE;i++) | ||
545 | { | ||
546 | struct fat_cache_entry *fce = &fat_cache[i]; | ||
547 | if(fce->inuse | ||
548 | #ifdef HAVE_MULTIVOLUME | ||
549 | && fce->fat_vol == fat_bpb | ||
550 | #endif | ||
551 | ) | ||
552 | { | ||
553 | fce->inuse = false; /* discard all from that volume */ | ||
554 | fce->dirty = false; | ||
555 | } | ||
556 | } | ||
557 | mutex_unlock(&cache_mutex); | ||
558 | rc = 0; | ||
559 | } | ||
560 | #ifdef HAVE_MULTIVOLUME | ||
561 | fat_bpb->mounted = false; | ||
562 | #endif | ||
563 | return rc; | ||
564 | } | 652 | } |
565 | 653 | ||
566 | void fat_recalc_free(IF_MV_NONVOID(int volume)) | 654 | /* initialize the parse state; call before parsing first long entry */ |
655 | static void NO_INLINE fatlong_parse_start(struct fatlong_parse_state *lnparse) | ||
567 | { | 656 | { |
568 | #ifndef HAVE_MULTIVOLUME | 657 | /* no inline so gcc can't figure out what isn't initialized here; |
569 | const int volume = 0; | 658 | ord_max is king as to the validity of all other fields */ |
570 | #endif | 659 | lnparse->ord_max = -1; /* one resync per parse operation */ |
571 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 660 | } |
572 | long free = 0; | 661 | |
573 | unsigned long i; | 662 | /* convert the FAT long name entry to a contiguous segment */ |
574 | #ifdef HAVE_FAT16SUPPORT | 663 | static bool fatlong_parse_entry(struct fatlong_parse_state *lnparse, |
575 | if (fat_bpb->is_fat16) | 664 | const union raw_dirent *ent, |
665 | struct fat_direntry *fatent) | ||
666 | { | ||
667 | int ord = ent->ldir_ord; | ||
668 | |||
669 | if (ord & FATLONG_ORD_F_LAST) | ||
576 | { | 670 | { |
577 | for (i = 0; i<fat_bpb->fatsize; i++) { | 671 | /* this entry is the first long entry (first in order but |
578 | unsigned int j; | 672 | containing last part) */ |
579 | uint16_t* fat = cache_fat_sector(IF_MV(fat_bpb,) i, false); | 673 | ord &= ~FATLONG_ORD_F_LAST; |
580 | for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 674 | |
581 | unsigned int c = i * CLUSTERS_PER_FAT16_SECTOR + j; | 675 | if (ord == 0 || ord > FATLONG_MAX_ORDER) |
582 | if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 676 | { |
583 | break; | 677 | lnparse->ord_max = 0; |
584 | 678 | return true; | |
585 | if (letoh16(fat[j]) == 0x0000) { | ||
586 | free++; | ||
587 | if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | ||
588 | fat_bpb->fsinfo.nextfree = c; | ||
589 | } | ||
590 | } | ||
591 | } | 679 | } |
680 | |||
681 | lnparse->ord_max = ord; | ||
682 | lnparse->ord = ord; | ||
683 | lnparse->chksum = ent->ldir_chksum; | ||
592 | } | 684 | } |
593 | else | 685 | else |
594 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 686 | { |
595 | { | 687 | /* valid ordinals yet? */ |
596 | for (i = 0; i<fat_bpb->fatsize; i++) { | 688 | if (lnparse->ord_max <= 0) |
597 | unsigned int j; | 689 | { |
598 | uint32_t* fat = cache_fat_sector(IF_MV(fat_bpb,) i, false); | 690 | if (lnparse->ord_max == 0) |
599 | for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 691 | return true; |
600 | unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; | 692 | |
601 | if ( c > fat_bpb->dataclusters+1 ) /* nr 0 is unused */ | 693 | lnparse->ord_max = 0; |
602 | break; | 694 | return false; /* try resync */ |
603 | 695 | } | |
604 | if (!(letoh32(fat[j]) & 0x0fffffff)) { | 696 | |
605 | free++; | 697 | /* check ordinal continuity and that the checksum matches the |
606 | if ( fat_bpb->fsinfo.nextfree == 0xffffffff ) | 698 | one stored in the last entry */ |
607 | fat_bpb->fsinfo.nextfree = c; | 699 | if (ord == 0 || ord != lnparse->ord - 1 || |
608 | } | 700 | lnparse->chksum != ent->ldir_chksum) |
609 | } | 701 | { |
702 | lnparse->ord_max = 0; | ||
703 | return true; | ||
610 | } | 704 | } |
611 | } | 705 | } |
612 | fat_bpb->fsinfo.freecount = free; | ||
613 | update_fsinfo(IF_MV(fat_bpb)); | ||
614 | } | ||
615 | 706 | ||
616 | static int bpb_is_sane(IF_MV_NONVOID(struct bpb* fat_bpb)) | 707 | /* so far so good; save entry information */ |
617 | { | 708 | lnparse->ord = ord; |
618 | #ifndef HAVE_MULTIVOLUME | 709 | |
619 | struct bpb* fat_bpb = &fat_bpbs[0]; | 710 | uint16_t *ucsp = fatent->ucssegs[ord - 1 + 5]; |
620 | #endif | 711 | unsigned int i = longent_char_first(); |
621 | if(fat_bpb->bpb_bytspersec % SECTOR_SIZE) | 712 | |
713 | while ((*ucsp++ = BYTES2INT16(ent->data, i))) | ||
622 | { | 714 | { |
623 | DEBUGF( "bpb_is_sane() - Error: sector size is not sane (%d)\n", | 715 | if (!(i = longent_char_next(i))) |
624 | fat_bpb->bpb_bytspersec); | 716 | return true; |
625 | return -1; | ||
626 | } | ||
627 | if((long)fat_bpb->bpb_secperclus * SECTOR_SIZE > 128L*1024L) | ||
628 | { | ||
629 | /* We don't multiply by bpb_bytspersec here, because | ||
630 | * back in fat_mount_internal() bpb_secperclus has been | ||
631 | * "normalised" to 512 byte clusters, by multiplying with | ||
632 | * secmult. */ | ||
633 | DEBUGF( "bpb_is_sane() - Error: cluster size is larger than 128K " | ||
634 | "(%d * %d = %d)\n", | ||
635 | fat_bpb->bpb_bytspersec, | ||
636 | fat_bpb->bpb_secperclus / (fat_bpb->bpb_bytspersec / SECTOR_SIZE), | ||
637 | fat_bpb->bpb_bytspersec * fat_bpb->bpb_secperclus / | ||
638 | (fat_bpb->bpb_bytspersec / SECTOR_SIZE)); | ||
639 | return -2; | ||
640 | } | 717 | } |
641 | if(fat_bpb->bpb_numfats != 2) | 718 | |
719 | /* segment may end early only in last entry */ | ||
720 | if (ord == lnparse->ord_max) | ||
642 | { | 721 | { |
643 | DEBUGF( "bpb_is_sane() - Warning: NumFATS is not 2 (%d)\n", | 722 | /* the only valid padding, if any, is 0xffff */ |
644 | fat_bpb->bpb_numfats); | 723 | do |
724 | { | ||
725 | if (!(i = longent_char_next(i))) | ||
726 | return true; | ||
727 | } | ||
728 | while (BYTES2INT16(ent->data, i) == 0xffff); | ||
645 | } | 729 | } |
646 | if(fat_bpb->bpb_media != 0xf0 && fat_bpb->bpb_media < 0xf8) | 730 | |
731 | /* long filename is corrupt */ | ||
732 | lnparse->ord_max = 0; | ||
733 | return true; | ||
734 | } | ||
735 | |||
736 | /* finish parsing of the longname entries and do the conversion to | ||
737 | UTF-8 if we have all the segments */ | ||
738 | static bool fatlong_parse_finish(struct fatlong_parse_state *lnparse, | ||
739 | const union raw_dirent *ent, | ||
740 | struct fat_direntry *fatent) | ||
741 | { | ||
742 | parse_short_direntry(ent, fatent); | ||
743 | |||
744 | /* ord_max must not have been set to <= 0 because of any earlier problems | ||
745 | and the ordinal immediately before the shortname entry must be 1 */ | ||
746 | if (lnparse->ord_max <= 0 || lnparse->ord != 1) | ||
747 | return false; | ||
748 | |||
749 | /* check the longname checksums against the shortname checksum */ | ||
750 | if (lnparse->chksum != shortname_checksum(ent->name)) | ||
751 | return false; | ||
752 | |||
753 | /* longname is good so far so convert all the segments to UTF-8 */ | ||
754 | unsigned char * const name = fatent->name; | ||
755 | unsigned char *p = name; | ||
756 | |||
757 | /* ensure the last segment is NULL-terminated if it is filled */ | ||
758 | fatent->ucssegs[lnparse->ord_max + 5][0] = 0x0000; | ||
759 | |||
760 | for (uint16_t *ucsp = fatent->ucssegs[5], ucc = *ucsp; | ||
761 | ucc; ucc = *++ucsp) | ||
647 | { | 762 | { |
648 | DEBUGF( "bpb_is_sane() - Warning: Non-standard " | 763 | /* end should be hit before ever seeing padding */ |
649 | "media type (0x%02x)\n", | 764 | if (ucc == 0xffff) |
650 | fat_bpb->bpb_media); | 765 | return false; |
766 | |||
767 | if ((p = utf8encode(ucc, p)) - name > FAT_DIRENTRY_NAME_MAX) | ||
768 | return false; | ||
651 | } | 769 | } |
652 | if(fat_bpb->last_word != 0xaa55) | 770 | |
771 | /* longname ok */ | ||
772 | *p = '\0'; | ||
773 | return true; | ||
774 | } | ||
775 | |||
776 | static unsigned long cluster2sec(struct bpb *fat_bpb, long cluster) | ||
777 | { | ||
778 | long zerocluster = 2; | ||
779 | |||
780 | /* negative clusters (FAT16 root dir) don't get the 2 offset */ | ||
781 | #ifdef HAVE_FAT16SUPPORT | ||
782 | if (fat_bpb->is_fat16 && cluster < 0) | ||
653 | { | 783 | { |
654 | DEBUGF( "bpb_is_sane() - Error: Last word is not " | 784 | zerocluster = 0; |
655 | "0xaa55 (0x%04x)\n", fat_bpb->last_word); | ||
656 | return -3; | ||
657 | } | 785 | } |
658 | 786 | else | |
659 | if (fat_bpb->fsinfo.freecount > | 787 | #endif /* HAVE_FAT16SUPPORT */ |
660 | (fat_bpb->totalsectors - fat_bpb->firstdatasector)/ | 788 | if ((unsigned long)cluster > fat_bpb->dataclusters + 1) |
661 | fat_bpb->bpb_secperclus) | ||
662 | { | 789 | { |
663 | DEBUGF( "bpb_is_sane() - Error: FSInfo.Freecount > disk size " | 790 | DEBUGF( "%s() - Bad cluster number (%ld)\n", __func__, cluster); |
664 | "(0x%04lx)\n", (unsigned long)fat_bpb->fsinfo.freecount); | 791 | return 0; |
665 | return -4; | ||
666 | } | 792 | } |
667 | 793 | ||
668 | return 0; | 794 | return (unsigned long)(cluster - zerocluster)*fat_bpb->bpb_secperclus |
795 | + fat_bpb->firstdatasector; | ||
669 | } | 796 | } |
670 | 797 | ||
671 | static void flush_fat_sector(struct fat_cache_entry *fce, | 798 | #ifdef HAVE_FAT16SUPPORT |
672 | unsigned char *sectorbuf) | 799 | static long get_next_cluster16(struct bpb *fat_bpb, long startcluster) |
673 | { | 800 | { |
674 | int rc; | 801 | /* if FAT16 root dir, dont use the FAT */ |
675 | long secnum; | 802 | if (startcluster < 0) |
803 | return startcluster + 1; | ||
676 | 804 | ||
677 | /* With multivolume, use only the FAT info from the cached sector! */ | 805 | unsigned long entry = startcluster; |
678 | #ifdef HAVE_MULTIVOLUME | 806 | unsigned long sector = entry / CLUSTERS_PER_FAT16_SECTOR; |
679 | secnum = fce->secnum + fce->fat_vol->startsector; | 807 | unsigned long offset = entry % CLUSTERS_PER_FAT16_SECTOR; |
680 | #else | 808 | |
681 | secnum = fce->secnum + fat_bpbs[0].startsector; | 809 | dc_lock_cache(); |
682 | #endif | ||
683 | 810 | ||
684 | /* Write to the first FAT */ | 811 | uint16_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); |
685 | rc = storage_write_sectors(IF_MD(fce->fat_vol->drive,) | 812 | if (!sec) |
686 | secnum, 1, | ||
687 | sectorbuf); | ||
688 | if(rc < 0) | ||
689 | { | 813 | { |
690 | panicf("flush_fat_sector() - Could not write sector %ld" | 814 | dc_unlock_cache(); |
691 | " (error %d)\n", | 815 | DEBUGF("%s: Could not cache sector %d\n", __func__, sector); |
692 | secnum, rc); | 816 | return -1; |
693 | } | 817 | } |
694 | #ifdef HAVE_MULTIVOLUME | 818 | |
695 | if(fce->fat_vol->bpb_numfats > 1) | 819 | long next = letoh16(sec[offset]); |
696 | #else | 820 | |
697 | if(fat_bpbs[0].bpb_numfats > 1) | 821 | /* is this last cluster in chain? */ |
698 | #endif | 822 | if (next >= FAT16_EOF_MARK) |
823 | next = 0; | ||
824 | |||
825 | dc_unlock_cache(); | ||
826 | return next; | ||
827 | } | ||
828 | |||
829 | static long find_free_cluster16(struct bpb *fat_bpb, long startcluster) | ||
830 | { | ||
831 | unsigned long entry = startcluster; | ||
832 | unsigned long sector = entry / CLUSTERS_PER_FAT16_SECTOR; | ||
833 | unsigned long offset = entry % CLUSTERS_PER_FAT16_SECTOR; | ||
834 | |||
835 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) | ||
699 | { | 836 | { |
700 | /* Write to the second FAT */ | 837 | unsigned long nr = (i + sector) % fat_bpb->fatsize; |
701 | #ifdef HAVE_MULTIVOLUME | 838 | uint16_t *sec = cache_sector(fat_bpb, nr + fat_bpb->fatrgnstart); |
702 | secnum += fce->fat_vol->fatsize; | 839 | if (!sec) |
703 | #else | 840 | break; |
704 | secnum += fat_bpbs[0].fatsize; | 841 | |
705 | #endif | 842 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) |
706 | rc = storage_write_sectors(IF_MD(fce->fat_vol->drive,) | ||
707 | secnum, 1, sectorbuf); | ||
708 | if(rc < 0) | ||
709 | { | 843 | { |
710 | panicf("flush_fat_sector() - Could not write sector %ld" | 844 | unsigned long k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; |
711 | " (error %d)\n", | 845 | |
712 | secnum, rc); | 846 | if (letoh16(sec[k]) == 0x0000) |
847 | { | ||
848 | unsigned long c = nr * CLUSTERS_PER_FAT16_SECTOR + k; | ||
849 | /* Ignore the reserved clusters 0 & 1, and also | ||
850 | cluster numbers out of bounds */ | ||
851 | if (c < 2 || c > fat_bpb->dataclusters + 1) | ||
852 | continue; | ||
853 | |||
854 | DEBUGF("%s(%lx) == %x\n", __func__, startcluster, c); | ||
855 | |||
856 | fat_bpb->fsinfo.nextfree = c; | ||
857 | return c; | ||
858 | } | ||
713 | } | 859 | } |
860 | |||
861 | offset = 0; | ||
714 | } | 862 | } |
715 | fce->dirty = false; | 863 | |
864 | DEBUGF("%s(%lx) == 0\n", __func__, startcluster); | ||
865 | return 0; /* 0 is an illegal cluster number */ | ||
716 | } | 866 | } |
717 | 867 | ||
718 | /* Note: The returned pointer is only safely valid until the next | 868 | static int update_fat_entry16(struct bpb *fat_bpb, unsigned long entry, |
719 | task switch! (Any subsequent ata read/write may yield.) */ | 869 | unsigned long val) |
720 | static void *cache_fat_sector(IF_MV(struct bpb* fat_bpb,) | ||
721 | long fatsector, bool dirty) | ||
722 | { | 870 | { |
723 | #ifndef HAVE_MULTIVOLUME | 871 | unsigned long sector = entry / CLUSTERS_PER_FAT16_SECTOR; |
724 | struct bpb* fat_bpb = &fat_bpbs[0]; | 872 | unsigned long offset = entry % CLUSTERS_PER_FAT16_SECTOR; |
725 | #endif | ||
726 | long secnum = fatsector + fat_bpb->bpb_rsvdseccnt; | ||
727 | int cache_index = secnum & FAT_CACHE_MASK; | ||
728 | struct fat_cache_entry *fce = &fat_cache[cache_index]; | ||
729 | unsigned char *sectorbuf = &fat_cache_sectors[cache_index][0]; | ||
730 | int rc; | ||
731 | 873 | ||
732 | mutex_lock(&cache_mutex); /* make changes atomic */ | 874 | val &= 0xFFFF; |
733 | 875 | ||
734 | /* Delete the cache entry if it isn't the sector we want */ | 876 | DEBUGF("%s(entry:%lx,val:%lx)\n", __func__, entry, val); |
735 | if(fce->inuse && (fce->secnum != secnum | 877 | |
736 | #ifdef HAVE_MULTIVOLUME | 878 | if (entry == val) |
737 | || fce->fat_vol != fat_bpb | 879 | panicf("Creating FAT16 loop: %lx,%lx\n", entry, val); |
738 | #endif | 880 | |
739 | )) | 881 | if (entry < 2) |
882 | panicf("Updating reserved FAT16 entry %lu\n", entry); | ||
883 | |||
884 | dc_lock_cache(); | ||
885 | |||
886 | int16_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); | ||
887 | if (!sec) | ||
740 | { | 888 | { |
741 | /* Write back if it is dirty */ | 889 | dc_unlock_cache(); |
742 | if(fce->dirty) | 890 | DEBUGF("Could not cache sector %u\n", sector); |
743 | { | 891 | return -1; |
744 | flush_fat_sector(fce, sectorbuf); | ||
745 | } | ||
746 | fce->inuse = false; | ||
747 | } | 892 | } |
748 | 893 | ||
749 | /* Load the sector if it is not cached */ | 894 | uint16_t curval = letoh16(sec[offset]); |
750 | if(!fce->inuse) | 895 | |
896 | if (val) | ||
751 | { | 897 | { |
752 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) | 898 | /* being allocated */ |
753 | secnum + fat_bpb->startsector,1, | 899 | if (curval == 0x0000 && fat_bpb->fsinfo.freecount > 0) |
754 | sectorbuf); | 900 | fat_bpb->fsinfo.freecount--; |
755 | if(rc < 0) | ||
756 | { | ||
757 | DEBUGF( "cache_fat_sector() - Could not read sector %ld" | ||
758 | " (error %d)\n", secnum, rc); | ||
759 | mutex_unlock(&cache_mutex); | ||
760 | return NULL; | ||
761 | } | ||
762 | fce->inuse = true; | ||
763 | fce->secnum = secnum; | ||
764 | #ifdef HAVE_MULTIVOLUME | ||
765 | fce->fat_vol = fat_bpb; | ||
766 | #endif | ||
767 | } | 901 | } |
768 | if (dirty) | 902 | else |
769 | fce->dirty = true; /* dirt remains, sticky until flushed */ | 903 | { |
770 | mutex_unlock(&cache_mutex); | 904 | /* being freed */ |
771 | return sectorbuf; | 905 | if (curval != 0x0000) |
906 | fat_bpb->fsinfo.freecount++; | ||
907 | } | ||
908 | |||
909 | DEBUGF("%lu free clusters\n", (unsigned long)fat_bpb->fsinfo.freecount); | ||
910 | |||
911 | sec[offset] = htole16(val); | ||
912 | dc_dirty_buf(sec); | ||
913 | |||
914 | dc_unlock_cache(); | ||
915 | return 0; | ||
772 | } | 916 | } |
773 | 917 | ||
774 | static unsigned long find_free_cluster(IF_MV(struct bpb* fat_bpb,) | 918 | static void fat_recalc_free_internal16(struct bpb *fat_bpb) |
775 | unsigned long startcluster) | ||
776 | { | 919 | { |
777 | #ifndef HAVE_MULTIVOLUME | 920 | unsigned long free = 0; |
778 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
779 | #endif | ||
780 | unsigned long sector; | ||
781 | unsigned long offset; | ||
782 | unsigned long i; | ||
783 | 921 | ||
784 | #ifdef HAVE_FAT16SUPPORT | 922 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) |
785 | if (fat_bpb->is_fat16) | ||
786 | { | 923 | { |
787 | sector = startcluster / CLUSTERS_PER_FAT16_SECTOR; | 924 | uint16_t *sec = cache_sector(fat_bpb, i + fat_bpb->fatrgnstart); |
788 | offset = startcluster % CLUSTERS_PER_FAT16_SECTOR; | 925 | if (!sec) |
926 | break; | ||
789 | 927 | ||
790 | for (i = 0; i<fat_bpb->fatsize; i++) { | 928 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) |
791 | unsigned int j; | 929 | { |
792 | unsigned int nr = (i + sector) % fat_bpb->fatsize; | 930 | unsigned long c = i * CLUSTERS_PER_FAT16_SECTOR + j; |
793 | uint16_t* fat = cache_fat_sector(IF_MV(fat_bpb,) nr, false); | 931 | |
794 | if ( !fat ) | 932 | if (c < 2 || c > fat_bpb->dataclusters + 1) /* nr 0 is unused */ |
795 | break; | 933 | continue; |
796 | for (j = 0; j < CLUSTERS_PER_FAT16_SECTOR; j++) { | 934 | |
797 | int k = (j + offset) % CLUSTERS_PER_FAT16_SECTOR; | 935 | if (letoh16(sec[j]) != 0x0000) |
798 | if (letoh16(fat[k]) == 0x0000) { | 936 | continue; |
799 | unsigned int c = nr * CLUSTERS_PER_FAT16_SECTOR + k; | 937 | |
800 | /* Ignore the reserved clusters 0 & 1, and also | 938 | free++; |
801 | cluster numbers out of bounds */ | 939 | if (fat_bpb->fsinfo.nextfree == 0xffffffff) |
802 | if ( c < 2 || c > fat_bpb->dataclusters+1 ) | 940 | fat_bpb->fsinfo.nextfree = c; |
803 | continue; | ||
804 | LDEBUGF("find_free_cluster(%lx) == %x\n",startcluster,c); | ||
805 | fat_bpb->fsinfo.nextfree = c; | ||
806 | return c; | ||
807 | } | ||
808 | } | ||
809 | offset = 0; | ||
810 | } | 941 | } |
811 | } | 942 | } |
812 | else | ||
813 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
814 | { | ||
815 | sector = startcluster / CLUSTERS_PER_FAT_SECTOR; | ||
816 | offset = startcluster % CLUSTERS_PER_FAT_SECTOR; | ||
817 | 943 | ||
818 | for (i = 0; i<fat_bpb->fatsize; i++) { | 944 | fat_bpb->fsinfo.freecount = free; |
819 | unsigned int j; | 945 | } |
820 | unsigned long nr = (i + sector) % fat_bpb->fatsize; | 946 | #endif /* HAVE_FAT16SUPPORT */ |
821 | uint32_t* fat = cache_fat_sector(IF_MV(fat_bpb,) nr, false); | 947 | |
822 | if ( !fat ) | 948 | static void update_fsinfo32(struct bpb *fat_bpb) |
823 | break; | 949 | { |
824 | for (j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) { | 950 | uint8_t *fsinfo = cache_sector(fat_bpb, fat_bpb->bpb_fsinfo); |
825 | int k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; | 951 | if (!fsinfo) |
826 | if (!(letoh32(fat[k]) & 0x0fffffff)) { | 952 | { |
827 | unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; | 953 | DEBUGF("%s() - Couldn't read FSInfo" |
828 | /* Ignore the reserved clusters 0 & 1, and also | 954 | " (err code %d)", __func__, rc); |
829 | cluster numbers out of bounds */ | 955 | return; |
830 | if ( c < 2 || c > fat_bpb->dataclusters+1 ) | ||
831 | continue; | ||
832 | LDEBUGF("find_free_cluster(%lx) == %lx\n",startcluster,c); | ||
833 | fat_bpb->fsinfo.nextfree = c; | ||
834 | return c; | ||
835 | } | ||
836 | } | ||
837 | offset = 0; | ||
838 | } | ||
839 | } | 956 | } |
840 | 957 | ||
841 | LDEBUGF("find_free_cluster(%lx) == 0\n",startcluster); | 958 | INT322BYTES(fsinfo, FSINFO_FREECOUNT, fat_bpb->fsinfo.freecount); |
842 | return 0; /* 0 is an illegal cluster number */ | 959 | INT322BYTES(fsinfo, FSINFO_NEXTFREE, fat_bpb->fsinfo.nextfree); |
960 | dc_dirty_buf(fsinfo); | ||
843 | } | 961 | } |
844 | 962 | ||
845 | static int update_fat_entry(IF_MV(struct bpb* fat_bpb,) unsigned long entry, | 963 | static long get_next_cluster32(struct bpb *fat_bpb, long startcluster) |
846 | unsigned long val) | ||
847 | { | 964 | { |
848 | #ifndef HAVE_MULTIVOLUME | 965 | unsigned long entry = startcluster; |
849 | struct bpb* fat_bpb = &fat_bpbs[0]; | 966 | unsigned long sector = entry / CLUSTERS_PER_FAT_SECTOR; |
850 | #endif | 967 | unsigned long offset = entry % CLUSTERS_PER_FAT_SECTOR; |
851 | #ifdef HAVE_FAT16SUPPORT | 968 | |
852 | if (fat_bpb->is_fat16) | 969 | dc_lock_cache(); |
970 | |||
971 | uint32_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); | ||
972 | if (!sec) | ||
853 | { | 973 | { |
854 | int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 974 | dc_unlock_cache(); |
855 | int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 975 | DEBUGF("%s: Could not cache sector %d\n", __func__, sector); |
856 | unsigned short* sec; | 976 | return -1; |
977 | } | ||
857 | 978 | ||
858 | val &= 0xFFFF; | 979 | long next = letoh32(sec[offset]) & 0x0fffffff; |
859 | 980 | ||
860 | LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val); | 981 | /* is this last cluster in chain? */ |
982 | if (next >= FAT_EOF_MARK) | ||
983 | next = 0; | ||
861 | 984 | ||
862 | if (entry==val) | 985 | dc_unlock_cache(); |
863 | panicf("Creating FAT loop: %lx,%lx\n",entry,val); | 986 | return next; |
987 | } | ||
864 | 988 | ||
865 | if ( entry < 2 ) | 989 | static long find_free_cluster32(struct bpb *fat_bpb, long startcluster) |
866 | panicf("Updating reserved FAT entry %ld.\n",entry); | 990 | { |
991 | unsigned long entry = startcluster; | ||
992 | unsigned long sector = entry / CLUSTERS_PER_FAT_SECTOR; | ||
993 | unsigned long offset = entry % CLUSTERS_PER_FAT_SECTOR; | ||
867 | 994 | ||
868 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, true); | 995 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) |
996 | { | ||
997 | unsigned long nr = (i + sector) % fat_bpb->fatsize; | ||
998 | uint32_t *sec = cache_sector(fat_bpb, nr + fat_bpb->fatrgnstart); | ||
869 | if (!sec) | 999 | if (!sec) |
1000 | break; | ||
1001 | |||
1002 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) | ||
870 | { | 1003 | { |
871 | DEBUGF( "update_fat_entry() - Could not cache sector %d\n", sector); | 1004 | unsigned long k = (j + offset) % CLUSTERS_PER_FAT_SECTOR; |
872 | return -1; | ||
873 | } | ||
874 | 1005 | ||
875 | if ( val ) { | 1006 | if (!(letoh32(sec[k]) & 0x0fffffff)) |
876 | if (letoh16(sec[offset]) == 0x0000 && fat_bpb->fsinfo.freecount > 0) | 1007 | { |
877 | fat_bpb->fsinfo.freecount--; | 1008 | unsigned long c = nr * CLUSTERS_PER_FAT_SECTOR + k; |
878 | } | 1009 | /* Ignore the reserved clusters 0 & 1, and also |
879 | else { | 1010 | cluster numbers out of bounds */ |
880 | if (letoh16(sec[offset])) | 1011 | if (c < 2 || c > fat_bpb->dataclusters + 1) |
881 | fat_bpb->fsinfo.freecount++; | 1012 | continue; |
1013 | |||
1014 | DEBUGF("%s(%lx) == %lx\n", __func__, startcluster, c); | ||
1015 | |||
1016 | fat_bpb->fsinfo.nextfree = c; | ||
1017 | return c; | ||
1018 | } | ||
882 | } | 1019 | } |
883 | 1020 | ||
884 | LDEBUGF("update_fat_entry: %lu free clusters\n", | 1021 | offset = 0; |
885 | (unsigned long)fat_bpb->fsinfo.freecount); | 1022 | } |
1023 | |||
1024 | DEBUGF("%s(%lx) == 0\n", __func__, startcluster); | ||
1025 | return 0; /* 0 is an illegal cluster number */ | ||
1026 | } | ||
1027 | |||
1028 | static int update_fat_entry32(struct bpb *fat_bpb, unsigned long entry, | ||
1029 | unsigned long val) | ||
1030 | { | ||
1031 | unsigned long sector = entry / CLUSTERS_PER_FAT_SECTOR; | ||
1032 | unsigned long offset = entry % CLUSTERS_PER_FAT_SECTOR; | ||
1033 | |||
1034 | DEBUGF("%s(entry:%lx,val:%lx)\n", __func__, entry, val); | ||
1035 | |||
1036 | if (entry == val) | ||
1037 | panicf("Creating FAT32 loop: %lx,%lx\n", entry, val); | ||
1038 | |||
1039 | if (entry < 2) | ||
1040 | panicf("Updating reserved FAT32 entry %lu\n", entry); | ||
1041 | |||
1042 | dc_lock_cache(); | ||
1043 | |||
1044 | uint32_t *sec = cache_sector(fat_bpb, sector + fat_bpb->fatrgnstart); | ||
1045 | if (!sec) | ||
1046 | { | ||
1047 | dc_unlock_cache(); | ||
1048 | DEBUGF("Could not cache sector %u\n", sector); | ||
1049 | return -1; | ||
1050 | } | ||
886 | 1051 | ||
887 | sec[offset] = htole16(val); | 1052 | uint32_t curval = letoh32(sec[offset]); |
1053 | |||
1054 | if (val) | ||
1055 | { | ||
1056 | /* being allocated */ | ||
1057 | if (!(curval & 0x0fffffff) && fat_bpb->fsinfo.freecount > 0) | ||
1058 | fat_bpb->fsinfo.freecount--; | ||
888 | } | 1059 | } |
889 | else | 1060 | else |
890 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | ||
891 | { | 1061 | { |
892 | long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 1062 | /* being freed */ |
893 | int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 1063 | if (curval & 0x0fffffff) |
894 | uint32_t* sec; | 1064 | fat_bpb->fsinfo.freecount++; |
1065 | } | ||
1066 | |||
1067 | DEBUGF("%lu free clusters\n", (unsigned long)fat_bpb->fsinfo.freecount); | ||
895 | 1068 | ||
896 | LDEBUGF("update_fat_entry(%lx,%lx)\n",entry,val); | 1069 | /* don't change top 4 bits */ |
1070 | sec[offset] = htole32((curval & 0xf0000000) | (val & 0x0fffffff)); | ||
1071 | dc_dirty_buf(sec); | ||
897 | 1072 | ||
898 | if (entry==val) | 1073 | dc_unlock_cache(); |
899 | panicf("Creating FAT loop: %lx,%lx\n",entry,val); | 1074 | return 0; |
1075 | } | ||
900 | 1076 | ||
901 | if ( entry < 2 ) | 1077 | static void fat_recalc_free_internal32(struct bpb *fat_bpb) |
902 | panicf("Updating reserved FAT entry %ld.\n",entry); | 1078 | { |
1079 | unsigned long free = 0; | ||
903 | 1080 | ||
904 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, true); | 1081 | for (unsigned long i = 0; i < fat_bpb->fatsize; i++) |
1082 | { | ||
1083 | uint32_t *sec = cache_sector(fat_bpb, i + fat_bpb->fatrgnstart); | ||
905 | if (!sec) | 1084 | if (!sec) |
1085 | break; | ||
1086 | |||
1087 | for (unsigned long j = 0; j < CLUSTERS_PER_FAT_SECTOR; j++) | ||
906 | { | 1088 | { |
907 | DEBUGF("update_fat_entry() - Could not cache sector %ld\n", sector); | 1089 | unsigned long c = i * CLUSTERS_PER_FAT_SECTOR + j; |
908 | return -1; | ||
909 | } | ||
910 | 1090 | ||
911 | if ( val ) { | 1091 | if (c < 2 || c > fat_bpb->dataclusters + 1) /* nr 0 is unused */ |
912 | if (!(letoh32(sec[offset]) & 0x0fffffff) && | 1092 | continue; |
913 | fat_bpb->fsinfo.freecount > 0) | ||
914 | fat_bpb->fsinfo.freecount--; | ||
915 | } | ||
916 | else { | ||
917 | if (letoh32(sec[offset]) & 0x0fffffff) | ||
918 | fat_bpb->fsinfo.freecount++; | ||
919 | } | ||
920 | 1093 | ||
921 | LDEBUGF("update_fat_entry: %ld free clusters\n", | 1094 | if (letoh32(sec[j]) & 0x0fffffff) |
922 | (unsigned long)fat_bpb->fsinfo.freecount); | 1095 | continue; |
923 | 1096 | ||
924 | /* don't change top 4 bits */ | 1097 | free++; |
925 | sec[offset] &= htole32(0xf0000000); | 1098 | if (fat_bpb->fsinfo.nextfree == 0xffffffff) |
926 | sec[offset] |= htole32(val & 0x0fffffff); | 1099 | fat_bpb->fsinfo.nextfree = c; |
1100 | } | ||
927 | } | 1101 | } |
928 | 1102 | ||
929 | return 0; | 1103 | fat_bpb->fsinfo.freecount = free; |
1104 | update_fsinfo32(fat_bpb); | ||
930 | } | 1105 | } |
931 | 1106 | ||
932 | static long read_fat_entry(IF_MV(struct bpb* fat_bpb,) unsigned long entry) | 1107 | static int fat_mount_internal(struct bpb *fat_bpb) |
933 | { | 1108 | { |
1109 | int rc; | ||
1110 | /* safe to grab buffer: bpb is irrelevant and no sector will be cached | ||
1111 | for this volume since it isn't mounted */ | ||
1112 | uint8_t * const buf = dc_get_buffer(); | ||
1113 | if (!buf) | ||
1114 | FAT_ERROR(-1); | ||
1115 | |||
1116 | /* Read the sector */ | ||
1117 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) fat_bpb->startsector, | ||
1118 | 1, buf); | ||
1119 | if(rc) | ||
1120 | { | ||
1121 | DEBUGF("%s() - Couldn't read BPB" | ||
1122 | " (err %d)\n", __func__, rc); | ||
1123 | FAT_ERROR(rc * 10 - 2); | ||
1124 | } | ||
1125 | |||
1126 | fat_bpb->bpb_bytspersec = BYTES2INT16(buf, BPB_BYTSPERSEC); | ||
1127 | unsigned long secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; | ||
1128 | /* Sanity check is performed later */ | ||
1129 | |||
1130 | fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; | ||
1131 | fat_bpb->bpb_rsvdseccnt = secmult * BYTES2INT16(buf, BPB_RSVDSECCNT); | ||
1132 | fat_bpb->bpb_numfats = buf[BPB_NUMFATS]; | ||
1133 | fat_bpb->bpb_media = buf[BPB_MEDIA]; | ||
1134 | fat_bpb->bpb_fatsz16 = secmult * BYTES2INT16(buf, BPB_FATSZ16); | ||
1135 | fat_bpb->bpb_fatsz32 = secmult * BYTES2INT32(buf, BPB_FATSZ32); | ||
1136 | fat_bpb->bpb_totsec16 = secmult * BYTES2INT16(buf, BPB_TOTSEC16); | ||
1137 | fat_bpb->bpb_totsec32 = secmult * BYTES2INT32(buf, BPB_TOTSEC32); | ||
1138 | fat_bpb->last_word = BYTES2INT16(buf, BPB_LAST_WORD); | ||
1139 | |||
1140 | /* calculate a few commonly used values */ | ||
1141 | if (fat_bpb->bpb_fatsz16 != 0) | ||
1142 | fat_bpb->fatsize = fat_bpb->bpb_fatsz16; | ||
1143 | else | ||
1144 | fat_bpb->fatsize = fat_bpb->bpb_fatsz32; | ||
1145 | |||
1146 | fat_bpb->fatrgnstart = fat_bpb->bpb_rsvdseccnt; | ||
1147 | fat_bpb->fatrgnend = fat_bpb->bpb_rsvdseccnt + fat_bpb->fatsize; | ||
1148 | |||
1149 | if (fat_bpb->bpb_totsec16 != 0) | ||
1150 | fat_bpb->totalsectors = fat_bpb->bpb_totsec16; | ||
1151 | else | ||
1152 | fat_bpb->totalsectors = fat_bpb->bpb_totsec32; | ||
1153 | |||
1154 | unsigned int rootdirsectors = 0; | ||
1155 | #ifdef HAVE_FAT16SUPPORT | ||
1156 | fat_bpb->bpb_rootentcnt = BYTES2INT16(buf, BPB_ROOTENTCNT); | ||
1157 | |||
1158 | if (!fat_bpb->bpb_bytspersec) | ||
1159 | FAT_ERROR(-3); | ||
1160 | |||
1161 | rootdirsectors = secmult * ((fat_bpb->bpb_rootentcnt * DIR_ENTRY_SIZE | ||
1162 | + fat_bpb->bpb_bytspersec - 1) / fat_bpb->bpb_bytspersec); | ||
1163 | #endif /* HAVE_FAT16SUPPORT */ | ||
1164 | |||
1165 | fat_bpb->firstdatasector = fat_bpb->bpb_rsvdseccnt | ||
1166 | + fat_bpb->bpb_numfats * fat_bpb->fatsize | ||
1167 | + rootdirsectors; | ||
1168 | |||
1169 | /* Determine FAT type */ | ||
1170 | unsigned long datasec = fat_bpb->totalsectors - fat_bpb->firstdatasector; | ||
1171 | |||
1172 | if (!fat_bpb->bpb_secperclus) | ||
1173 | FAT_ERROR(-4); | ||
1174 | |||
1175 | fat_bpb->dataclusters = datasec / fat_bpb->bpb_secperclus; | ||
1176 | |||
1177 | #ifdef TEST_FAT | ||
1178 | /* | ||
1179 | we are sometimes testing with "illegally small" fat32 images, | ||
1180 | so we don't use the proper fat32 test case for test code | ||
1181 | */ | ||
1182 | if (fat_bpb->bpb_fatsz16) | ||
1183 | #else /* !TEST_FAT */ | ||
1184 | if (fat_bpb->dataclusters < 65525) | ||
1185 | #endif /* TEST_FAT */ | ||
1186 | { /* FAT16 */ | ||
1187 | #ifdef HAVE_FAT16SUPPORT | ||
1188 | fat_bpb->is_fat16 = true; | ||
1189 | if (fat_bpb->dataclusters < 4085) | ||
1190 | { /* FAT12 */ | ||
1191 | DEBUGF("This is FAT12. Go away!\n"); | ||
1192 | FAT_ERROR(-5); | ||
1193 | } | ||
1194 | #else /* !HAVE_FAT16SUPPORT */ | ||
1195 | DEBUGF("This is not FAT32. Go away!\n"); | ||
1196 | FAT_ERROR(-6); | ||
1197 | #endif /* HAVE_FAT16SUPPORT */ | ||
1198 | } | ||
1199 | |||
934 | #ifdef HAVE_FAT16SUPPORT | 1200 | #ifdef HAVE_FAT16SUPPORT |
935 | #ifndef HAVE_MULTIVOLUME | ||
936 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
937 | #endif | ||
938 | if (fat_bpb->is_fat16) | 1201 | if (fat_bpb->is_fat16) |
939 | { | 1202 | { |
940 | int sector = entry / CLUSTERS_PER_FAT16_SECTOR; | 1203 | /* FAT16 specific part of BPB */ |
941 | int offset = entry % CLUSTERS_PER_FAT16_SECTOR; | 1204 | fat_bpb->rootdirsector = fat_bpb->bpb_rsvdseccnt |
942 | unsigned short* sec; | 1205 | + fat_bpb->bpb_numfats * fat_bpb->bpb_fatsz16; |
1206 | long dirclusters = ((rootdirsectors + fat_bpb->bpb_secperclus - 1) | ||
1207 | / fat_bpb->bpb_secperclus); /* rounded up, to full clusters */ | ||
1208 | /* I assign negative pseudo cluster numbers for the root directory, | ||
1209 | their range is counted upward until -1. */ | ||
1210 | fat_bpb->bpb_rootclus = 0 - dirclusters; /* backwards, before the data */ | ||
1211 | fat_bpb->rootdirsectornum = dirclusters * fat_bpb->bpb_secperclus | ||
1212 | - rootdirsectors; | ||
1213 | } | ||
1214 | else | ||
1215 | #endif /* HAVE_FAT16SUPPORT */ | ||
1216 | { | ||
1217 | /* FAT32 specific part of BPB */ | ||
1218 | fat_bpb->bpb_rootclus = BYTES2INT32(buf, BPB_ROOTCLUS); | ||
1219 | fat_bpb->bpb_fsinfo = secmult * BYTES2INT16(buf, BPB_FSINFO); | ||
1220 | fat_bpb->rootdirsector = cluster2sec(fat_bpb, fat_bpb->bpb_rootclus); | ||
1221 | } | ||
943 | 1222 | ||
944 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, false); | 1223 | rc = bpb_is_sane(fat_bpb); |
945 | if (!sec) | 1224 | if (rc < 0) |
946 | { | 1225 | { |
947 | DEBUGF( "read_fat_entry() - Could not cache sector %d\n", sector); | 1226 | DEBUGF("%s: BPB is insane!\n", __func__); |
948 | return -1; | 1227 | FAT_ERROR(rc * 10 - 7); |
949 | } | 1228 | } |
950 | 1229 | ||
951 | return letoh16(sec[offset]); | 1230 | #ifdef HAVE_FAT16SUPPORT |
1231 | if (fat_bpb->is_fat16) | ||
1232 | { | ||
1233 | fat_bpb->fsinfo.freecount = 0xffffffff; /* force recalc later */ | ||
1234 | fat_bpb->fsinfo.nextfree = 0xffffffff; | ||
952 | } | 1235 | } |
953 | else | 1236 | else |
954 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 1237 | #endif /* HAVE_FAT16SUPPORT */ |
955 | { | 1238 | { |
956 | long sector = entry / CLUSTERS_PER_FAT_SECTOR; | 1239 | /* Read the fsinfo sector */ |
957 | int offset = entry % CLUSTERS_PER_FAT_SECTOR; | 1240 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) |
958 | uint32_t* sec; | 1241 | fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1, buf); |
959 | 1242 | ||
960 | sec = cache_fat_sector(IF_MV(fat_bpb,) sector, false); | 1243 | if (rc < 0) |
961 | if (!sec) | ||
962 | { | 1244 | { |
963 | DEBUGF( "read_fat_entry() - Could not cache sector %ld\n", sector); | 1245 | DEBUGF("%s() - Couldn't read FSInfo" |
964 | return -1; | 1246 | " (error code %d)\n", __func__, rc); |
1247 | FAT_ERROR(rc * 10 - 8); | ||
965 | } | 1248 | } |
966 | 1249 | ||
967 | return letoh32(sec[offset]) & 0x0fffffff; | 1250 | fat_bpb->fsinfo.freecount = BYTES2INT32(buf, FSINFO_FREECOUNT); |
1251 | fat_bpb->fsinfo.nextfree = BYTES2INT32(buf, FSINFO_NEXTFREE); | ||
968 | } | 1252 | } |
969 | } | ||
970 | |||
971 | static long get_next_cluster(IF_MV(struct bpb* fat_bpb,) long cluster) | ||
972 | { | ||
973 | long next_cluster; | ||
974 | long eof_mark = FAT_EOF_MARK; | ||
975 | 1253 | ||
976 | #ifdef HAVE_FAT16SUPPORT | 1254 | #ifdef HAVE_FAT16SUPPORT |
977 | #ifndef HAVE_MULTIVOLUME | 1255 | /* Fix up calls that change per FAT type */ |
978 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
979 | #endif | ||
980 | if (fat_bpb->is_fat16) | 1256 | if (fat_bpb->is_fat16) |
981 | { | 1257 | { |
982 | eof_mark &= 0xFFFF; /* only 16 bit */ | 1258 | BPB_FN_SET16(fat_bpb, get_next_cluster); |
983 | if (cluster < 0) /* FAT16 root dir */ | 1259 | BPB_FN_SET16(fat_bpb, find_free_cluster); |
984 | return cluster + 1; /* don't use the FAT */ | 1260 | BPB_FN_SET16(fat_bpb, update_fat_entry); |
1261 | BPB_FN_SET16(fat_bpb, fat_recalc_free_internal); | ||
985 | } | 1262 | } |
986 | #endif | ||
987 | next_cluster = read_fat_entry(IF_MV(fat_bpb,) cluster); | ||
988 | |||
989 | /* is this last cluster in chain? */ | ||
990 | if ( next_cluster >= eof_mark ) | ||
991 | return 0; | ||
992 | else | 1263 | else |
993 | return next_cluster; | 1264 | { |
1265 | BPB_FN_SET32(fat_bpb, get_next_cluster); | ||
1266 | BPB_FN_SET32(fat_bpb, find_free_cluster); | ||
1267 | BPB_FN_SET32(fat_bpb, update_fat_entry); | ||
1268 | BPB_FN_SET32(fat_bpb, fat_recalc_free_internal); | ||
1269 | } | ||
1270 | #endif /* HAVE_FAT16SUPPORT */ | ||
1271 | |||
1272 | rc = 0; | ||
1273 | fat_error: | ||
1274 | dc_release_buffer(buf); | ||
1275 | return rc; | ||
994 | } | 1276 | } |
995 | 1277 | ||
996 | static int update_fsinfo(IF_MV_NONVOID(struct bpb* fat_bpb)) | 1278 | static union raw_dirent * cache_direntry(struct bpb *fat_bpb, |
1279 | struct fat_filestr *filestr, | ||
1280 | unsigned int entry) | ||
997 | { | 1281 | { |
998 | #ifndef HAVE_MULTIVOLUME | 1282 | filestr->eof = false; |
999 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1000 | #endif | ||
1001 | uint32_t* intptr; | ||
1002 | int rc; | ||
1003 | 1283 | ||
1004 | #ifdef HAVE_FAT16SUPPORT | 1284 | if (entry >= MAX_DIRENTRIES) |
1005 | if (fat_bpb->is_fat16) | 1285 | { |
1006 | return 0; /* FAT16 has no FsInfo */ | 1286 | DEBUGF("%s() - Dir is too large (entry %u)\n", __func__, entry); |
1007 | #endif /* #ifdef HAVE_FAT16SUPPORT */ | 1287 | return NULL; |
1288 | } | ||
1008 | 1289 | ||
1009 | unsigned char* fsinfo = fat_get_sector_buffer(); | 1290 | unsigned long sector = entry / DIR_ENTRIES_PER_SECTOR; |
1010 | /* update fsinfo */ | 1291 | |
1011 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) | 1292 | if (fat_query_sectornum(filestr) != sector + 1) |
1012 | fat_bpb->startsector + fat_bpb->bpb_fsinfo, 1,fsinfo); | ||
1013 | if (rc < 0) | ||
1014 | { | 1293 | { |
1015 | fat_release_sector_buffer(); | 1294 | int rc = fat_seek(filestr, sector + 1); |
1016 | DEBUGF( "update_fsinfo() - Couldn't read FSInfo (error code %d)", rc); | 1295 | if (rc < 0) |
1017 | return rc * 10 - 1; | 1296 | { |
1297 | if (rc == FAT_SEEK_EOF) | ||
1298 | { | ||
1299 | DEBUGF("%s() - End of dir (entry %u)\n", __func__, entry); | ||
1300 | fat_seek(filestr, sector); | ||
1301 | filestr->eof = true; | ||
1302 | } | ||
1303 | |||
1304 | return NULL; | ||
1305 | } | ||
1018 | } | 1306 | } |
1019 | intptr = (uint32_t*)&(fsinfo[FSINFO_FREECOUNT]); | ||
1020 | *intptr = htole32(fat_bpb->fsinfo.freecount); | ||
1021 | 1307 | ||
1022 | intptr = (uint32_t*)&(fsinfo[FSINFO_NEXTFREE]); | 1308 | union raw_dirent *ent = cache_sector(fat_bpb, filestr->lastsector); |
1023 | *intptr = htole32(fat_bpb->fsinfo.nextfree); | ||
1024 | 1309 | ||
1025 | rc = storage_write_sectors(IF_MD(fat_bpb->drive,) | 1310 | if (ent) |
1026 | fat_bpb->startsector + fat_bpb->bpb_fsinfo,1,fsinfo); | 1311 | ent += entry % DIR_ENTRIES_PER_SECTOR; |
1027 | fat_release_sector_buffer(); | 1312 | |
1028 | if (rc < 0) | 1313 | return ent; |
1314 | } | ||
1315 | |||
1316 | static long next_write_cluster(struct bpb *fat_bpb, long oldcluster) | ||
1317 | { | ||
1318 | DEBUGF("%s(old:%lx)\n", __func__, oldcluster); | ||
1319 | |||
1320 | long cluster = 0; | ||
1321 | |||
1322 | /* cluster already allocated? */ | ||
1323 | if (oldcluster) | ||
1324 | cluster = get_next_cluster(fat_bpb, oldcluster); | ||
1325 | |||
1326 | if (!cluster) | ||
1029 | { | 1327 | { |
1030 | DEBUGF( "update_fsinfo() - Couldn't write FSInfo (error code %d)", rc); | 1328 | /* passed end of existing entries and now need to append */ |
1031 | return rc * 10 - 2; | 1329 | #ifdef HAVE_FAT16SUPPORT |
1330 | if (UNLIKELY(oldcluster < 0)) | ||
1331 | return 0; /* negative, pseudo-cluster of the root dir */ | ||
1332 | /* impossible to append something to the root */ | ||
1333 | #endif /* HAVE_FAT16SUPPORT */ | ||
1334 | |||
1335 | dc_lock_cache(); | ||
1336 | |||
1337 | long findstart = oldcluster > 0 ? | ||
1338 | oldcluster + 1 : (long)fat_bpb->fsinfo.nextfree; | ||
1339 | |||
1340 | cluster = find_free_cluster(fat_bpb, findstart); | ||
1341 | |||
1342 | if (cluster) | ||
1343 | { | ||
1344 | /* create the cluster chain */ | ||
1345 | if (oldcluster) | ||
1346 | update_fat_entry(fat_bpb, oldcluster, cluster); | ||
1347 | |||
1348 | update_fat_entry(fat_bpb, cluster, FAT_EOF_MARK); | ||
1349 | } | ||
1350 | else | ||
1351 | { | ||
1352 | #ifdef TEST_FAT | ||
1353 | if (fat_bpb->fsinfo.freecount > 0) | ||
1354 | panicf("There is free space, but find_free_cluster() " | ||
1355 | "didn't find it!\n"); | ||
1356 | #endif | ||
1357 | DEBUGF("Disk full!\n"); | ||
1358 | } | ||
1359 | |||
1360 | dc_unlock_cache(); | ||
1032 | } | 1361 | } |
1033 | 1362 | ||
1034 | return 0; | 1363 | return cluster; |
1035 | } | 1364 | } |
1036 | 1365 | ||
1037 | static int flush_fat(IF_MV_NONVOID(struct bpb* fat_bpb)) | 1366 | /* extend dir file by one cluster and clear it; file position should be at the |
1367 | current last cluster before calling and size of dir checked */ | ||
1368 | static int fat_extend_dir(struct bpb *fat_bpb, struct fat_filestr *dirstr) | ||
1038 | { | 1369 | { |
1039 | int i; | 1370 | DEBUGF("%s()\n", __func__); |
1371 | |||
1040 | int rc; | 1372 | int rc; |
1041 | unsigned char *sec; | ||
1042 | LDEBUGF("flush_fat()\n"); | ||
1043 | 1373 | ||
1044 | mutex_lock(&cache_mutex); | 1374 | long cluster = dirstr->lastcluster; |
1045 | for(i = 0;i < FAT_CACHE_SIZE;i++) | 1375 | long newcluster = next_write_cluster(fat_bpb, cluster); |
1376 | |||
1377 | if (!newcluster) | ||
1046 | { | 1378 | { |
1047 | struct fat_cache_entry *fce = &fat_cache[i]; | 1379 | /* no more room or something went wrong */ |
1048 | if(fce->inuse | 1380 | DEBUGF("Out of space\n"); |
1049 | #ifdef HAVE_MULTIVOLUME | 1381 | FAT_ERROR(FAT_RC_ENOSPC); |
1050 | && fce->fat_vol == fat_bpb | 1382 | } |
1051 | #endif | 1383 | |
1052 | && fce->dirty) | 1384 | /* we must clear whole clusters */ |
1385 | unsigned long startsector = cluster2sec(fat_bpb, newcluster); | ||
1386 | unsigned long sector = startsector - 1; | ||
1387 | |||
1388 | if (startsector == 0) | ||
1389 | FAT_ERROR(-1); | ||
1390 | |||
1391 | for (unsigned int i = 0; i < fat_bpb->bpb_secperclus; i++) | ||
1392 | { | ||
1393 | dc_lock_cache(); | ||
1394 | |||
1395 | void *sec = cache_sector_buffer(IF_MV(fat_bpb,) ++sector); | ||
1396 | if (!sec) | ||
1053 | { | 1397 | { |
1054 | sec = fat_cache_sectors[i]; | 1398 | dc_unlock_cache(); |
1055 | flush_fat_sector(fce, sec); | 1399 | DEBUGF("Cannot clear cluster %ld\n", newcluster); |
1400 | update_fat_entry(fat_bpb, newcluster, 0); | ||
1401 | FAT_ERROR(-2); | ||
1056 | } | 1402 | } |
1403 | |||
1404 | memset(sec, 0, SECTOR_SIZE); | ||
1405 | dc_dirty_buf(sec); | ||
1406 | dc_unlock_cache(); | ||
1057 | } | 1407 | } |
1058 | mutex_unlock(&cache_mutex); | ||
1059 | 1408 | ||
1060 | rc = update_fsinfo(IF_MV(fat_bpb)); | 1409 | if (!dirstr->fatfilep->firstcluster) |
1061 | if (rc < 0) | 1410 | dirstr->fatfilep->firstcluster = newcluster; |
1062 | return rc * 10 - 3; | ||
1063 | 1411 | ||
1064 | return 0; | 1412 | dirstr->lastcluster = newcluster; |
1413 | dirstr->lastsector = sector; | ||
1414 | dirstr->clusternum++; | ||
1415 | dirstr->sectornum = sector - startsector; | ||
1416 | dirstr->eof = false; | ||
1417 | |||
1418 | rc = 0; | ||
1419 | fat_error: | ||
1420 | return rc; | ||
1065 | } | 1421 | } |
1066 | 1422 | ||
1067 | static void fat_time(unsigned short* date, | 1423 | static void fat_open_internal(IF_MV(int volume,) long startcluster, |
1068 | unsigned short* time, | 1424 | struct fat_file *file) |
1069 | unsigned short* tenth ) | ||
1070 | { | 1425 | { |
1426 | #ifdef HAVE_MULTIVOLUME | ||
1427 | file->volume = volume; | ||
1428 | #endif | ||
1429 | file->firstcluster = startcluster; | ||
1430 | file->dircluster = 0; | ||
1431 | file->e.entry = 0; | ||
1432 | file->e.entries = 0; | ||
1433 | } | ||
1434 | |||
1071 | #if CONFIG_RTC | 1435 | #if CONFIG_RTC |
1072 | struct tm* tm = get_time(); | 1436 | static void fat_time(uint16_t *date, uint16_t *time, int16_t *tenth) |
1437 | { | ||
1438 | struct tm *tm = get_time(); | ||
1073 | 1439 | ||
1074 | if (date) | 1440 | if (date) |
1441 | { | ||
1075 | *date = ((tm->tm_year - 80) << 9) | | 1442 | *date = ((tm->tm_year - 80) << 9) | |
1076 | ((tm->tm_mon + 1) << 5) | | 1443 | ((tm->tm_mon + 1) << 5) | |
1077 | tm->tm_mday; | 1444 | tm->tm_mday; |
1445 | } | ||
1078 | 1446 | ||
1079 | if (time) | 1447 | if (time) |
1448 | { | ||
1080 | *time = (tm->tm_hour << 11) | | 1449 | *time = (tm->tm_hour << 11) | |
1081 | (tm->tm_min << 5) | | 1450 | (tm->tm_min << 5) | |
1082 | (tm->tm_sec >> 1); | 1451 | (tm->tm_sec >> 1); |
1452 | } | ||
1083 | 1453 | ||
1084 | if (tenth) | 1454 | if (tenth) |
1085 | *tenth = (tm->tm_sec & 1) * 100; | 1455 | *tenth = (tm->tm_sec & 1) * 100; |
1086 | #else | 1456 | } |
1457 | |||
1458 | #else /* !CONFIG_RTC */ | ||
1459 | |||
1460 | static void fat_time(uint16_t *date, uint16_t *time, int16_t *tenth) | ||
1461 | { | ||
1087 | /* non-RTC version returns an increment from the supplied time, or a | 1462 | /* non-RTC version returns an increment from the supplied time, or a |
1088 | * fixed standard time/date if no time given as input */ | 1463 | * fixed standard time/date if no time given as input */ |
1089 | 1464 | ||
1090 | /* Macros to convert a 2-digit string to a decimal constant. | 1465 | /* Macros to convert a 2-digit string to a decimal constant. |
1091 | (YEAR), MONTH and DAY are set by the date command, which outputs | 1466 | (YEAR), MONTH and DAY are set by the date command, which outputs |
1092 | DAY as 00..31 and MONTH as 01..12. The leading zero would lead to | 1467 | DAY as 00..31 and MONTH as 01..12. The leading zero would lead to |
1093 | misinterpretation as an octal constant. */ | 1468 | misinterpretation as an octal constant. */ |
1094 | #define S100(x) 1 ## x | 1469 | #define S100(x) 1 ## x |
1095 | #define C2DIG2DEC(x) (S100(x)-100) | 1470 | #define C2DIG2DEC(x) (S100(x)-100) |
1096 | /* The actual build date, as FAT date constant */ | 1471 | /* The actual build date, as FAT date constant */ |
1097 | #define BUILD_DATE_FAT (((YEAR - 1980) << 9) \ | 1472 | #define BUILD_DATE_FAT (((YEAR - 1980) << 9) \ |
1098 | | (C2DIG2DEC(MONTH) << 5) \ | 1473 | | (C2DIG2DEC(MONTH) << 5) \ |
1099 | | C2DIG2DEC(DAY)) | 1474 | | C2DIG2DEC(DAY)) |
1100 | 1475 | ||
1101 | bool date_forced = false; | 1476 | bool date_forced = false; |
1102 | bool next_day = false; | 1477 | bool next_day = false; |
@@ -1107,7 +1482,7 @@ static void fat_time(unsigned short* date, | |||
1107 | *date = BUILD_DATE_FAT; | 1482 | *date = BUILD_DATE_FAT; |
1108 | date_forced = true; | 1483 | date_forced = true; |
1109 | } | 1484 | } |
1110 | 1485 | ||
1111 | if (time) | 1486 | if (time) |
1112 | { | 1487 | { |
1113 | time2 = *time << 1; | 1488 | time2 = *time << 1; |
@@ -1119,8 +1494,8 @@ static void fat_time(unsigned short* date, | |||
1119 | { | 1494 | { |
1120 | unsigned mins = (time2 >> 6) & 0x3f; | 1495 | unsigned mins = (time2 >> 6) & 0x3f; |
1121 | unsigned hours = (time2 >> 12) & 0x1f; | 1496 | unsigned hours = (time2 >> 12) & 0x1f; |
1122 | 1497 | ||
1123 | mins = 11 * ((mins/11) + 1); /* advance to next multiple of 11 */ | 1498 | mins = 11 * ((mins / 11) + 1); /* advance to next multiple of 11 */ |
1124 | if (mins > 59) | 1499 | if (mins > 59) |
1125 | { | 1500 | { |
1126 | mins = 11; /* 00 would be a bad marker */ | 1501 | mins = 11; /* 00 would be a bad marker */ |
@@ -1134,14 +1509,14 @@ static void fat_time(unsigned short* date, | |||
1134 | } | 1509 | } |
1135 | *time = time2 >> 1; | 1510 | *time = time2 >> 1; |
1136 | } | 1511 | } |
1137 | 1512 | ||
1138 | if (tenth) | 1513 | if (tenth) |
1139 | *tenth = (time2 & 1) * 100; | 1514 | *tenth = (time2 & 1) * 100; |
1140 | 1515 | ||
1141 | if (date && next_day) | 1516 | if (date && next_day) |
1142 | { | 1517 | { |
1143 | static const unsigned char daysinmonth[] = | 1518 | static const unsigned char daysinmonth[] = |
1144 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; | 1519 | { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; |
1145 | unsigned day = *date & 0x1f; | 1520 | unsigned day = *date & 0x1f; |
1146 | unsigned month = (*date >> 5) & 0x0f; | 1521 | unsigned month = (*date >> 5) & 0x0f; |
1147 | unsigned year = (*date >> 9) & 0x7f; | 1522 | unsigned year = (*date >> 9) & 0x7f; |
@@ -1156,1514 +1531,1393 @@ static void fat_time(unsigned short* date, | |||
1156 | year++; | 1531 | year++; |
1157 | } | 1532 | } |
1158 | } | 1533 | } |
1534 | |||
1159 | *date = (year << 9) | (month << 5) | day; | 1535 | *date = (year << 9) | (month << 5) | day; |
1160 | } | 1536 | } |
1161 | |||
1162 | #endif /* CONFIG_RTC */ | ||
1163 | } | 1537 | } |
1538 | #endif /* CONFIG_RTC */ | ||
1164 | 1539 | ||
1165 | static int write_long_name(struct fat_file* file, | 1540 | static int write_longname(struct bpb *fat_bpb, struct fat_filestr *parentstr, |
1166 | unsigned int firstentry, | 1541 | struct fat_file *file, const unsigned char *name, |
1167 | unsigned int numentries, | 1542 | unsigned long ucslen, const unsigned char *shortname, |
1168 | const unsigned char* name, | 1543 | union raw_dirent *srcent, uint8_t attr, |
1169 | const unsigned char* shortname, | 1544 | unsigned int flags) |
1170 | bool is_directory) | ||
1171 | { | 1545 | { |
1172 | unsigned char* entry; | 1546 | DEBUGF("%s(file:%lx, first:%d, num:%d, name:%s)\n", __func__, |
1173 | unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR; | 1547 | parent->info->firstcluster, firstentry, numentries, name); |
1174 | unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 1548 | |
1175 | unsigned char chksum = 0; | ||
1176 | unsigned int i, j=0; | ||
1177 | unsigned int nameidx=0, namelen = utf8length(name); | ||
1178 | int rc; | 1549 | int rc; |
1179 | unsigned short name_utf16[namelen + 1]; | 1550 | union raw_dirent *ent; |
1551 | |||
1552 | uint16_t date = 0, time = 0, tenth = 0; | ||
1553 | fat_time(&date, &time, &tenth); | ||
1554 | time = htole16(time); | ||
1555 | date = htole16(date); | ||
1180 | 1556 | ||
1181 | LDEBUGF("write_long_name(file:%lx, first:%d, num:%d, name:%s)\n", | 1557 | /* shortname checksum saved in each longname entry */ |
1182 | file->firstcluster, firstentry, numentries, name); | 1558 | uint8_t chksum = shortname_checksum(shortname); |
1183 | 1559 | ||
1184 | rc = fat_seek(file, sector); | 1560 | /* we need to convert the name first since the entries are written in |
1185 | if (rc<0) | 1561 | reverse order */ |
1186 | return rc * 10 - 1; | 1562 | unsigned long ucspadlen = ALIGN_UP(ucslen, FATLONG_NAME_CHARS); |
1563 | uint16_t ucsname[ucspadlen]; | ||
1187 | 1564 | ||
1188 | unsigned char* buf = fat_get_sector_buffer(); | 1565 | for (unsigned long i = 0; i < ucspadlen; i++) |
1189 | rc = fat_readwrite(file, 1, buf, false); | ||
1190 | if (rc<1) | ||
1191 | { | 1566 | { |
1192 | fat_release_sector_buffer(); | 1567 | if (i < ucslen) |
1193 | return rc * 10 - 2; | 1568 | name = utf8decode(name, &ucsname[i]); |
1569 | else if (i == ucslen) | ||
1570 | ucsname[i] = 0x0000; /* name doesn't fill last block */ | ||
1571 | else /* i > ucslen */ | ||
1572 | ucsname[i] = 0xffff; /* pad-out to end */ | ||
1194 | } | 1573 | } |
1195 | 1574 | ||
1196 | /* calculate shortname checksum */ | 1575 | dc_lock_cache(); |
1197 | for (i=11; i>0; i--) | ||
1198 | chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; | ||
1199 | |||
1200 | /* calc position of last name segment */ | ||
1201 | if ( namelen > NAME_BYTES_PER_ENTRY ) | ||
1202 | for (nameidx=0; | ||
1203 | nameidx < (namelen - NAME_BYTES_PER_ENTRY); | ||
1204 | nameidx += NAME_BYTES_PER_ENTRY); | ||
1205 | |||
1206 | /* we need to convert the name first */ | ||
1207 | /* since it is written in reverse order */ | ||
1208 | for (i = 0; i <= namelen; i++) | ||
1209 | name = utf8decode(name, &name_utf16[i]); | ||
1210 | |||
1211 | for (i=0; i < numentries; i++) { | ||
1212 | /* new sector? */ | ||
1213 | if ( idx >= DIR_ENTRIES_PER_SECTOR ) { | ||
1214 | /* update current sector */ | ||
1215 | rc = fat_seek(file, sector); | ||
1216 | if (rc<0) | ||
1217 | { | ||
1218 | fat_release_sector_buffer(); | ||
1219 | return rc * 10 - 3; | ||
1220 | } | ||
1221 | |||
1222 | rc = fat_readwrite(file, 1, buf, true); | ||
1223 | if (rc<1) | ||
1224 | { | ||
1225 | fat_release_sector_buffer(); | ||
1226 | return rc * 10 - 4; | ||
1227 | } | ||
1228 | |||
1229 | /* read next sector */ | ||
1230 | rc = fat_readwrite(file, 1, buf, false); | ||
1231 | if (rc<0) { | ||
1232 | fat_release_sector_buffer(); | ||
1233 | LDEBUGF("Failed writing new sector: %d\n",rc); | ||
1234 | return rc * 10 - 5; | ||
1235 | } | ||
1236 | if (rc==0) | ||
1237 | /* end of dir */ | ||
1238 | memset(buf, 0, SECTOR_SIZE); | ||
1239 | 1576 | ||
1240 | sector++; | 1577 | const unsigned int longentries = file->e.entries - 1; |
1241 | idx = 0; | 1578 | const unsigned int firstentry = file->e.entry - longentries; |
1242 | } | ||
1243 | 1579 | ||
1244 | entry = buf + idx * DIR_ENTRY_SIZE; | 1580 | /* longame entries */ |
1581 | for (unsigned int i = 0; i < longentries; i++) | ||
1582 | { | ||
1583 | ent = cache_direntry(fat_bpb, parentstr, firstentry + i); | ||
1584 | if (!ent) | ||
1585 | FAT_ERROR(-2); | ||
1245 | 1586 | ||
1246 | /* verify this entry is free */ | 1587 | /* verify this entry is free */ |
1247 | if (entry[0] && entry[0] != 0xe5 ) | 1588 | if (ent->name[0] && ent->name[0] != 0xe5) |
1248 | { | 1589 | { |
1249 | fat_release_sector_buffer(); | ||
1250 | panicf("Dir entry %d in sector %x is not free! " | 1590 | panicf("Dir entry %d in sector %x is not free! " |
1251 | "%02x %02x %02x %02x", | 1591 | "%02x %02x %02x %02x", |
1252 | idx, sector, | 1592 | i + firstentry, (unsigned)parentstr->lastsector, |
1253 | entry[0], entry[1], entry[2], entry[3]); | 1593 | (unsigned)ent->data[0], (unsigned)ent->data[1], |
1594 | (unsigned)ent->data[2], (unsigned)ent->data[3]); | ||
1254 | } | 1595 | } |
1255 | 1596 | ||
1256 | memset(entry, 0, DIR_ENTRY_SIZE); | 1597 | memset(ent->data, 0, DIR_ENTRY_SIZE); |
1257 | if ( i+1 < numentries ) { | ||
1258 | /* longname entry */ | ||
1259 | unsigned int k, l = nameidx; | ||
1260 | |||
1261 | entry[FATLONG_ORDER] = numentries-i-1; | ||
1262 | if (i==0) { | ||
1263 | /* mark this as last long entry */ | ||
1264 | entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY; | ||
1265 | |||
1266 | /* pad name with 0xffff */ | ||
1267 | for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | ||
1268 | for (k=14; k<26; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | ||
1269 | for (k=28; k<32; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; | ||
1270 | }; | ||
1271 | /* set name */ | ||
1272 | for (k=0; k<5 && l <= namelen; k++) { | ||
1273 | entry[k*2 + 1] = (unsigned char)(name_utf16[l] & 0xff); | ||
1274 | entry[k*2 + 2] = (unsigned char)(name_utf16[l++] >> 8); | ||
1275 | } | ||
1276 | for (k=0; k<6 && l <= namelen; k++) { | ||
1277 | entry[k*2 + 14] = (unsigned char)(name_utf16[l] & 0xff); | ||
1278 | entry[k*2 + 15] = (unsigned char)(name_utf16[l++] >> 8); | ||
1279 | } | ||
1280 | for (k=0; k<2 && l <= namelen; k++) { | ||
1281 | entry[k*2 + 28] = (unsigned char)(name_utf16[l] & 0xff); | ||
1282 | entry[k*2 + 29] = (unsigned char)(name_utf16[l++] >> 8); | ||
1283 | } | ||
1284 | 1598 | ||
1285 | entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME; | 1599 | unsigned int ord = longentries - i; |
1286 | entry[FATDIR_FSTCLUSLO] = 0; | 1600 | |
1287 | entry[FATLONG_TYPE] = 0; | 1601 | ent->ldir_ord = ord | (i == 0 ? FATLONG_ORD_F_LAST : 0); |
1288 | entry[FATLONG_CHKSUM] = chksum; | 1602 | ent->ldir_attr = ATTR_LONG_NAME; |
1289 | LDEBUGF("Longname entry %d: %s\n", idx, name+nameidx); | 1603 | ent->ldir_chksum = chksum; |
1290 | } | 1604 | |
1291 | else { | 1605 | /* set name */ |
1292 | /* shortname entry */ | 1606 | uint16_t *ucsptr = &ucsname[(ord - 1) * FATLONG_NAME_CHARS]; |
1293 | unsigned short date=0, time=0, tenth=0; | 1607 | for (unsigned j = longent_char_first(); j; j = longent_char_next(j)) |
1294 | LDEBUGF("Shortname entry: %s\n", shortname); | 1608 | { |
1295 | memcpy(entry + FATDIR_NAME, shortname, 11); | 1609 | uint16_t ucs = *ucsptr++; |
1296 | entry[FATDIR_ATTR] = is_directory?FAT_ATTR_DIRECTORY:0; | 1610 | INT162BYTES(ent->data, j, ucs); |
1297 | entry[FATDIR_NTRES] = 0; | ||
1298 | |||
1299 | fat_time(&date, &time, &tenth); | ||
1300 | entry[FATDIR_CRTTIMETENTH] = tenth; | ||
1301 | *(unsigned short*)(entry + FATDIR_CRTTIME) = htole16(time); | ||
1302 | *(unsigned short*)(entry + FATDIR_WRTTIME) = htole16(time); | ||
1303 | *(unsigned short*)(entry + FATDIR_CRTDATE) = htole16(date); | ||
1304 | *(unsigned short*)(entry + FATDIR_WRTDATE) = htole16(date); | ||
1305 | *(unsigned short*)(entry + FATDIR_LSTACCDATE) = htole16(date); | ||
1306 | } | 1611 | } |
1307 | idx++; | 1612 | |
1308 | nameidx -= NAME_BYTES_PER_ENTRY; | 1613 | dc_dirty_buf(ent); |
1614 | DEBUGF("Longname entry %d\n", ord); | ||
1309 | } | 1615 | } |
1310 | 1616 | ||
1311 | /* update last sector */ | 1617 | /* shortname entry */ |
1312 | rc = fat_seek(file, sector); | 1618 | DEBUGF("Shortname '%s'\n", shortname); |
1313 | if (rc<0) | 1619 | |
1620 | ent = cache_direntry(fat_bpb, parentstr, file->e.entry); | ||
1621 | if (!ent) | ||
1622 | FAT_ERROR(-2); | ||
1623 | |||
1624 | if (srcent && (flags & DIRENT_TEMPL)) | ||
1314 | { | 1625 | { |
1315 | fat_release_sector_buffer(); | 1626 | /* srcent points to short entry template */ |
1316 | return rc * 10 - 6; | 1627 | *ent = *srcent; |
1628 | } | ||
1629 | else | ||
1630 | { | ||
1631 | /* make our own short entry */ | ||
1632 | memset(ent->data, 0, DIR_ENTRY_SIZE); | ||
1633 | ent->attr = attr; | ||
1317 | } | 1634 | } |
1318 | 1635 | ||
1319 | rc = fat_readwrite(file, 1, buf, true); | 1636 | /* short name may change even if just moving */ |
1320 | fat_release_sector_buffer(); | 1637 | memcpy(ent->name, shortname, 11); |
1321 | if (rc<1) | 1638 | raw_dirent_set_fstclus(ent, file->firstcluster); |
1322 | return rc * 10 - 7; | ||
1323 | 1639 | ||
1324 | return 0; | 1640 | if (!(flags & DIRENT_TEMPL_CRT)) |
1325 | } | ||
1326 | |||
1327 | static int fat_checkname(const unsigned char* newname) | ||
1328 | { | ||
1329 | static const char invalid_chars[] = "\"*/:<>?\\|"; | ||
1330 | int len = strlen(newname); | ||
1331 | /* More sanity checks are probably needed */ | ||
1332 | if (len > 255 || newname[len - 1] == '.') | ||
1333 | { | 1641 | { |
1334 | return -1; | 1642 | ent->crttimetenth = tenth; |
1643 | ent->crttime = time; | ||
1644 | ent->crtdate = date; | ||
1335 | } | 1645 | } |
1336 | while (*newname) | 1646 | |
1647 | if (!(flags & DIRENT_TEMPL_WRT)) | ||
1337 | { | 1648 | { |
1338 | if (*newname < ' ' || strchr(invalid_chars, *newname) != NULL) | 1649 | ent->wrttime = time; |
1339 | return -1; | 1650 | ent->wrtdate = date; |
1340 | newname++; | ||
1341 | } | 1651 | } |
1342 | /* check trailing space(s) */ | ||
1343 | if(*(--newname) == ' ') | ||
1344 | return -1; | ||
1345 | 1652 | ||
1346 | return 0; | 1653 | if (!(flags & DIRENT_TEMPL_ACC)) |
1654 | ent->lstaccdate = date; | ||
1655 | |||
1656 | if (srcent && (flags & DIRENT_RETURN)) | ||
1657 | *srcent = *ent; /* caller wants */ | ||
1658 | |||
1659 | dc_dirty_buf(ent); | ||
1660 | |||
1661 | rc = 0; | ||
1662 | fat_error: | ||
1663 | dc_unlock_cache(); | ||
1664 | return rc; | ||
1347 | } | 1665 | } |
1348 | 1666 | ||
1349 | static int add_dir_entry(struct fat_dir* dir, | 1667 | static int add_dir_entry(struct bpb *fat_bpb, struct fat_filestr *parentstr, |
1350 | struct fat_file* file, | 1668 | struct fat_file *file, const char *name, |
1351 | const char* name, | 1669 | union raw_dirent *srcent, uint8_t attr, |
1352 | bool is_directory, | 1670 | unsigned int flags) |
1353 | bool dotdir) | ||
1354 | { | 1671 | { |
1355 | #ifdef HAVE_MULTIVOLUME | 1672 | DEBUGF("%s(name:\"%s\",first:%lx)\n", __func__, name, |
1356 | struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | 1673 | file->firstcluster); |
1357 | #else | ||
1358 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1359 | #endif | ||
1360 | unsigned char shortname[12]; | ||
1361 | int rc; | ||
1362 | unsigned int sector; | ||
1363 | bool done = false; | ||
1364 | int entries_needed, entries_found = 0; | ||
1365 | int firstentry; | ||
1366 | |||
1367 | LDEBUGF( "add_dir_entry(%s,%lx)\n", | ||
1368 | name, file->firstcluster); | ||
1369 | |||
1370 | /* Don't check dotdirs name for validity */ | ||
1371 | if (dotdir == false) { | ||
1372 | rc = fat_checkname(name); | ||
1373 | if (rc < 0) { | ||
1374 | /* filename is invalid */ | ||
1375 | return rc * 10 - 1; | ||
1376 | } | ||
1377 | } | ||
1378 | 1674 | ||
1379 | #ifdef HAVE_MULTIVOLUME | 1675 | int rc; |
1380 | file->volume = dir->file.volume; /* inherit the volume, to make sure */ | ||
1381 | #endif | ||
1382 | 1676 | ||
1383 | /* The "." and ".." directory entries must not be long names */ | 1677 | unsigned char basisname[11], shortname[11]; |
1384 | if(dotdir) { | 1678 | int n; |
1385 | int i; | 1679 | int entries_needed; |
1386 | strlcpy(shortname, name, 12); | 1680 | unsigned long ucslen = 0; |
1387 | for(i = strlen(shortname); i < 12; i++) | ||
1388 | shortname[i] = ' '; | ||
1389 | 1681 | ||
1682 | if (is_dotdir_name(name) && (attr & ATTR_DIRECTORY)) | ||
1683 | { | ||
1684 | /* The "." and ".." directory entries must not be long names */ | ||
1685 | int dots = strlcpy(shortname, name, 11); | ||
1686 | memset(&shortname[dots], ' ', 11 - dots); | ||
1390 | entries_needed = 1; | 1687 | entries_needed = 1; |
1391 | } else { | 1688 | } |
1392 | create_dos_name(name, shortname); | 1689 | else |
1690 | { | ||
1691 | rc = check_longname(name); | ||
1692 | if (rc < 0) | ||
1693 | FAT_ERROR(rc * 10 - 1); /* filename is invalid */ | ||
1694 | |||
1695 | create_dos_name(basisname, name, &n); | ||
1696 | randomize_dos_name(shortname, basisname, &n); | ||
1393 | 1697 | ||
1394 | /* one dir entry needed for every 13 bytes of filename, | 1698 | /* one dir entry needed for every 13 characters of filename, |
1395 | plus one entry for the short name */ | 1699 | plus one entry for the short name */ |
1396 | entries_needed = (utf8length(name) + (NAME_BYTES_PER_ENTRY-1)) | 1700 | ucslen = utf8length(name); |
1397 | / NAME_BYTES_PER_ENTRY + 1; | 1701 | if (ucslen > 255) |
1702 | FAT_ERROR(-2); /* name is too long */ | ||
1703 | |||
1704 | entries_needed = (ucslen + FATLONG_NAME_CHARS - 1) | ||
1705 | / FATLONG_NAME_CHARS + 1; | ||
1398 | } | 1706 | } |
1399 | 1707 | ||
1400 | unsigned char* buf = fat_get_sector_buffer(); | 1708 | int entry = 0, entries_found = 0, firstentry = -1; |
1401 | restart: | 1709 | const int entperclus = DIR_ENTRIES_PER_SECTOR*fat_bpb->bpb_secperclus; |
1402 | firstentry = -1; | ||
1403 | 1710 | ||
1404 | rc = fat_seek(&dir->file, 0); | 1711 | /* step 1: search for a sufficiently-long run of free entries and check |
1405 | if (rc < 0) | 1712 | for duplicate shortname */ |
1406 | { | 1713 | dc_lock_cache(); |
1407 | fat_release_sector_buffer(); | ||
1408 | return rc * 10 - 2; | ||
1409 | } | ||
1410 | 1714 | ||
1411 | /* step 1: search for free entries and check for duplicate shortname */ | 1715 | for (bool done = false; !done;) |
1412 | for (sector = 0; !done; sector++) | ||
1413 | { | 1716 | { |
1414 | unsigned int i; | 1717 | union raw_dirent *ent = cache_direntry(fat_bpb, parentstr, entry); |
1415 | 1718 | ||
1416 | rc = fat_readwrite(&dir->file, 1, buf, false); | 1719 | if (!ent) |
1417 | if (rc < 0) { | 1720 | { |
1418 | fat_release_sector_buffer(); | 1721 | if (parentstr->eof) |
1419 | DEBUGF( "add_dir_entry() - Couldn't read dir" | 1722 | { |
1420 | " (error code %d)\n", rc); | 1723 | DEBUGF("End of dir (entry %d)\n", entry); |
1421 | return rc * 10 - 3; | 1724 | break; |
1422 | } | 1725 | } |
1423 | 1726 | ||
1424 | if (rc == 0) { /* current end of dir reached */ | 1727 | DEBUGF("Couldn't read dir (entry %d)\n", entry); |
1425 | LDEBUGF("End of dir on cluster boundary\n"); | 1728 | dc_unlock_cache(); |
1426 | break; | 1729 | FAT_ERROR(-3); |
1427 | } | 1730 | } |
1428 | 1731 | ||
1429 | /* look for free slots */ | 1732 | switch (ent->name[0]) |
1430 | for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) | ||
1431 | { | 1733 | { |
1432 | switch (buf[i * DIR_ENTRY_SIZE]) { | 1734 | case 0: /* all remaining entries in cluster are free */ |
1433 | case 0: | 1735 | DEBUGF("Found end of dir %d\n", entry); |
1434 | entries_found += DIR_ENTRIES_PER_SECTOR - i; | 1736 | int found = entperclus - (entry % entperclus); |
1435 | LDEBUGF("Found end of dir %d\n", | 1737 | entries_found += found; |
1436 | sector * DIR_ENTRIES_PER_SECTOR + i); | 1738 | entry += found; /* move entry passed end of cluster */ |
1437 | i = DIR_ENTRIES_PER_SECTOR - 1; | 1739 | done = true; |
1438 | done = true; | 1740 | break; |
1439 | break; | ||
1440 | 1741 | ||
1441 | case 0xe5: | 1742 | case 0xe5: /* individual free entry */ |
1442 | entries_found++; | 1743 | entries_found++; |
1443 | LDEBUGF("Found free entry %d (%d/%d)\n", | 1744 | entry++; |
1444 | sector * DIR_ENTRIES_PER_SECTOR + i, | 1745 | DEBUGF("Found free entry %d (%d/%d)\n", |
1445 | entries_found, entries_needed); | 1746 | entry, entries_found, entries_needed); |
1446 | break; | 1747 | break; |
1447 | 1748 | ||
1448 | default: | 1749 | default: /* occupied */ |
1449 | entries_found = 0; | 1750 | entries_found = 0; |
1751 | entry++; | ||
1450 | 1752 | ||
1451 | /* check that our intended shortname doesn't already exist */ | 1753 | if ((ent->ldir_attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) |
1452 | if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 11)) { | 1754 | break; /* ignore long name entry */ |
1453 | /* shortname exists already, make a new one */ | ||
1454 | randomize_dos_name(shortname); | ||
1455 | LDEBUGF("Duplicate shortname, changing to %s\n", | ||
1456 | shortname); | ||
1457 | 1755 | ||
1458 | /* name has changed, we need to restart search */ | 1756 | /* check that our intended shortname doesn't already exist */ |
1459 | goto restart; | 1757 | if (!strncmp(shortname, ent->name, 11)) |
1460 | } | 1758 | { |
1461 | break; | 1759 | /* shortname exists already, make a new one */ |
1760 | DEBUGF("Duplicate shortname '%.11s'", shortname); | ||
1761 | randomize_dos_name(shortname, basisname, &n); | ||
1762 | DEBUGF(", changing to '%.11s'\n", shortname); | ||
1763 | |||
1764 | /* name has changed, we need to restart search */ | ||
1765 | entry = 0; | ||
1766 | firstentry = -1; | ||
1462 | } | 1767 | } |
1463 | if (firstentry < 0 && (entries_found >= entries_needed)) | 1768 | break; |
1464 | firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1 | 1769 | } |
1465 | - entries_found; | 1770 | |
1771 | if (firstentry < 0 && entries_found >= entries_needed) | ||
1772 | { | ||
1773 | /* found adequate space; point to initial free entry */ | ||
1774 | firstentry = entry - entries_found; | ||
1466 | } | 1775 | } |
1467 | } | 1776 | } |
1468 | 1777 | ||
1778 | dc_unlock_cache(); | ||
1779 | |||
1469 | /* step 2: extend the dir if necessary */ | 1780 | /* step 2: extend the dir if necessary */ |
1470 | if (firstentry < 0) | 1781 | if (firstentry < 0) |
1471 | { | 1782 | { |
1472 | LDEBUGF("Adding new sector(s) to dir\n"); | 1783 | DEBUGF("Adding new cluster(s) to dir\n"); |
1473 | rc = fat_seek(&dir->file, sector); | 1784 | |
1474 | if (rc < 0) | 1785 | if (entry + entries_needed - entries_found > MAX_DIRENTRIES) |
1475 | { | 1786 | { |
1476 | fat_release_sector_buffer(); | 1787 | /* FAT specification allows no more than 65536 entries (2MB) |
1477 | return rc * 10 - 4; | 1788 | per directory */ |
1789 | DEBUGF("Directory would be too large.\n"); | ||
1790 | FAT_ERROR(-4); | ||
1478 | } | 1791 | } |
1479 | memset(buf, 0, SECTOR_SIZE); | ||
1480 | 1792 | ||
1481 | /* we must clear whole clusters */ | 1793 | while (entries_found < entries_needed) |
1482 | for (; (entries_found < entries_needed) || | ||
1483 | (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++) | ||
1484 | { | 1794 | { |
1485 | if (sector >= (65536/DIR_ENTRIES_PER_SECTOR)) | 1795 | rc = fat_extend_dir(fat_bpb, parentstr); |
1486 | { | 1796 | if (rc == FAT_RC_ENOSPC) |
1487 | fat_release_sector_buffer(); | 1797 | FAT_ERROR(RC); |
1488 | return -5; /* dir too large -- FAT specification */ | 1798 | else if (rc < 0) |
1489 | } | 1799 | FAT_ERROR(rc * 10 - 5); |
1490 | 1800 | ||
1491 | rc = fat_readwrite(&dir->file, 1, buf, true); | 1801 | entries_found += entperclus; |
1492 | if (rc < 1) /* No more room or something went wrong */ | 1802 | entry += entperclus; |
1493 | { | ||
1494 | fat_release_sector_buffer(); | ||
1495 | return rc * 10 - 6; | ||
1496 | } | ||
1497 | |||
1498 | entries_found += DIR_ENTRIES_PER_SECTOR; | ||
1499 | } | 1803 | } |
1500 | 1804 | ||
1501 | firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found; | 1805 | firstentry = entry - entries_found; |
1502 | } | 1806 | } |
1503 | fat_release_sector_buffer(); | ||
1504 | 1807 | ||
1505 | /* step 3: add entry */ | 1808 | /* remember the parent directory entry information */ |
1506 | sector = firstentry / DIR_ENTRIES_PER_SECTOR; | 1809 | #ifdef HAVE_MULTIVOLUME |
1507 | LDEBUGF("Adding longname to entry %d in sector %d\n", | 1810 | file->volume = parentstr->fatfilep->volume; |
1508 | firstentry, sector); | 1811 | #endif |
1812 | file->dircluster = parentstr->fatfilep->firstcluster; | ||
1813 | file->e.entry = firstentry + entries_needed - 1; | ||
1814 | file->e.entries = entries_needed; | ||
1509 | 1815 | ||
1510 | rc = write_long_name(&dir->file, firstentry, | 1816 | /* step 3: add entry */ |
1511 | entries_needed, name, | 1817 | DEBUGF("Adding longname to entry %d\n", firstentry); |
1512 | shortname, is_directory); | 1818 | rc = write_longname(fat_bpb, parentstr, file, name, ucslen, |
1819 | shortname, srcent, attr, flags); | ||
1513 | if (rc < 0) | 1820 | if (rc < 0) |
1514 | return rc * 10 - 7; | 1821 | FAT_ERROR(rc * 10 - 6); |
1515 | 1822 | ||
1516 | /* remember where the shortname dir entry is located */ | 1823 | DEBUGF("Added new dir entry %u; using %u entries\n", |
1517 | file->direntry = firstentry + entries_needed - 1; | 1824 | file->e.entry, file->e.entries); |
1518 | file->direntries = entries_needed; | ||
1519 | file->dircluster = dir->file.firstcluster; | ||
1520 | LDEBUGF("Added new dir entry %d, using %d slots.\n", | ||
1521 | file->direntry, file->direntries); | ||
1522 | 1825 | ||
1523 | return 0; | 1826 | rc = 0; |
1827 | fat_error: | ||
1828 | return rc; | ||
1524 | } | 1829 | } |
1525 | 1830 | ||
1526 | static unsigned char char2dos(unsigned char c, int* randomize) | 1831 | static int update_short_entry(struct bpb *fat_bpb, struct fat_file *file, |
1832 | uint32_t size, struct fat_direntry *fatent) | ||
1527 | { | 1833 | { |
1528 | static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|"; | 1834 | DEBUGF("%s(cluster:%lx entry:%d size:%ld)\n", |
1529 | 1835 | __func__, file->firstcluster, file->e.entry, size); | |
1530 | if (c <= 0x20) | ||
1531 | c = 0; /* Illegal char, remove */ | ||
1532 | else if (strchr(invalid_chars, c) != NULL) | ||
1533 | { | ||
1534 | /* Illegal char, replace */ | ||
1535 | c = '_'; | ||
1536 | *randomize = 1; /* as per FAT spec */ | ||
1537 | } | ||
1538 | else | ||
1539 | c = toupper(c); | ||
1540 | 1836 | ||
1541 | return c; | 1837 | int rc; |
1542 | } | ||
1543 | |||
1544 | static void create_dos_name(const unsigned char *name, unsigned char *newname) | ||
1545 | { | ||
1546 | int i; | ||
1547 | unsigned char *ext; | ||
1548 | int randomize = 0; | ||
1549 | 1838 | ||
1550 | /* Find extension part */ | 1839 | #if CONFIG_RTC |
1551 | ext = strrchr(name, '.'); | 1840 | uint16_t time = 0; |
1552 | if (ext == name) /* handle .dotnames */ | 1841 | uint16_t date = 0; |
1553 | ext = NULL; | 1842 | #else |
1843 | /* get old time to increment from */ | ||
1844 | uint16_t time = letoh16(fatent->wrttime); | ||
1845 | uint16_t date = letoh16(fatent->wrtdate); | ||
1846 | #endif | ||
1847 | fat_time(&date, &time, NULL); | ||
1848 | date = htole16(date); | ||
1849 | time = htole16(time); | ||
1554 | 1850 | ||
1555 | /* needs to randomize? */ | 1851 | /* open the parent directory */ |
1556 | if((ext && (strlen(ext) > 4)) || | 1852 | struct fat_file parent; |
1557 | ((ext ? (unsigned int)(ext-name) : strlen(name)) > 8) ) | 1853 | fat_open_internal(IF_MV(file->volume,) file->dircluster, &parent); |
1558 | randomize = 1; | ||
1559 | 1854 | ||
1560 | /* Name part */ | 1855 | struct fat_filestr parentstr; |
1561 | for (i = 0; *name && (!ext || name < ext) && (i < 8); name++) | 1856 | fat_filestr_init(&parentstr, &parent); |
1562 | { | ||
1563 | unsigned char c = char2dos(*name, &randomize); | ||
1564 | if (c) | ||
1565 | newname[i++] = c; | ||
1566 | } | ||
1567 | 1857 | ||
1568 | /* Pad both name and extension */ | 1858 | dc_lock_cache(); |
1569 | while (i < 11) | ||
1570 | newname[i++] = ' '; | ||
1571 | 1859 | ||
1572 | if (newname[0] == 0xe5) /* Special kanji character */ | 1860 | union raw_dirent *ent = cache_direntry(fat_bpb, &parentstr, file->e.entry); |
1573 | newname[0] = 0x05; | 1861 | if (!ent) |
1862 | FAT_ERROR(-1); | ||
1574 | 1863 | ||
1575 | if (ext) | 1864 | if (!ent->name[0] || ent->name[0] == 0xe5) |
1576 | { /* Extension part */ | 1865 | panicf("Updating size on empty dir entry %d\n", file->e.entry); |
1577 | ext++; | ||
1578 | for (i = 8; *ext && (i < 11); ext++) | ||
1579 | { | ||
1580 | unsigned char c = char2dos(*ext, &randomize); | ||
1581 | if (c) | ||
1582 | newname[i++] = c; | ||
1583 | } | ||
1584 | } | ||
1585 | 1866 | ||
1586 | if(randomize) | 1867 | /* basic file data */ |
1587 | randomize_dos_name(newname); | 1868 | raw_dirent_set_fstclus(ent, file->firstcluster); |
1588 | } | 1869 | ent->filesize = htole32(size); |
1589 | 1870 | ||
1590 | static void randomize_dos_name(unsigned char *name) | 1871 | /* time and date info */ |
1591 | { | 1872 | ent->wrttime = time; |
1592 | unsigned char* tilde = NULL; /* ~ location */ | 1873 | ent->wrtdate = date; |
1593 | unsigned char* lastpt = NULL; /* last point of filename */ | 1874 | ent->lstaccdate = date; |
1594 | unsigned char* nameptr = name; /* working copy of name pointer */ | ||
1595 | unsigned char num[9]; /* holds number as string */ | ||
1596 | int i = 0; | ||
1597 | int cnt = 1; | ||
1598 | int numlen; | ||
1599 | int offset; | ||
1600 | 1875 | ||
1601 | while(i++ < 8) | 1876 | if (fatent) |
1602 | { | 1877 | { |
1603 | /* hunt for ~ and where to put it */ | 1878 | fatent->name[0] = '\0'; /* not gonna bother here */ |
1604 | if(!tilde && *nameptr == '~') | 1879 | parse_short_direntry(ent, fatent); |
1605 | tilde = nameptr; | ||
1606 | if(!lastpt && (*nameptr == ' ' || *nameptr == '~')) | ||
1607 | lastpt = nameptr; | ||
1608 | nameptr++; | ||
1609 | } | 1880 | } |
1610 | if(tilde) | ||
1611 | { | ||
1612 | /* extract current count and increment */ | ||
1613 | memcpy(num,tilde+1,7-(unsigned int)(tilde-name)); | ||
1614 | num[7-(unsigned int)(tilde-name)] = 0; | ||
1615 | cnt = atoi(num) + 1; | ||
1616 | } | ||
1617 | cnt %= 10000000; /* protection */ | ||
1618 | snprintf(num, 9, "~%d", cnt); /* allow room for trailing zero */ | ||
1619 | numlen = strlen(num); /* required space */ | ||
1620 | offset = (unsigned int)(lastpt ? lastpt - name : 8); /* prev startpoint */ | ||
1621 | if(offset > (8-numlen)) offset = 8-numlen; /* correct for new numlen */ | ||
1622 | 1881 | ||
1623 | memcpy(&name[offset], num, numlen); | 1882 | dc_dirty_buf(ent); |
1624 | 1883 | ||
1625 | /* in special case of counter overflow: pad with spaces */ | 1884 | rc = 0; |
1626 | for(offset = offset+numlen; offset < 8; offset++) | 1885 | fat_error: |
1627 | name[offset] = ' '; | 1886 | dc_unlock_cache(); |
1887 | return rc; | ||
1628 | } | 1888 | } |
1629 | 1889 | ||
1630 | static int update_short_entry( struct fat_file* file, long size, int attr ) | 1890 | static int free_direntries(struct bpb *fat_bpb, struct fat_file *file) |
1631 | { | 1891 | { |
1632 | int sector = file->direntry / DIR_ENTRIES_PER_SECTOR; | 1892 | /* open the parent directory */ |
1633 | uint32_t* sizeptr; | 1893 | struct fat_file parent; |
1634 | uint16_t* clusptr; | 1894 | fat_open_internal(IF_MV(file->volume,) file->dircluster, &parent); |
1635 | struct fat_file dir; | ||
1636 | int rc; | ||
1637 | 1895 | ||
1638 | LDEBUGF("update_file_size(cluster:%lx entry:%d size:%ld)\n", | 1896 | struct fat_filestr parentstr; |
1639 | file->firstcluster, file->direntry, size); | 1897 | fat_filestr_init(&parentstr, &parent); |
1640 | 1898 | ||
1641 | /* create a temporary file handle for the dir holding this file */ | 1899 | for (unsigned int entries = file->e.entries, |
1642 | rc = fat_open(IF_MV(file->volume,) file->dircluster, &dir, NULL); | 1900 | entry = file->e.entry - entries + 1; |
1643 | if (rc < 0) | 1901 | entries; entries--, entry++) |
1644 | return rc * 10 - 1; | ||
1645 | |||
1646 | rc = fat_seek( &dir, sector ); | ||
1647 | if (rc<0) | ||
1648 | return rc * 10 - 2; | ||
1649 | |||
1650 | unsigned char* buf = fat_get_sector_buffer(); | ||
1651 | unsigned char* entry = | ||
1652 | buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR); | ||
1653 | rc = fat_readwrite(&dir, 1, buf, false); | ||
1654 | if (rc < 1) | ||
1655 | { | 1902 | { |
1656 | fat_release_sector_buffer(); | 1903 | DEBUGF("Clearing dir entry %d (%d/%d)\n", |
1657 | return rc * 10 - 3; | 1904 | entry, entry - numentries + 1, numentries); |
1658 | } | ||
1659 | 1905 | ||
1660 | if (!entry[0] || entry[0] == 0xe5) | 1906 | dc_lock_cache(); |
1661 | { | ||
1662 | fat_release_sector_buffer(); | ||
1663 | panicf("Updating size on empty dir entry %d\n", file->direntry); | ||
1664 | } | ||
1665 | 1907 | ||
1666 | entry[FATDIR_ATTR] = attr & 0xFF; | 1908 | union raw_dirent *ent = cache_direntry(fat_bpb, &parentstr, entry); |
1909 | if (!ent) | ||
1910 | { | ||
1911 | dc_unlock_cache(); | ||
1667 | 1912 | ||
1668 | clusptr = (uint16_t*)(entry + FATDIR_FSTCLUSHI); | 1913 | if (entries == file->e.entries) |
1669 | *clusptr = htole16(file->firstcluster >> 16); | 1914 | return -1; /* nothing at all freed */ |
1670 | 1915 | ||
1671 | clusptr = (uint16_t*)(entry + FATDIR_FSTCLUSLO); | 1916 | /* longname already destroyed; revert to shortname */ |
1672 | *clusptr = htole16(file->firstcluster & 0xffff); | 1917 | file->e.entries = 1; |
1918 | return 0; | ||
1919 | } | ||
1673 | 1920 | ||
1674 | sizeptr = (uint32_t*)(entry + FATDIR_FILESIZE); | 1921 | ent->data[0] = 0xe5; |
1675 | *sizeptr = htole32(size); | ||
1676 | 1922 | ||
1677 | { | 1923 | dc_dirty_buf(ent); |
1678 | #if CONFIG_RTC | 1924 | dc_unlock_cache(); |
1679 | uint16_t time = 0; | ||
1680 | uint16_t date = 0; | ||
1681 | #else | ||
1682 | /* get old time to increment from */ | ||
1683 | uint16_t time = htole16(*(uint16_t*)(entry+FATDIR_WRTTIME)); | ||
1684 | uint16_t date = htole16(*(uint16_t*)(entry+FATDIR_WRTDATE)); | ||
1685 | #endif | ||
1686 | fat_time(&date, &time, NULL); | ||
1687 | *(uint16_t*)(entry + FATDIR_WRTTIME) = htole16(time); | ||
1688 | *(uint16_t*)(entry + FATDIR_WRTDATE) = htole16(date); | ||
1689 | *(uint16_t*)(entry + FATDIR_LSTACCDATE) = htole16(date); | ||
1690 | } | 1925 | } |
1691 | 1926 | ||
1692 | rc = fat_seek( &dir, sector ); | 1927 | /* directory entry info is now gone */ |
1693 | if (rc < 0) | 1928 | file->dircluster = 0; |
1694 | { | 1929 | file->e.entry = FAT_RW_VAL; |
1695 | fat_release_sector_buffer(); | 1930 | file->e.entries = 0; |
1696 | return rc * 10 - 4; | ||
1697 | } | ||
1698 | |||
1699 | rc = fat_readwrite(&dir, 1, buf, true); | ||
1700 | fat_release_sector_buffer(); | ||
1701 | if (rc < 1) | ||
1702 | return rc * 10 - 5; | ||
1703 | 1931 | ||
1704 | return 0; | 1932 | return 1; |
1705 | } | 1933 | } |
1706 | 1934 | ||
1707 | static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) | 1935 | static int free_cluster_chain(struct bpb *fat_bpb, long startcluster) |
1708 | { | 1936 | { |
1709 | int i=0,j=0; | 1937 | for (long last = startcluster, next; last; last = next) |
1710 | unsigned char c; | 1938 | { |
1711 | bool lowercase; | 1939 | next = get_next_cluster(fat_bpb, last); |
1940 | int rc = update_fat_entry(fat_bpb, last, 0); | ||
1941 | if (LIKELY(rc >= 0 && !startcluster)) | ||
1942 | continue; | ||
1712 | 1943 | ||
1713 | memset(de, 0, sizeof(struct fat_direntry)); | 1944 | if (rc < 0) |
1714 | de->attr = buf[FATDIR_ATTR]; | 1945 | return startcluster ? -1 : 0; |
1715 | de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; | ||
1716 | de->crtdate = BYTES2INT16(buf,FATDIR_CRTDATE); | ||
1717 | de->crttime = BYTES2INT16(buf,FATDIR_CRTTIME); | ||
1718 | de->wrtdate = BYTES2INT16(buf,FATDIR_WRTDATE); | ||
1719 | de->wrttime = BYTES2INT16(buf,FATDIR_WRTTIME); | ||
1720 | de->filesize = BYTES2INT32(buf,FATDIR_FILESIZE); | ||
1721 | de->firstcluster = ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSLO)) | | ||
1722 | ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16); | ||
1723 | /* The double cast is to prevent a sign-extension to be done on CalmRISC16. | ||
1724 | (the result of the shift is always considered signed) */ | ||
1725 | 1946 | ||
1726 | /* fix the name */ | 1947 | startcluster = 0; |
1727 | lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME); | ||
1728 | c = buf[FATDIR_NAME]; | ||
1729 | if (c == 0x05) /* special kanji char */ | ||
1730 | c = 0xe5; | ||
1731 | i = 0; | ||
1732 | while (c != ' ') { | ||
1733 | de->name[j++] = lowercase ? tolower(c) : c; | ||
1734 | if (++i >= 8) | ||
1735 | break; | ||
1736 | c = buf[FATDIR_NAME+i]; | ||
1737 | } | ||
1738 | if (buf[FATDIR_NAME+8] != ' ') { | ||
1739 | lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT); | ||
1740 | de->name[j++] = '.'; | ||
1741 | for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++) | ||
1742 | de->name[j++] = lowercase ? tolower(c) : c; | ||
1743 | } | 1948 | } |
1949 | |||
1744 | return 1; | 1950 | return 1; |
1745 | } | 1951 | } |
1746 | 1952 | ||
1747 | int fat_open(IF_MV(int volume,) | ||
1748 | long startcluster, | ||
1749 | struct fat_file *file, | ||
1750 | const struct fat_dir* dir) | ||
1751 | { | ||
1752 | /* Remember where the file's dir entry is located | ||
1753 | * Do it before assigning other fields so that fat_open | ||
1754 | * can be called with file == &dir->file (see fat_opendir) */ | ||
1755 | if ( dir ) { | ||
1756 | file->direntry = dir->entry - 1; | ||
1757 | file->direntries = dir->entrycount; | ||
1758 | file->dircluster = dir->file.firstcluster; | ||
1759 | } | ||
1760 | |||
1761 | file->firstcluster = startcluster; | ||
1762 | file->lastcluster = startcluster; | ||
1763 | file->lastsector = 0; | ||
1764 | file->clusternum = 0; | ||
1765 | file->sectornum = 0; | ||
1766 | file->eof = false; | ||
1767 | #ifdef HAVE_MULTIVOLUME | ||
1768 | file->volume = volume; | ||
1769 | /* fixme: remove error check when done */ | ||
1770 | if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | ||
1771 | { | ||
1772 | LDEBUGF("fat_open() illegal volume %d\n", volume); | ||
1773 | return -1; | ||
1774 | } | ||
1775 | #endif | ||
1776 | 1953 | ||
1777 | LDEBUGF("fat_open(%lx), entry %d\n",startcluster,file->direntry); | 1954 | /** File entity functions **/ |
1778 | return 0; | ||
1779 | } | ||
1780 | 1955 | ||
1781 | int fat_create_file(const char* name, | 1956 | int fat_create_file(struct fat_file *parent, const char *name, |
1782 | struct fat_file* file, | 1957 | uint8_t attr, struct fat_file *file, |
1783 | struct fat_dir* dir) | 1958 | struct fat_direntry *fatent) |
1784 | { | 1959 | { |
1785 | int rc; | 1960 | DEBUGF("%s(\"%s\",%lx,%lx)\n", __func__, name, (long)file, (long)dir); |
1786 | 1961 | struct bpb * const fat_bpb = FAT_BPB(parent->volume); | |
1787 | LDEBUGF("fat_create_file(\"%s\",%lx,%lx)\n",name,(long)file,(long)dir); | 1962 | if (!fat_bpb) |
1788 | rc = add_dir_entry(dir, file, name, false, false); | 1963 | return -1; |
1789 | if (!rc) { | ||
1790 | file->firstcluster = 0; | ||
1791 | file->lastcluster = 0; | ||
1792 | file->lastsector = 0; | ||
1793 | file->clusternum = 0; | ||
1794 | file->sectornum = 0; | ||
1795 | file->eof = false; | ||
1796 | } | ||
1797 | 1964 | ||
1798 | return rc; | 1965 | int rc; |
1799 | } | ||
1800 | 1966 | ||
1801 | /* noinline because this is only split out of fat_create_dir to make sure | 1967 | fat_open_internal(IF_MV(parent->volume,) 0, file); |
1802 | * the sector buffer doesn't remain on the stack, to avoid nasty stack | ||
1803 | * overflows later on (when flush_fat() is called) */ | ||
1804 | static __attribute__((noinline)) int fat_clear_cluster(int sector, | ||
1805 | struct bpb *fat_bpb) | ||
1806 | { | ||
1807 | unsigned char* buf = fat_get_sector_buffer(); | ||
1808 | int i,rc; | ||
1809 | memset(buf, 0, SECTOR_SIZE); | ||
1810 | for(i = 0;i < (int)fat_bpb->bpb_secperclus;i++) { | ||
1811 | rc = transfer(IF_MV(fat_bpb,) sector + i, 1, buf, true ); | ||
1812 | if (rc < 0) | ||
1813 | { | ||
1814 | fat_release_sector_buffer(); | ||
1815 | return rc * 10 - 2; | ||
1816 | } | ||
1817 | } | ||
1818 | fat_release_sector_buffer(); | ||
1819 | return 0; | ||
1820 | } | ||
1821 | 1968 | ||
1822 | int fat_create_dir(const char* name, | 1969 | struct fat_filestr parentstr; |
1823 | struct fat_dir* newdir, | 1970 | fat_filestr_init(&parentstr, parent); |
1824 | struct fat_dir* dir) | ||
1825 | { | ||
1826 | #ifdef HAVE_MULTIVOLUME | ||
1827 | struct bpb* fat_bpb = &fat_bpbs[dir->file.volume]; | ||
1828 | #else | ||
1829 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1830 | #endif | ||
1831 | long sector; | ||
1832 | int rc; | ||
1833 | struct fat_file dummyfile; | ||
1834 | 1971 | ||
1835 | LDEBUGF("fat_create_dir(\"%s\",%lx,%lx)\n",name,(long)newdir,(long)dir); | 1972 | const bool isdir = attr & ATTR_DIRECTORY; |
1973 | unsigned int addflags = fatent ? DIRENT_RETURN : 0; | ||
1974 | union raw_dirent *newentp = (isdir || fatent) ? | ||
1975 | alloca(sizeof (union raw_dirent)) : NULL; | ||
1836 | 1976 | ||
1837 | memset(newdir, 0, sizeof(struct fat_dir)); | 1977 | if (isdir) |
1838 | memset(&dummyfile, 0, sizeof(struct fat_file)); | 1978 | { |
1979 | struct fat_filestr dirstr; | ||
1980 | fat_filestr_init(&dirstr, file); | ||
1839 | 1981 | ||
1840 | /* First, add the entry in the parent directory */ | 1982 | /* create the first cluster */ |
1841 | rc = add_dir_entry(dir, &newdir->file, name, true, false); | 1983 | rc = fat_extend_dir(fat_bpb, &dirstr); |
1842 | if (rc < 0) | 1984 | if (rc == FAT_RC_ENOSPC) |
1843 | return rc * 10 - 1; | 1985 | FAT_ERROR(RC); |
1986 | else if (rc < 0) | ||
1987 | FAT_ERROR(rc * 10 - 2); | ||
1844 | 1988 | ||
1845 | /* Allocate a new cluster for the directory */ | 1989 | struct fat_file dummyfile; |
1846 | newdir->file.firstcluster = find_free_cluster(IF_MV(fat_bpb,) | ||
1847 | fat_bpb->fsinfo.nextfree); | ||
1848 | if(newdir->file.firstcluster == 0) | ||
1849 | return -1; | ||
1850 | 1990 | ||
1851 | update_fat_entry(IF_MV(fat_bpb,) newdir->file.firstcluster, FAT_EOF_MARK); | 1991 | /* add the "." entry */ |
1992 | fat_open_internal(IF_MV(0,) file->firstcluster, &dummyfile); | ||
1852 | 1993 | ||
1853 | /* Clear the entire cluster */ | 1994 | /* this returns the short entry template for the remaining entries */ |
1854 | sector = cluster2sec(IF_MV(fat_bpb,) newdir->file.firstcluster); | 1995 | rc = add_dir_entry(fat_bpb, &dirstr, &dummyfile, ".", newentp, |
1855 | rc = fat_clear_cluster(sector,fat_bpb); | 1996 | attr, DIRENT_RETURN); |
1856 | if (rc < 0) | 1997 | if (rc < 0) |
1857 | return rc; | 1998 | FAT_ERROR(rc * 10 - 3); |
1858 | 1999 | ||
2000 | /* and the ".." entry */ | ||
2001 | /* the root cluster is cluster 0 in the ".." entry */ | ||
2002 | fat_open_internal(IF_MV(0,) | ||
2003 | parent->firstcluster == fat_bpb->bpb_rootclus ? | ||
2004 | 0 : parent->firstcluster, &dummyfile); | ||
1859 | 2005 | ||
1860 | /* Then add the "." entry */ | 2006 | rc = add_dir_entry(fat_bpb, &dirstr, &dummyfile, "..", newentp, |
1861 | rc = add_dir_entry(newdir, &dummyfile, ".", true, true); | 2007 | attr, DIRENT_TEMPL_TIMES); |
1862 | if (rc < 0) | 2008 | if (rc < 0) |
1863 | return rc * 10 - 3; | 2009 | FAT_ERROR(rc * 10 - 4); |
1864 | dummyfile.firstcluster = newdir->file.firstcluster; | ||
1865 | update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY); | ||
1866 | 2010 | ||
1867 | /* and the ".." entry */ | 2011 | addflags |= DIRENT_TEMPL_TIMES; |
1868 | rc = add_dir_entry(newdir, &dummyfile, "..", true, true); | 2012 | } |
1869 | if (rc < 0) | ||
1870 | return rc * 10 - 4; | ||
1871 | 2013 | ||
1872 | /* The root cluster is cluster 0 in the ".." entry */ | 2014 | /* lastly, add the entry in the parent directory */ |
1873 | if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | 2015 | rc = add_dir_entry(fat_bpb, &parentstr, file, name, newentp, |
1874 | dummyfile.firstcluster = 0; | 2016 | attr, addflags); |
1875 | else | 2017 | if (rc == FAT_RC_ENOSPC) |
1876 | dummyfile.firstcluster = dir->file.firstcluster; | 2018 | FAT_ERROR(RC); |
1877 | update_short_entry(&dummyfile, 0, FAT_ATTR_DIRECTORY); | 2019 | else if (rc < 0) |
2020 | FAT_ERROR(rc * 10 - 5); | ||
1878 | 2021 | ||
1879 | /* Set the firstcluster field in the direntry */ | 2022 | if (fatent) |
1880 | update_short_entry(&newdir->file, 0, FAT_ATTR_DIRECTORY); | 2023 | { |
2024 | strcpy(fatent->name, name); | ||
2025 | parse_short_direntry(newentp, fatent); | ||
2026 | } | ||
1881 | 2027 | ||
1882 | rc = flush_fat(IF_MV(fat_bpb)); | 2028 | rc = 0; |
2029 | fat_error: | ||
1883 | if (rc < 0) | 2030 | if (rc < 0) |
1884 | return rc * 10 - 5; | 2031 | free_cluster_chain(fat_bpb, file->firstcluster); |
1885 | 2032 | ||
2033 | cache_commit(fat_bpb); | ||
1886 | return rc; | 2034 | return rc; |
1887 | } | 2035 | } |
1888 | 2036 | ||
1889 | int fat_truncate(const struct fat_file *file) | 2037 | bool fat_dir_is_parent(const struct fat_file *dir, const struct fat_file *file) |
2038 | { | ||
2039 | /* if the directory file's first cluster is the same as the file's | ||
2040 | directory cluster and they're on the same volume, 'dir' is its parent | ||
2041 | directory; the file must also have a dircluster (ie. not removed) */ | ||
2042 | long dircluster = file->dircluster; | ||
2043 | return dircluster && dircluster == dir->firstcluster | ||
2044 | IF_MV( && dir->volume == file->volume ); | ||
2045 | } | ||
2046 | |||
2047 | bool fat_file_is_same(const struct fat_file *file1, | ||
2048 | const struct fat_file *file2) | ||
2049 | { | ||
2050 | /* first, triviality */ | ||
2051 | if (file1 == file2) | ||
2052 | return true; | ||
2053 | |||
2054 | /* if the directory info matches and the volumes are the same, file1 and | ||
2055 | file2 refer to the same file/directory */ | ||
2056 | return file1->dircluster == file2->dircluster | ||
2057 | && file1->e.entry == file2->e.entry | ||
2058 | IF_MV( && file1->volume == file2->volume ); | ||
2059 | } | ||
2060 | |||
2061 | int fat_open(const struct fat_file *parent, long startcluster, | ||
2062 | struct fat_file *file) | ||
1890 | { | 2063 | { |
1891 | /* truncate trailing clusters */ | 2064 | if (!parent) |
1892 | long next; | 2065 | return -2; /* this does _not_ open any root */ |
1893 | long last = file->lastcluster; | 2066 | |
2067 | struct bpb * const fat_bpb = FAT_BPB(parent->volume); | ||
2068 | if (!fat_bpb) | ||
2069 | return -1; | ||
2070 | |||
2071 | /* inherit basic parent information; dirscan info is expected to have been | ||
2072 | initialized beforehand (usually via scanning for the entry ;) */ | ||
1894 | #ifdef HAVE_MULTIVOLUME | 2073 | #ifdef HAVE_MULTIVOLUME |
1895 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2074 | file->volume = parent->volume; |
1896 | #endif | 2075 | #endif |
2076 | file->firstcluster = startcluster; | ||
2077 | file->dircluster = parent->firstcluster; | ||
1897 | 2078 | ||
1898 | LDEBUGF("fat_truncate(%lx, %lx)\n", file->firstcluster, last); | 2079 | return 0; |
2080 | } | ||
1899 | 2081 | ||
1900 | for ( last = get_next_cluster(IF_MV(fat_bpb,) last); last; last = next ) { | 2082 | int fat_open_rootdir(IF_MV(int volume,) struct fat_file *dir) |
1901 | next = get_next_cluster(IF_MV(fat_bpb,) last); | 2083 | { |
1902 | update_fat_entry(IF_MV(fat_bpb,) last,0); | 2084 | struct bpb * const fat_bpb = FAT_BPB(volume); |
1903 | } | 2085 | if (!fat_bpb) |
1904 | if (file->lastcluster) | 2086 | return -1; |
1905 | update_fat_entry(IF_MV(fat_bpb,) file->lastcluster,FAT_EOF_MARK); | ||
1906 | 2087 | ||
2088 | fat_open_internal(IF_MV(volume,) fat_bpb->bpb_rootclus, dir); | ||
1907 | return 0; | 2089 | return 0; |
1908 | } | 2090 | } |
1909 | 2091 | ||
1910 | int fat_closewrite(struct fat_file *file, long size, int attr) | 2092 | int fat_remove(struct fat_file *file, enum fat_remove_op what) |
1911 | { | 2093 | { |
2094 | struct bpb * const fat_bpb = FAT_BPB(file->volume); | ||
2095 | if (!fat_bpb) | ||
2096 | return -1; | ||
2097 | |||
1912 | int rc; | 2098 | int rc; |
1913 | #ifdef HAVE_MULTIVOLUME | ||
1914 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | ||
1915 | #endif | ||
1916 | LDEBUGF("fat_closewrite(size=%ld)\n",size); | ||
1917 | 2099 | ||
1918 | if (!size) { | 2100 | if (file->firstcluster == fat_bpb->bpb_rootclus) |
1919 | /* empty file */ | 2101 | { |
1920 | if ( file->firstcluster ) { | 2102 | DEBUGF("Trying to remove root of volume %d\n", |
1921 | update_fat_entry(IF_MV(fat_bpb,) file->firstcluster, 0); | 2103 | IF_MV_VOL(info->volume)); |
1922 | file->firstcluster = 0; | 2104 | FAT_ERROR(-2); |
1923 | } | ||
1924 | } | 2105 | } |
1925 | 2106 | ||
1926 | if (file->dircluster) { | 2107 | if (file->dircluster && (what & FAT_RM_DIRENTRIES)) |
1927 | rc = update_short_entry(file, size, attr); | 2108 | { |
1928 | if (rc < 0) | 2109 | /* free everything in the parent directory */ |
1929 | return rc * 10 - 1; | 2110 | DEBUGF("Removing dir entries: %lX\n", info->dircluster); |
2111 | rc = free_direntries(fat_bpb, file); | ||
2112 | if (rc <= 0) | ||
2113 | FAT_ERROR(rc * 10 - 3); | ||
1930 | } | 2114 | } |
1931 | 2115 | ||
1932 | flush_fat(IF_MV(fat_bpb)); | 2116 | if (file->firstcluster && (what & FAT_RM_DATA)) |
2117 | { | ||
2118 | /* mark all clusters in the chain as free */ | ||
2119 | DEBUGF("Removing cluster chain: %lX\n", file->firstcluster); | ||
2120 | rc = free_cluster_chain(fat_bpb, file->firstcluster); | ||
2121 | if (rc < 0) | ||
2122 | FAT_ERROR(rc * 10 - 4); | ||
1933 | 2123 | ||
1934 | #ifdef TEST_FAT | 2124 | /* at least the first cluster was freed */ |
1935 | if ( file->firstcluster ) { | 2125 | file->firstcluster = 0; |
1936 | /* debug */ | 2126 | |
1937 | #ifdef HAVE_MULTIVOLUME | 2127 | if (rc == 0) |
1938 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2128 | FAT_ERROR(-5); |
1939 | #else | ||
1940 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
1941 | #endif | ||
1942 | long count = 0; | ||
1943 | long len; | ||
1944 | long next; | ||
1945 | for ( next = file->firstcluster; next; | ||
1946 | next = get_next_cluster(IF_MV(fat_bpb,) next) ) { | ||
1947 | LDEBUGF("cluster %ld: %lx\n", count, next); | ||
1948 | count++; | ||
1949 | } | ||
1950 | len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; | ||
1951 | LDEBUGF("File is %ld clusters (chainlen=%ld, size=%ld)\n", | ||
1952 | count, len, size ); | ||
1953 | if ( len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) | ||
1954 | panicf("Cluster chain is too long\n"); | ||
1955 | if ( len < size ) | ||
1956 | panicf("Cluster chain is too short\n"); | ||
1957 | } | 2129 | } |
1958 | #endif | ||
1959 | 2130 | ||
1960 | return 0; | 2131 | rc = 0; |
2132 | fat_error: | ||
2133 | cache_commit(fat_bpb); | ||
2134 | return rc; | ||
1961 | } | 2135 | } |
1962 | 2136 | ||
1963 | static int free_direntries(struct fat_file* file) | 2137 | int fat_rename(struct fat_file *parent, struct fat_file *file, |
2138 | const unsigned char *newname) | ||
1964 | { | 2139 | { |
1965 | struct fat_file dir; | 2140 | struct bpb * const fat_bpb = FAT_BPB(parent->volume); |
1966 | int numentries = file->direntries; | 2141 | if (!fat_bpb) |
1967 | unsigned int entry = file->direntry - numentries + 1; | 2142 | return -1; |
1968 | unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR; | 2143 | |
1969 | int i; | ||
1970 | int rc; | 2144 | int rc; |
2145 | /* save old file; don't change it unless everything succeeds */ | ||
2146 | struct fat_file newfile = *file; | ||
1971 | 2147 | ||
1972 | /* create a temporary file handle for the dir holding this file */ | 2148 | #ifdef HAVE_MULTIVOLUME |
1973 | rc = fat_open(IF_MV(file->volume,) file->dircluster, &dir, NULL); | 2149 | /* rename only works on the same volume */ |
1974 | if (rc < 0) | 2150 | if (file->volume != parent->volume) |
1975 | return rc * 10 - 1; | 2151 | { |
2152 | DEBUGF("No rename across volumes!\n"); | ||
2153 | FAT_ERROR(-2); | ||
2154 | } | ||
2155 | #endif | ||
1976 | 2156 | ||
1977 | rc = fat_seek( &dir, sector ); | 2157 | /* root directories can't be renamed */ |
1978 | if (rc < 0) | 2158 | if (file->firstcluster == fat_bpb->bpb_rootclus) |
1979 | return rc * 10 - 2; | 2159 | { |
2160 | DEBUGF("Trying to rename root of volume %d\n", | ||
2161 | IF_MV_VOL(file->volume)); | ||
2162 | FAT_ERROR(-3); | ||
2163 | } | ||
1980 | 2164 | ||
1981 | unsigned char* buf = fat_get_sector_buffer(); | 2165 | if (!file->dircluster) |
1982 | rc = fat_readwrite(&dir, 1, buf, false); | ||
1983 | if (rc < 1) | ||
1984 | { | 2166 | { |
1985 | fat_release_sector_buffer(); | 2167 | /* file was removed but is still open */ |
1986 | return rc * 10 - 3; | 2168 | DEBUGF("File has no dir cluster!\n"); |
2169 | FAT_ERROR(-4); | ||
1987 | } | 2170 | } |
1988 | 2171 | ||
1989 | for (i=0; i < numentries; i++) { | 2172 | struct fat_file dir; |
1990 | LDEBUGF("Clearing dir entry %d (%d/%d)\n", | 2173 | struct fat_filestr dirstr; |
1991 | entry, i+1, numentries); | ||
1992 | buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5; | ||
1993 | entry++; | ||
1994 | 2174 | ||
1995 | if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) { | 2175 | /* open old parent */ |
1996 | /* flush this sector */ | 2176 | fat_open_internal(IF_MV(file->volume,) file->dircluster, &dir); |
1997 | rc = fat_seek(&dir, sector); | 2177 | fat_filestr_init(&dirstr, &dir); |
1998 | if (rc < 0) | ||
1999 | { | ||
2000 | fat_release_sector_buffer(); | ||
2001 | return rc * 10 - 4; | ||
2002 | } | ||
2003 | 2178 | ||
2004 | rc = fat_readwrite(&dir, 1, buf, true); | 2179 | /* fetch a copy of the existing short entry */ |
2005 | if (rc < 1) | 2180 | dc_lock_cache(); |
2006 | { | ||
2007 | fat_release_sector_buffer(); | ||
2008 | return rc * 10 - 5; | ||
2009 | } | ||
2010 | 2181 | ||
2011 | if ( i+1 < numentries ) { | 2182 | union raw_dirent *ent = cache_direntry(fat_bpb, &dirstr, file->e.entry); |
2012 | /* read next sector */ | 2183 | if (!ent) |
2013 | rc = fat_readwrite(&dir, 1, buf, false); | 2184 | { |
2014 | if (rc < 1) | 2185 | dc_unlock_cache(); |
2015 | { | 2186 | FAT_ERROR(-5); |
2016 | fat_release_sector_buffer(); | ||
2017 | return rc * 10 - 6; | ||
2018 | } | ||
2019 | } | ||
2020 | sector++; | ||
2021 | } | ||
2022 | } | 2187 | } |
2023 | 2188 | ||
2024 | if ( entry % DIR_ENTRIES_PER_SECTOR ) { | 2189 | union raw_dirent rawent = *ent; |
2025 | /* flush this sector */ | 2190 | |
2026 | rc = fat_seek(&dir, sector); | 2191 | dc_unlock_cache(); |
2027 | if (rc < 0) | 2192 | |
2193 | /* create new name in new parent directory */ | ||
2194 | fat_filestr_init(&dirstr, parent); | ||
2195 | rc = add_dir_entry(fat_bpb, &dirstr, &newfile, newname, &rawent, | ||
2196 | 0, DIRENT_TEMPL_CRT | DIRENT_TEMPL_WRT); | ||
2197 | if (rc == FAT_RC_ENOSPC) | ||
2198 | FAT_ERROR(RC); | ||
2199 | else if (rc < 0) | ||
2200 | FAT_ERROR(rc * 10 - 6); | ||
2201 | |||
2202 | /* if renaming a directory and it was a move, update the '..' entry to | ||
2203 | keep it pointing to its parent directory */ | ||
2204 | if ((rawent.attr & ATTR_DIRECTORY) && newfile.dircluster != file->dircluster) | ||
2205 | { | ||
2206 | /* open the dir that was renamed */ | ||
2207 | fat_open_internal(IF_MV(newfile.volume,) newfile.firstcluster, &dir); | ||
2208 | fat_filestr_init(&dirstr, &dir); | ||
2209 | |||
2210 | /* obtain dot-dot directory entry */ | ||
2211 | dc_lock_cache(); | ||
2212 | ent = cache_direntry(fat_bpb, &dirstr, 1); | ||
2213 | if (!ent) | ||
2028 | { | 2214 | { |
2029 | fat_release_sector_buffer(); | 2215 | dc_unlock_cache(); |
2030 | return rc * 10 - 7; | 2216 | FAT_ERROR(-7); |
2031 | } | 2217 | } |
2032 | 2218 | ||
2033 | rc = fat_readwrite(&dir, 1, buf, true); | 2219 | if (strncmp(".. ", ent->name, 11)) |
2034 | if (rc < 1) | ||
2035 | { | 2220 | { |
2036 | fat_release_sector_buffer(); | 2221 | /* .. entry must be second entry according to FAT spec (p.29) */ |
2037 | return rc * 10 - 8; | 2222 | DEBUGF("Second dir entry is not double-dot!\n"); |
2223 | dc_unlock_cache(); | ||
2224 | FAT_ERROR(-8); | ||
2038 | } | 2225 | } |
2039 | } | ||
2040 | fat_release_sector_buffer(); | ||
2041 | 2226 | ||
2042 | return 0; | 2227 | /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ |
2043 | } | 2228 | long parentcluster = 0; |
2044 | 2229 | if (parent->firstcluster != fat_bpb->bpb_rootclus) | |
2045 | int fat_remove(struct fat_file* file) | 2230 | parentcluster = parent->firstcluster; |
2046 | { | ||
2047 | long next, last = file->firstcluster; | ||
2048 | int rc; | ||
2049 | #ifdef HAVE_MULTIVOLUME | ||
2050 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | ||
2051 | #endif | ||
2052 | 2231 | ||
2053 | LDEBUGF("fat_remove(%lx)\n",last); | 2232 | raw_dirent_set_fstclus(ent, parentcluster); |
2054 | 2233 | ||
2055 | while ( last ) { | 2234 | dc_dirty_buf(ent); |
2056 | next = get_next_cluster(IF_MV(fat_bpb,) last); | 2235 | dc_unlock_cache(); |
2057 | update_fat_entry(IF_MV(fat_bpb,) last,0); | ||
2058 | last = next; | ||
2059 | } | 2236 | } |
2060 | 2237 | ||
2061 | if ( file->dircluster ) { | 2238 | /* remove old name */ |
2062 | rc = free_direntries(file); | 2239 | rc = free_direntries(fat_bpb, file); |
2063 | if (rc < 0) | 2240 | if (rc <= 0) |
2064 | return rc * 10 - 1; | 2241 | FAT_ERROR(rc * 10 - 9); |
2065 | } | ||
2066 | 2242 | ||
2067 | file->firstcluster = 0; | 2243 | /* finally, update old file with new directory entry info */ |
2068 | file->dircluster = 0; | 2244 | *file = newfile; |
2069 | 2245 | ||
2070 | rc = flush_fat(IF_MV(fat_bpb)); | 2246 | rc = 0; |
2071 | if (rc < 0) | 2247 | fat_error: |
2072 | return rc * 10 - 2; | 2248 | if (rc < 0 && !fat_file_is_same(&newfile, file)) |
2249 | free_direntries(fat_bpb, &newfile); | ||
2073 | 2250 | ||
2074 | return 0; | 2251 | cache_commit(fat_bpb); |
2252 | return rc; | ||
2075 | } | 2253 | } |
2076 | 2254 | ||
2077 | int fat_rename(struct fat_file* file, | ||
2078 | struct fat_dir* dir, | ||
2079 | const unsigned char* newname, | ||
2080 | long size, | ||
2081 | int attr) | ||
2082 | { | ||
2083 | int rc; | ||
2084 | struct fat_file olddir_file; | ||
2085 | struct fat_file newfile = *file; | ||
2086 | unsigned char* entry = NULL; | ||
2087 | unsigned short* clusptr = NULL; | ||
2088 | unsigned int parentcluster; | ||
2089 | #ifdef HAVE_MULTIVOLUME | ||
2090 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | ||
2091 | 2255 | ||
2092 | if (file->volume != dir->file.volume) { | 2256 | /** File stream functions **/ |
2093 | DEBUGF("No rename across volumes!\n"); | ||
2094 | return -1; | ||
2095 | } | ||
2096 | #else | ||
2097 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2098 | #endif | ||
2099 | |||
2100 | if ( !file->dircluster ) { | ||
2101 | DEBUGF("File has no dir cluster!\n"); | ||
2102 | return -2; | ||
2103 | } | ||
2104 | |||
2105 | /* create new name */ | ||
2106 | rc = add_dir_entry(dir, &newfile, newname, false, false); | ||
2107 | if (rc < 0) | ||
2108 | return rc * 10 - 2; | ||
2109 | |||
2110 | /* write size and cluster link */ | ||
2111 | rc = update_short_entry(&newfile, size, attr); | ||
2112 | if (rc < 0) | ||
2113 | return rc * 10 - 3; | ||
2114 | 2257 | ||
2115 | /* remove old name */ | 2258 | int fat_closewrite(struct fat_filestr *filestr, uint32_t size, |
2116 | rc = free_direntries(file); | 2259 | struct fat_direntry *fatentp) |
2117 | if (rc < 0) | 2260 | { |
2118 | return rc * 10 - 4; | 2261 | DEBUGF("%s(size=%ld)\n", __func__, size); |
2262 | struct fat_file * const file = filestr->fatfilep; | ||
2263 | struct bpb * const fat_bpb = FAT_BPB(file->volume); | ||
2264 | if (!fat_bpb) | ||
2265 | return -1; | ||
2119 | 2266 | ||
2120 | rc = flush_fat(IF_MV(fat_bpb)); | 2267 | int rc; |
2121 | if (rc < 0) | ||
2122 | return rc * 10 - 5; | ||
2123 | 2268 | ||
2124 | /* if renaming a directory, update the .. entry to make sure | 2269 | if (!size && file->firstcluster) |
2125 | it points to its parent directory (we don't check if it was a move) */ | 2270 | { |
2126 | if(FAT_ATTR_DIRECTORY == attr) { | 2271 | /* empty file */ |
2127 | /* open the dir that was renamed, we re-use the olddir_file struct */ | 2272 | rc = update_fat_entry(fat_bpb, file->firstcluster, 0); |
2128 | rc = fat_open(IF_MV(file->volume,) newfile.firstcluster, &olddir_file, NULL); | ||
2129 | if (rc < 0) | 2273 | if (rc < 0) |
2130 | return rc * 10 - 6; | 2274 | FAT_ERROR(rc * 10 - 2); |
2131 | 2275 | ||
2132 | /* get the first sector of the dir */ | 2276 | file->firstcluster = 0; |
2133 | rc = fat_seek(&olddir_file, 0); | 2277 | fat_rewind(filestr); |
2134 | if (rc < 0) | 2278 | } |
2135 | return rc * 10 - 7; | ||
2136 | 2279 | ||
2137 | unsigned char* buf = fat_get_sector_buffer(); | 2280 | if (file->dircluster) |
2138 | rc = fat_readwrite(&olddir_file, 1, buf, false); | 2281 | { |
2282 | rc = update_short_entry(fat_bpb, file, size, fatentp); | ||
2139 | if (rc < 0) | 2283 | if (rc < 0) |
2140 | { | 2284 | FAT_ERROR(rc * 10 - 3); |
2141 | fat_release_sector_buffer(); | 2285 | } |
2142 | return rc * 10 - 8; | 2286 | else if (fatentp) |
2143 | } | 2287 | { |
2144 | 2288 | fat_empty_fat_direntry(fatentp); | |
2145 | /* parent cluster is 0 if parent dir is the root - FAT spec (p.29) */ | 2289 | } |
2146 | if(dir->file.firstcluster == fat_bpb->bpb_rootclus) | ||
2147 | parentcluster = 0; | ||
2148 | else | ||
2149 | parentcluster = dir->file.firstcluster; | ||
2150 | 2290 | ||
2151 | entry = buf + DIR_ENTRY_SIZE; | 2291 | #ifdef TEST_FAT |
2152 | if(strncmp(".. ", entry, 11)) | 2292 | if (file->firstcluster) |
2293 | { | ||
2294 | unsigned long count = 0; | ||
2295 | for (long next = file->firstcluster; next; | ||
2296 | next = get_next_cluster(fat_bpb, next)) | ||
2153 | { | 2297 | { |
2154 | fat_release_sector_buffer(); | 2298 | DEBUGF("cluster %ld: %lx\n", count, next); |
2155 | /* .. entry must be second entry according to FAT spec (p.29) */ | 2299 | count++; |
2156 | DEBUGF("Second dir entry is not double-dot!\n"); | ||
2157 | return rc * 10 - 9; | ||
2158 | } | 2300 | } |
2159 | clusptr = (short*)(entry + FATDIR_FSTCLUSHI); | ||
2160 | *clusptr = htole16(parentcluster >> 16); | ||
2161 | 2301 | ||
2162 | clusptr = (short*)(entry + FATDIR_FSTCLUSLO); | 2302 | uint32_t len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; |
2163 | *clusptr = htole16(parentcluster & 0xffff); | 2303 | DEBUGF("File is %lu clusters (chainlen=%lu, size=%lu)\n", |
2304 | count, len, size ); | ||
2164 | 2305 | ||
2165 | /* write back this sector */ | 2306 | if (len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) |
2166 | rc = fat_seek(&olddir_file, 0); | 2307 | panicf("Cluster chain is too long\n"); |
2167 | if (rc < 0) | ||
2168 | { | ||
2169 | fat_release_sector_buffer(); | ||
2170 | return rc * 10 - 7; | ||
2171 | } | ||
2172 | 2308 | ||
2173 | rc = fat_readwrite(&olddir_file, 1, buf, true); | 2309 | if (len < size) |
2174 | fat_release_sector_buffer(); | 2310 | panicf("Cluster chain is too short\n"); |
2175 | if (rc < 1) | ||
2176 | return rc * 10 - 8; | ||
2177 | } | 2311 | } |
2312 | #endif /* TEST_FAT */ | ||
2178 | 2313 | ||
2179 | return 0; | 2314 | rc = 0; |
2315 | fat_error: | ||
2316 | cache_commit(fat_bpb); | ||
2317 | return rc; | ||
2180 | } | 2318 | } |
2181 | 2319 | ||
2182 | static long next_write_cluster(struct fat_file* file, | 2320 | void fat_filestr_init(struct fat_filestr *fatstr, struct fat_file *file) |
2183 | long oldcluster, | ||
2184 | long* newsector) | ||
2185 | { | 2321 | { |
2186 | #ifdef HAVE_MULTIVOLUME | 2322 | fatstr->fatfilep = file; |
2187 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2323 | fat_rewind(fatstr); |
2188 | #else | 2324 | } |
2189 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2190 | #endif | ||
2191 | long cluster = 0; | ||
2192 | long sector; | ||
2193 | |||
2194 | LDEBUGF("next_write_cluster(%lx,%lx)\n",file->firstcluster, oldcluster); | ||
2195 | |||
2196 | if (oldcluster) | ||
2197 | cluster = get_next_cluster(IF_MV(fat_bpb,) oldcluster); | ||
2198 | |||
2199 | if (!cluster) { | ||
2200 | if (oldcluster > 0) | ||
2201 | cluster = find_free_cluster(IF_MV(fat_bpb,) oldcluster+1); | ||
2202 | else if (oldcluster == 0) | ||
2203 | cluster = find_free_cluster(IF_MV(fat_bpb,) | ||
2204 | fat_bpb->fsinfo.nextfree); | ||
2205 | #ifdef HAVE_FAT16SUPPORT | ||
2206 | else /* negative, pseudo-cluster of the root dir */ | ||
2207 | return 0; /* impossible to append something to the root */ | ||
2208 | #endif | ||
2209 | 2325 | ||
2210 | if (cluster) { | 2326 | unsigned long fat_query_sectornum(const struct fat_filestr *filestr) |
2211 | if (oldcluster) | 2327 | { |
2212 | update_fat_entry(IF_MV(fat_bpb,) oldcluster, cluster); | 2328 | /* return next sector number to be transferred */ |
2213 | else | 2329 | struct bpb * const fat_bpb = FAT_BPB(filestr->fatfilep->volume); |
2214 | file->firstcluster = cluster; | 2330 | if (!fat_bpb) |
2215 | update_fat_entry(IF_MV(fat_bpb,) cluster, FAT_EOF_MARK); | 2331 | return INVALID_SECNUM; |
2216 | } | ||
2217 | else { | ||
2218 | #ifdef TEST_FAT | ||
2219 | if (fat_bpb->fsinfo.freecount>0) | ||
2220 | panicf("There is free space, but find_free_cluster() " | ||
2221 | "didn't find it!\n"); | ||
2222 | #endif | ||
2223 | DEBUGF("next_write_cluster(): Disk full!\n"); | ||
2224 | return 0; | ||
2225 | } | ||
2226 | } | ||
2227 | sector = cluster2sec(IF_MV(fat_bpb,) cluster); | ||
2228 | if (sector<0) | ||
2229 | return 0; | ||
2230 | 2332 | ||
2231 | *newsector = sector; | 2333 | return fat_bpb->bpb_secperclus*filestr->clusternum + filestr->sectornum + 1; |
2232 | return cluster; | ||
2233 | } | 2334 | } |
2234 | 2335 | ||
2235 | static int transfer(IF_MV(struct bpb* fat_bpb,) | 2336 | /* helper for fat_readwrite */ |
2236 | unsigned long start, long count, char* buf, bool write ) | 2337 | static long transfer(struct bpb *fat_bpb, unsigned long start, long count, |
2338 | char *buf, bool write) | ||
2237 | { | 2339 | { |
2238 | #ifndef HAVE_MULTIVOLUME | 2340 | long rc = 0; |
2239 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2240 | #endif | ||
2241 | int rc; | ||
2242 | 2341 | ||
2243 | LDEBUGF("transfer(s=%lx, c=%lx, %s)\n", | 2342 | DEBUGF("%s(s=%lx, c=%lx, wr=%u)\n", __func__, |
2244 | start+ fat_bpb->startsector, count, write?"write":"read"); | 2343 | start + fat_bpb->startsector, count, write ? 1 : 0); |
2245 | if (write) { | 2344 | |
2345 | if (write) | ||
2346 | { | ||
2246 | unsigned long firstallowed; | 2347 | unsigned long firstallowed; |
2247 | #ifdef HAVE_FAT16SUPPORT | 2348 | #ifdef HAVE_FAT16SUPPORT |
2248 | if (fat_bpb->is_fat16) | 2349 | if (fat_bpb->is_fat16) |
2249 | firstallowed = fat_bpb->rootdirsector; | 2350 | firstallowed = fat_bpb->rootdirsector; |
2250 | else | 2351 | else |
2251 | #endif | 2352 | #endif /* HAVE_FAT16SUPPORT */ |
2252 | firstallowed = fat_bpb->firstdatasector; | 2353 | firstallowed = fat_bpb->firstdatasector; |
2253 | 2354 | ||
2254 | if (start < firstallowed) | 2355 | if (start < firstallowed) |
2255 | panicf("Write %ld before data\n", firstallowed - start); | 2356 | panicf("Write %ld before data\n", firstallowed - start); |
2357 | |||
2256 | if (start + count > fat_bpb->totalsectors) | 2358 | if (start + count > fat_bpb->totalsectors) |
2359 | { | ||
2257 | panicf("Write %ld after data\n", | 2360 | panicf("Write %ld after data\n", |
2258 | start + count - fat_bpb->totalsectors); | 2361 | start + count - fat_bpb->totalsectors); |
2259 | rc = storage_write_sectors(IF_MD(fat_bpb->drive,) | 2362 | } |
2260 | start + fat_bpb->startsector, count, buf); | 2363 | else |
2364 | { | ||
2365 | rc = storage_write_sectors(IF_MD(fat_bpb->drive,) | ||
2366 | start + fat_bpb->startsector, count, buf); | ||
2367 | } | ||
2261 | } | 2368 | } |
2262 | else | 2369 | else |
2370 | { | ||
2263 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) | 2371 | rc = storage_read_sectors(IF_MD(fat_bpb->drive,) |
2264 | start + fat_bpb->startsector, count, buf); | 2372 | start + fat_bpb->startsector, count, buf); |
2265 | if (rc < 0) { | 2373 | } |
2266 | DEBUGF( "transfer() - Couldn't %s sector %lx" | 2374 | |
2267 | " (error code %d)\n", | 2375 | if (rc < 0) |
2268 | write ? "write":"read", start, rc); | 2376 | { |
2377 | DEBUGF("Couldn't %s sector %lx (err %d)\n", | ||
2378 | write ? "write":"read", start, rc); | ||
2269 | return rc; | 2379 | return rc; |
2270 | } | 2380 | } |
2381 | |||
2271 | return 0; | 2382 | return 0; |
2272 | } | 2383 | } |
2273 | 2384 | ||
2274 | 2385 | long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount, | |
2275 | long fat_readwrite( struct fat_file *file, long sectorcount, | 2386 | void *buf, bool write) |
2276 | void* buf, bool write ) | ||
2277 | { | 2387 | { |
2278 | #ifdef HAVE_MULTIVOLUME | 2388 | struct fat_file * const file = filestr->fatfilep; |
2279 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2389 | struct bpb * const fat_bpb = FAT_BPB(file->volume); |
2280 | #else | 2390 | if (!fat_bpb) |
2281 | struct bpb* fat_bpb = &fat_bpbs[0]; | 2391 | return -1; |
2282 | #endif | ||
2283 | long cluster = file->lastcluster; | ||
2284 | long sector = file->lastsector; | ||
2285 | long clusternum = file->clusternum; | ||
2286 | long numsec = file->sectornum; | ||
2287 | bool eof = file->eof; | ||
2288 | long first=0, last=0; | ||
2289 | long i; | ||
2290 | int rc; | ||
2291 | 2392 | ||
2292 | LDEBUGF( "fat_readwrite(file:%lx,count:0x%lx,buf:%lx,%s)\n", | 2393 | bool eof = filestr->eof; |
2293 | file->firstcluster,sectorcount,(long)buf,write?"write":"read"); | ||
2294 | LDEBUGF( "fat_readwrite: sec=%lx numsec=%ld eof=%d\n", | ||
2295 | sector,numsec, eof?1:0); | ||
2296 | 2394 | ||
2297 | if ( eof && !write) | 2395 | if ((eof && !write) || !sectorcount) |
2298 | return 0; | 2396 | return 0; |
2299 | 2397 | ||
2300 | /* find sequential sectors and write them all at once */ | 2398 | long rc; |
2301 | for (i=0; (i < sectorcount) && (sector > -1); i++ ) { | ||
2302 | numsec++; | ||
2303 | if ( numsec > (long)fat_bpb->bpb_secperclus || !cluster ) { | ||
2304 | long oldcluster = cluster; | ||
2305 | long oldsector = sector; | ||
2306 | long oldnumsec = numsec; | ||
2307 | if (write) | ||
2308 | cluster = next_write_cluster(file, cluster, §or); | ||
2309 | else { | ||
2310 | cluster = get_next_cluster(IF_MV(fat_bpb,) cluster); | ||
2311 | sector = cluster2sec(IF_MV(fat_bpb,) cluster); | ||
2312 | } | ||
2313 | 2399 | ||
2314 | clusternum++; | 2400 | long cluster = filestr->lastcluster; |
2315 | numsec=1; | 2401 | unsigned long sector = filestr->lastsector; |
2402 | long clusternum = filestr->clusternum; | ||
2403 | unsigned long sectornum = filestr->sectornum; | ||
2316 | 2404 | ||
2317 | if (!cluster) { | 2405 | DEBUGF("%s(file:%lx,count:0x%lx,buf:%lx,%s)\n", __func__, |
2318 | eof = true; | 2406 | file->firstcluster, sectorcount, (long)buf, |
2319 | if ( write ) { | 2407 | write ? "write":"read"); |
2320 | /* remember last cluster, in case | 2408 | DEBUGF("%s: sec:%lx numsec:%ld eof:%d\n", __func__, |
2321 | we want to append to the file */ | 2409 | sector, (long)sectornum, eof ? 1 : 0); |
2322 | sector = oldsector; | 2410 | |
2323 | cluster = oldcluster; | 2411 | eof = false; |
2324 | numsec = oldnumsec; | 2412 | |
2325 | clusternum--; | 2413 | if (!sector) |
2326 | i = -1; /* Error code */ | 2414 | { |
2327 | break; | 2415 | /* look up first sector of file */ |
2328 | } | 2416 | long newcluster = file->firstcluster; |
2329 | } | 2417 | |
2330 | else | 2418 | if (write && !newcluster) |
2331 | eof = false; | 2419 | { |
2420 | /* file is empty; try to allocate its first cluster */ | ||
2421 | newcluster = next_write_cluster(fat_bpb, 0); | ||
2422 | file->firstcluster = newcluster; | ||
2332 | } | 2423 | } |
2333 | else { | 2424 | |
2334 | if (sector) | 2425 | if (newcluster) |
2335 | sector++; | 2426 | { |
2336 | else { | 2427 | cluster = newcluster; |
2337 | /* look up first sector of file */ | 2428 | sector = cluster2sec(fat_bpb, cluster) - 1; |
2338 | sector = cluster2sec(IF_MV(fat_bpb,) file->firstcluster); | 2429 | |
2339 | numsec=1; | 2430 | #ifdef HAVE_FAT16SUPPORT |
2340 | #ifdef HAVE_FAT16SUPPORT | 2431 | if (fat_bpb->is_fat16 && file->firstcluster < 0) |
2341 | if (file->firstcluster < 0) | 2432 | { |
2342 | { /* FAT16 root dir */ | 2433 | sector += fat_bpb->rootdirsectornum; |
2343 | sector += fat_bpb->rootdiroffset; | 2434 | sectornum = fat_bpb->rootdirsectornum; |
2344 | numsec += fat_bpb->rootdiroffset; | ||
2345 | } | ||
2346 | #endif | ||
2347 | } | 2435 | } |
2436 | #endif /* HAVE_FAT16SUPPORT */ | ||
2348 | } | 2437 | } |
2438 | } | ||
2349 | 2439 | ||
2350 | if (!first) | 2440 | if (!sector) |
2351 | first = sector; | 2441 | { |
2442 | sectorcount = 0; | ||
2443 | eof = true; | ||
2444 | } | ||
2352 | 2445 | ||
2353 | if ( ((sector != first) && (sector != last+1)) || /* not sequential */ | 2446 | unsigned long transferred = 0; |
2354 | (last-first+1 == 256) ) { /* max 256 sectors per ata request */ | 2447 | unsigned long count = 0; |
2355 | long count = last - first + 1; | 2448 | unsigned long last = sector; |
2356 | rc = transfer(IF_MV(fat_bpb,) first, count, buf, write ); | ||
2357 | if (rc < 0) | ||
2358 | return rc * 10 - 1; | ||
2359 | 2449 | ||
2360 | buf = (char *)buf + count * SECTOR_SIZE; | 2450 | while (transferred + count < sectorcount) |
2361 | first = sector; | 2451 | { |
2452 | if (++sectornum >= fat_bpb->bpb_secperclus) | ||
2453 | { | ||
2454 | /* out of sectors in this cluster; get the next cluster */ | ||
2455 | long newcluster = write ? next_write_cluster(fat_bpb, cluster) : | ||
2456 | get_next_cluster(fat_bpb, cluster); | ||
2457 | if (newcluster) | ||
2458 | { | ||
2459 | cluster = newcluster; | ||
2460 | sector = cluster2sec(fat_bpb, cluster) - 1; | ||
2461 | clusternum++; | ||
2462 | sectornum = 0; | ||
2463 | |||
2464 | /* jumped clusters right at start? */ | ||
2465 | if (!count) | ||
2466 | last = sector; | ||
2467 | } | ||
2468 | else | ||
2469 | { | ||
2470 | sectornum--; /* remain in previous position */ | ||
2471 | eof = true; | ||
2472 | break; | ||
2473 | } | ||
2362 | } | 2474 | } |
2363 | 2475 | ||
2364 | if ((i == sectorcount-1) && /* last sector requested */ | 2476 | /* find sequential sectors and transfer them all at once */ |
2365 | (!eof)) | 2477 | if (sector != last || count >= FAT_MAX_TRANSFER_SIZE) |
2366 | { | 2478 | { |
2367 | long count = sector - first + 1; | 2479 | /* not sequential/over limit */ |
2368 | rc = transfer(IF_MV(fat_bpb,) first, count, buf, write ); | 2480 | rc = transfer(fat_bpb, last - count + 1, count, buf, write); |
2369 | if (rc < 0) | 2481 | if (rc < 0) |
2370 | return rc * 10 - 2; | 2482 | FAT_ERROR(rc * 10 - 2); |
2483 | |||
2484 | transferred += count; | ||
2485 | buf += count * SECTOR_SIZE; | ||
2486 | count = 0; | ||
2371 | } | 2487 | } |
2372 | 2488 | ||
2373 | last = sector; | 2489 | count++; |
2490 | last = ++sector; | ||
2374 | } | 2491 | } |
2375 | 2492 | ||
2376 | file->lastcluster = cluster; | 2493 | if (count) |
2377 | file->lastsector = sector; | 2494 | { |
2378 | file->clusternum = clusternum; | 2495 | /* transfer any remainder */ |
2379 | file->sectornum = numsec; | 2496 | rc = transfer(fat_bpb, last - count + 1, count, buf, write); |
2380 | file->eof = eof; | 2497 | if (rc < 0) |
2498 | FAT_ERROR(rc * 10 - 3); | ||
2499 | |||
2500 | transferred += count; | ||
2501 | } | ||
2502 | |||
2503 | rc = (eof && write) ? FAT_RC_ENOSPC : (long)transferred; | ||
2504 | fat_error: | ||
2505 | filestr->lastcluster = cluster; | ||
2506 | filestr->lastsector = sector; | ||
2507 | filestr->clusternum = clusternum; | ||
2508 | filestr->sectornum = sectornum; | ||
2509 | filestr->eof = eof; | ||
2510 | |||
2511 | if (rc >= 0) | ||
2512 | DEBUGF("Sectors transferred: %ld\n", rc); | ||
2381 | 2513 | ||
2382 | /* if eof, don't report last block as read/written */ | 2514 | return rc; |
2383 | if (eof) | 2515 | } |
2384 | i--; | ||
2385 | 2516 | ||
2386 | DEBUGF("Sectors written: %ld\n", i); | 2517 | void fat_rewind(struct fat_filestr *filestr) |
2387 | return i; | 2518 | { |
2519 | /* rewind the file position */ | ||
2520 | filestr->lastcluster = filestr->fatfilep->firstcluster; | ||
2521 | filestr->lastsector = 0; | ||
2522 | filestr->clusternum = 0; | ||
2523 | filestr->sectornum = FAT_RW_VAL; | ||
2524 | filestr->eof = false; | ||
2388 | } | 2525 | } |
2389 | 2526 | ||
2390 | int fat_seek(struct fat_file *file, unsigned long seeksector ) | 2527 | int fat_seek(struct fat_filestr *filestr, unsigned long seeksector) |
2391 | { | 2528 | { |
2392 | #ifdef HAVE_MULTIVOLUME | 2529 | const struct fat_file * const file = filestr->fatfilep; |
2393 | struct bpb* fat_bpb = &fat_bpbs[file->volume]; | 2530 | struct bpb * const fat_bpb = FAT_BPB(file->volume); |
2394 | #else | 2531 | if (!fat_bpb) |
2395 | struct bpb* fat_bpb = &fat_bpbs[0]; | 2532 | return -1; |
2396 | #endif | 2533 | |
2397 | long clusternum=0, numclusters=0, sectornum=0, sector=0; | 2534 | int rc; |
2398 | long cluster = file->firstcluster; | 2535 | long cluster = file->firstcluster; |
2399 | long i; | 2536 | unsigned long sector = 0; |
2537 | long clusternum = 0; | ||
2538 | unsigned long sectornum = FAT_RW_VAL; | ||
2400 | 2539 | ||
2401 | #ifdef HAVE_FAT16SUPPORT | 2540 | #ifdef HAVE_FAT16SUPPORT |
2402 | if (cluster < 0) /* FAT16 root dir */ | 2541 | if (fat_bpb->is_fat16 && cluster < 0) /* FAT16 root dir */ |
2403 | seeksector += fat_bpb->rootdiroffset; | 2542 | seeksector += fat_bpb->rootdirsectornum; |
2404 | #endif | 2543 | #endif /* HAVE_FAT16SUPPORT */ |
2544 | |||
2545 | filestr->eof = false; | ||
2546 | if (seeksector) | ||
2547 | { | ||
2548 | if (cluster == 0) | ||
2549 | { | ||
2550 | DEBUGF("Seeking beyond the end of empty file! " | ||
2551 | "(sector %lu, cluster %ld)\n", seeksector, | ||
2552 | seeksector / fat_bpb->bpb_secperclus); | ||
2553 | FAT_ERROR(FAT_SEEK_EOF); | ||
2554 | } | ||
2405 | 2555 | ||
2406 | file->eof = false; | ||
2407 | if (seeksector) { | ||
2408 | /* we need to find the sector BEFORE the requested, since | 2556 | /* we need to find the sector BEFORE the requested, since |
2409 | the file struct stores the last accessed sector */ | 2557 | the file struct stores the last accessed sector */ |
2410 | seeksector--; | 2558 | seeksector--; |
2411 | numclusters = clusternum = seeksector / fat_bpb->bpb_secperclus; | 2559 | clusternum = seeksector / fat_bpb->bpb_secperclus; |
2412 | sectornum = seeksector % fat_bpb->bpb_secperclus; | 2560 | sectornum = seeksector % fat_bpb->bpb_secperclus; |
2413 | 2561 | ||
2414 | if (file->clusternum && clusternum >= file->clusternum) | 2562 | long numclusters = clusternum; |
2563 | |||
2564 | if (filestr->clusternum && clusternum >= filestr->clusternum) | ||
2415 | { | 2565 | { |
2416 | cluster = file->lastcluster; | 2566 | /* seek forward from current position */ |
2417 | numclusters -= file->clusternum; | 2567 | cluster = filestr->lastcluster; |
2568 | numclusters -= filestr->clusternum; | ||
2418 | } | 2569 | } |
2419 | 2570 | ||
2420 | for (i=0; i<numclusters; i++) { | 2571 | for (long i = 0; i < numclusters; i++) |
2421 | cluster = get_next_cluster(IF_MV(fat_bpb,) cluster); | 2572 | { |
2422 | if (!cluster) { | 2573 | cluster = get_next_cluster(fat_bpb, cluster); |
2574 | |||
2575 | if (!cluster) | ||
2576 | { | ||
2423 | DEBUGF("Seeking beyond the end of the file! " | 2577 | DEBUGF("Seeking beyond the end of the file! " |
2424 | "(sector %ld, cluster %ld)\n", seeksector, i); | 2578 | "(sector %lu, cluster %ld)\n", seeksector, i); |
2425 | return -1; | 2579 | FAT_ERROR(FAT_SEEK_EOF); |
2426 | } | 2580 | } |
2427 | } | 2581 | } |
2428 | 2582 | ||
2429 | sector = cluster2sec(IF_MV(fat_bpb,) cluster) + sectornum; | 2583 | sector = cluster2sec(fat_bpb, cluster) + sectornum; |
2430 | } | ||
2431 | else { | ||
2432 | sectornum = -1; | ||
2433 | } | 2584 | } |
2434 | 2585 | ||
2435 | LDEBUGF("fat_seek(%lx, %lx) == %lx, %lx, %lx\n", | 2586 | DEBUGF("%s(%lx, %lx) == %lx, %lx, %lx\n", __func__, |
2436 | file->firstcluster, seeksector, cluster, sector, sectornum); | 2587 | file->firstcluster, seeksector, cluster, sector, sectornum); |
2437 | 2588 | ||
2438 | file->lastcluster = cluster; | 2589 | filestr->lastcluster = cluster; |
2439 | file->lastsector = sector; | 2590 | filestr->lastsector = sector; |
2440 | file->clusternum = clusternum; | 2591 | filestr->clusternum = clusternum; |
2441 | file->sectornum = sectornum + 1; | 2592 | filestr->sectornum = sectornum; |
2442 | return 0; | 2593 | |
2594 | rc = 0; | ||
2595 | fat_error: | ||
2596 | return rc; | ||
2443 | } | 2597 | } |
2444 | 2598 | ||
2445 | int fat_opendir(IF_MV(int volume,) | 2599 | int fat_truncate(const struct fat_filestr *filestr) |
2446 | struct fat_dir *dir, unsigned long startcluster, | ||
2447 | const struct fat_dir *parent_dir) | ||
2448 | { | 2600 | { |
2449 | #ifdef HAVE_MULTIVOLUME | 2601 | DEBUGF("%s(): %lX\n", __func__, filestr->lastcluster); |
2450 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 2602 | |
2451 | /* fixme: remove error check when done */ | 2603 | struct bpb * const fat_bpb = FAT_BPB(filestr->fatfilep->volume); |
2452 | if (volume >= NUM_VOLUMES || !fat_bpbs[volume].mounted) | 2604 | if (!fat_bpb) |
2453 | { | ||
2454 | LDEBUGF("fat_open() illegal volume %d\n", volume); | ||
2455 | return -1; | 2605 | return -1; |
2456 | } | ||
2457 | #else | ||
2458 | struct bpb* fat_bpb = &fat_bpbs[0]; | ||
2459 | #endif | ||
2460 | int rc; | ||
2461 | 2606 | ||
2462 | if (startcluster == 0) | 2607 | int rc = 1; |
2463 | startcluster = fat_bpb->bpb_rootclus; | ||
2464 | 2608 | ||
2465 | rc = fat_open(IF_MV(volume,) startcluster, &dir->file, parent_dir); | 2609 | long last = filestr->lastcluster; |
2466 | if(rc) | 2610 | long next = 0; |
2611 | |||
2612 | /* truncate trailing clusters after the current position */ | ||
2613 | if (last) | ||
2467 | { | 2614 | { |
2468 | DEBUGF( "fat_opendir() - Couldn't open dir" | 2615 | next = get_next_cluster(fat_bpb, last); |
2469 | " (error code %d)\n", rc); | 2616 | int rc2 = update_fat_entry(fat_bpb, last, FAT_EOF_MARK); |
2470 | return rc * 10 - 1; | 2617 | if (rc2 < 0) |
2618 | FAT_ERROR(rc2 * 10 - 2); | ||
2471 | } | 2619 | } |
2472 | |||
2473 | /* assign them after fat_open call so that fat_opendir can be called with the same | ||
2474 | * fat_dir as parent and result */ | ||
2475 | dir->entry = 0; | ||
2476 | dir->sector = 0; | ||
2477 | 2620 | ||
2478 | return 0; | 2621 | int rc2 = free_cluster_chain(fat_bpb, next); |
2622 | if (rc2 <= 0) | ||
2623 | { | ||
2624 | DEBUGF("Failed freeing cluster chain\n"); | ||
2625 | rc = 0; /* still partial success */ | ||
2626 | } | ||
2627 | |||
2628 | fat_error: | ||
2629 | return rc; | ||
2479 | } | 2630 | } |
2480 | 2631 | ||
2481 | int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) | 2632 | |
2633 | /** Directory stream functions **/ | ||
2634 | |||
2635 | int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan, | ||
2636 | struct filestr_cache *cachep, struct fat_direntry *entry) | ||
2482 | { | 2637 | { |
2483 | bool done = false; | 2638 | int rc = 0; |
2484 | int i, j; | 2639 | |
2485 | int rc; | 2640 | /* long file names are stored in special entries; each entry holds up to |
2486 | int order; | 2641 | 13 UTF-16 characters' thus, UTF-8 converted names can be max 255 chars |
2487 | unsigned char firstbyte; | 2642 | (1020 bytes) long, not including the trailing '\0'. */ |
2488 | /* Long file names are stored in special entries. Each entry holds | 2643 | struct fatlong_parse_state lnparse; |
2489 | up to 13 characters. Names can be max 255 chars (not bytes!) long */ | 2644 | fatlong_parse_start(&lnparse); |
2490 | /* The number of long entries in the long name can be retrieve from the first | ||
2491 | * long entry because there are stored in reverse order and have an ordinal */ | ||
2492 | int nb_longs = 0; | ||
2493 | /* The long entries are expected to be in order, so remember the last ordinal */ | ||
2494 | int last_long_ord = 0; | ||
2495 | 2645 | ||
2496 | dir->entrycount = 0; | 2646 | scan->entries = 0; |
2497 | 2647 | ||
2498 | while(!done) | 2648 | while (1) |
2499 | { | 2649 | { |
2500 | if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) | 2650 | unsigned int direntry = ++scan->entry; |
2651 | if (direntry >= MAX_DIRENTRIES) | ||
2501 | { | 2652 | { |
2502 | rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false); | 2653 | DEBUGF("%s() - Dir is too large (entry %u)\n", __func__, |
2503 | if (rc == 0) { | 2654 | direntry); |
2504 | /* eof */ | 2655 | FAT_ERROR(-1); |
2505 | entry->name[0] = 0; | 2656 | } |
2506 | break; | 2657 | |
2658 | unsigned long sector = direntry / DIR_ENTRIES_PER_SECTOR; | ||
2659 | if (cachep->sector != sector) | ||
2660 | { | ||
2661 | if (cachep->sector + 1 != sector) | ||
2662 | { | ||
2663 | /* Nothing cached or sector isn't contiguous */ | ||
2664 | int rc2 = fat_seek(dirstr, sector); | ||
2665 | if (rc2 < 0) | ||
2666 | FAT_ERROR(rc2 * 10 - 2); | ||
2507 | } | 2667 | } |
2508 | if (rc < 0) { | 2668 | |
2509 | DEBUGF( "fat_getnext() - Couldn't read dir" | 2669 | int rc2 = fat_readwrite(dirstr, 1, cachep->buffer, false); |
2510 | " (error code %d)\n", rc); | 2670 | if (rc2 <= 0) |
2511 | return rc * 10 - 1; | 2671 | { |
2672 | if (rc2 == 0) | ||
2673 | break; /* eof */ | ||
2674 | |||
2675 | DEBUGF("%s() - Couldn't read dir (err %d)\n", __func__, rc); | ||
2676 | FAT_ERROR(rc2 * 10 - 3); | ||
2512 | } | 2677 | } |
2513 | dir->sector = dir->file.lastsector; | 2678 | |
2679 | cachep->sector = sector; | ||
2514 | } | 2680 | } |
2515 | 2681 | ||
2516 | for (i = dir->entry % DIR_ENTRIES_PER_SECTOR; | 2682 | unsigned int index = direntry % DIR_ENTRIES_PER_SECTOR; |
2517 | i < DIR_ENTRIES_PER_SECTOR; i++) { | 2683 | union raw_dirent *ent = &((union raw_dirent *)cachep->buffer)[index]; |
2518 | unsigned int entrypos = i * DIR_ENTRY_SIZE; | ||
2519 | 2684 | ||
2520 | firstbyte = dir->sectorcache[entrypos]; | 2685 | if (ent->name[0] == 0) |
2521 | dir->entry++; | 2686 | break; /* last entry */ |
2522 | 2687 | ||
2523 | if (firstbyte == 0xe5) { | 2688 | if (ent->name[0] == 0xe5) |
2524 | /* free entry */ | 2689 | { |
2525 | dir->entrycount = 0; | 2690 | scan->entries = 0; |
2526 | continue; | 2691 | continue; /* free entry */ |
2527 | } | 2692 | } |
2528 | 2693 | ||
2529 | if (firstbyte == 0) { | 2694 | ++scan->entries; |
2530 | /* last entry */ | ||
2531 | entry->name[0] = 0; | ||
2532 | dir->entrycount = 0; | ||
2533 | return 0; | ||
2534 | } | ||
2535 | 2695 | ||
2536 | dir->entrycount++; | 2696 | if (IS_LDIR_ATTR(ent->ldir_attr)) |
2537 | 2697 | { | |
2538 | /* LFN entry? */ | 2698 | /* LFN entry */ |
2539 | if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] & | 2699 | if (UNLIKELY(!fatlong_parse_entry(&lnparse, ent, entry))) |
2540 | FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { | 2700 | { |
2541 | /* extract ordinal */ | 2701 | /* resync so we don't return just the short name if what we |
2542 | order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY; | 2702 | landed in the middle of is valid (directory changes |
2543 | /* is this entry the first long entry ? (first in order but containing last part) */ | 2703 | between calls likely created the situation; ignoring this |
2544 | if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) { | 2704 | case can be harmful elsewhere and is destructive to the |
2545 | /* check that order is not too big ! (and non-zero) */ | 2705 | entry series itself) */ |
2546 | if(order <= 0 || order > FATLONG_MAX_ORDER) | 2706 | struct bpb *fat_bpb = FAT_BPB(dirstr->fatfilep->volume); |
2547 | continue; /* ignore the whole LFN, will trigger lots of warnings */ | 2707 | if (!fat_bpb) |
2548 | nb_longs = order; | 2708 | FAT_ERROR(-4); |
2549 | last_long_ord = order; | 2709 | |
2550 | } | 2710 | dc_lock_cache(); |
2551 | else { | 2711 | |
2552 | /* check orphan entry */ | 2712 | while (--scan->entry != FAT_RW_VAL) /* at beginning? */ |
2553 | if (nb_longs == 0) { | 2713 | { |
2554 | logf("fat warning: orphan LFN entry"); | 2714 | ent = cache_direntry(fat_bpb, dirstr, scan->entry); |
2555 | /* ignore */ | ||
2556 | continue; | ||
2557 | } | ||
2558 | |||
2559 | /* check order */ | ||
2560 | if (order != (last_long_ord - 1)) { | ||
2561 | logf("fat warning: wrong LFN ordinal"); | ||
2562 | /* ignore the whole LFN, will trigger lots of warnings */ | ||
2563 | nb_longs = 0; | ||
2564 | } | ||
2565 | |||
2566 | last_long_ord = order; | ||
2567 | } | ||
2568 | 2715 | ||
2569 | /* copy part, reuse [order] for another purpose :) */ | 2716 | /* name[0] == 0 shouldn't happen here but... */ |
2570 | order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY; | 2717 | if (!ent || ent->name[0] == 0 || ent->name[0] == 0xe5 || |
2571 | for(j = 0; j < FATLONG_NAME_CHUNKS; j++) { | 2718 | !IS_LDIR_ATTR(ent->ldir_attr)) |
2572 | memcpy(dir->longname + order, | 2719 | break; |
2573 | dir->sectorcache + entrypos + FATLONG_NAME_POS[j], | ||
2574 | FATLONG_NAME_SIZE[j]); | ||
2575 | order += FATLONG_NAME_SIZE[j]; | ||
2576 | } | 2720 | } |
2721 | |||
2722 | dc_unlock_cache(); | ||
2723 | |||
2724 | /* retry it once from the new position */ | ||
2725 | scan->entries = 0; | ||
2726 | continue; | ||
2577 | } | 2727 | } |
2578 | else { | 2728 | } |
2579 | if ( parse_direntry(entry, dir->sectorcache + entrypos) ) { | 2729 | else if (!IS_VOL_ID_ATTR(ent->attr)) /* ignore volume id entry */ |
2580 | 2730 | { | |
2581 | /* don't return volume id entry */ | 2731 | rc = 1; |
2582 | if ( (entry->attr & | 2732 | |
2583 | (FAT_ATTR_VOLUME_ID|FAT_ATTR_DIRECTORY)) | 2733 | if (!fatlong_parse_finish(&lnparse, ent, entry)) |
2584 | == FAT_ATTR_VOLUME_ID) | 2734 | { |
2585 | continue; | 2735 | /* the long entries failed to pass all checks or there is |
2586 | 2736 | just a short entry. */ | |
2587 | /* replace shortname with longname? */ | 2737 | DEBUGF("SN-DOS:'%s'", entry->shortname); |
2588 | /* check that the long name is complete */ | 2738 | strcpy(entry->name, entry->shortname); |
2589 | if (nb_longs != 0 && last_long_ord == 1) { | 2739 | scan->entries = 1; |
2590 | /* hold a copy of the shortname in case the long one is too long */ | 2740 | rc = 2; /* name is OEM */ |
2591 | unsigned char shortname[13]; /* 8+3+dot+\0 */ | ||
2592 | int longname_utf8len = 0; | ||
2593 | /* One character at a time, add 1 for trailing \0, 4 is the maximum size | ||
2594 | * of a UTF8 encoded character in rockbox */ | ||
2595 | unsigned char longname_utf8segm[4 + 1]; | ||
2596 | unsigned short ucs; | ||
2597 | int segm_utf8len; | ||
2598 | /* Temporarily store short name */ | ||
2599 | strcpy(shortname, entry->name); | ||
2600 | entry->name[0] = 0; | ||
2601 | |||
2602 | /* Convert the FAT name to a utf8-encoded one. | ||
2603 | * The name is not necessary NUL-terminated ! */ | ||
2604 | for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) { | ||
2605 | ucs = dir->longname[j] | (dir->longname[j + 1] << 8); | ||
2606 | if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS) | ||
2607 | break; | ||
2608 | /* utf8encode will return a pointer after the converted | ||
2609 | * string, subtract the pointer to the start to get the length of it */ | ||
2610 | segm_utf8len = utf8encode(ucs, longname_utf8segm) - longname_utf8segm; | ||
2611 | |||
2612 | /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | ||
2613 | if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) { | ||
2614 | /* force use of short name */ | ||
2615 | longname_utf8len = FAT_FILENAME_BYTES + 1; | ||
2616 | break; /* fallback later */ | ||
2617 | } | ||
2618 | else { | ||
2619 | longname_utf8segm[segm_utf8len] = 0; | ||
2620 | strcat(entry->name + longname_utf8len, longname_utf8segm); | ||
2621 | longname_utf8len += segm_utf8len; | ||
2622 | } | ||
2623 | } | ||
2624 | |||
2625 | /* Does the utf8-encoded name fit into the entry? */ | ||
2626 | /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */ | ||
2627 | if (longname_utf8len >= FAT_FILENAME_BYTES) { | ||
2628 | /* Take the short DOS name. Need to utf8-encode it | ||
2629 | since it may contain chars from the upper half of | ||
2630 | the OEM code page which wouldn't be a valid utf8. | ||
2631 | Beware: this file will be shown with strange | ||
2632 | glyphs in file browser since unicode 0x80 to 0x9F | ||
2633 | are control characters. */ | ||
2634 | logf("SN-DOS: %s", shortname); | ||
2635 | unsigned char *utf8; | ||
2636 | utf8 = iso_decode(shortname, entry->name, -1, | ||
2637 | strlen(shortname)); | ||
2638 | *utf8 = 0; | ||
2639 | logf("SN: %s", entry->name); | ||
2640 | } else { | ||
2641 | logf("LN: %s", entry->name); | ||
2642 | logf("LNLen: %d", longname_utf8len); | ||
2643 | } | ||
2644 | } | ||
2645 | done = true; | ||
2646 | i++; | ||
2647 | break; | ||
2648 | } | ||
2649 | } | 2741 | } |
2742 | |||
2743 | DEBUGF("LN:\"%s\"", entry->name); | ||
2744 | break; | ||
2650 | } | 2745 | } |
2746 | } /* end while */ | ||
2747 | |||
2748 | fat_error: | ||
2749 | if (rc <= 0) | ||
2750 | { | ||
2751 | /* error or eod; stay on last good position */ | ||
2752 | fat_empty_fat_direntry(entry); | ||
2753 | scan->entry--; | ||
2754 | scan->entries = 0; | ||
2651 | } | 2755 | } |
2756 | |||
2757 | return rc; | ||
2758 | } | ||
2759 | |||
2760 | void fat_rewinddir(struct fat_dirscan_info *scan) | ||
2761 | { | ||
2762 | /* rewind the directory scan counter to the beginning */ | ||
2763 | scan->entry = FAT_RW_VAL; | ||
2764 | scan->entries = 0; | ||
2765 | } | ||
2766 | |||
2767 | |||
2768 | /** Mounting and unmounting functions **/ | ||
2769 | |||
2770 | bool fat_ismounted(IF_MV_NONVOID(int volume)) | ||
2771 | { | ||
2772 | return !!FAT_BPB(volume); | ||
2773 | } | ||
2774 | |||
2775 | int fat_mount(IF_MV(int volume,) IF_MD(int drive,) unsigned long startsector) | ||
2776 | { | ||
2777 | int rc; | ||
2778 | |||
2779 | struct bpb * const fat_bpb = &fat_bpbs[IF_MV_VOL(volume)]; | ||
2780 | if (fat_bpb->mounted) | ||
2781 | FAT_ERROR(-1); /* double mount */ | ||
2782 | |||
2783 | /* fill-in basic info first */ | ||
2784 | fat_bpb->startsector = startsector; | ||
2785 | #ifdef HAVE_MULTIVOLUME | ||
2786 | fat_bpb->volume = volume; | ||
2787 | #endif | ||
2788 | #ifdef HAVE_MULTIDRIVE | ||
2789 | fat_bpb->drive = drive; | ||
2790 | #endif | ||
2791 | |||
2792 | rc = fat_mount_internal(fat_bpb); | ||
2793 | if (rc < 0) | ||
2794 | FAT_ERROR(rc * 10 - 2); | ||
2795 | |||
2796 | /* it worked */ | ||
2797 | fat_bpb->mounted = true; | ||
2798 | |||
2799 | /* calculate freecount if unset */ | ||
2800 | if (fat_bpb->fsinfo.freecount == 0xffffffff) | ||
2801 | fat_recalc_free(IF_MV(fat_bpb->volume)); | ||
2802 | |||
2803 | DEBUGF("Freecount: %ld\n", (unsigned long)fat_bpb->fsinfo.freecount); | ||
2804 | DEBUGF("Nextfree: 0x%lx\n", (unsigned long)fat_bpb->fsinfo.nextfree); | ||
2805 | DEBUGF("Cluster count: 0x%lx\n", fat_bpb->dataclusters); | ||
2806 | DEBUGF("Sectors per cluster: %d\n", fat_bpb->bpb_secperclus); | ||
2807 | DEBUGF("FAT sectors: 0x%lx\n", fat_bpb->fatsize); | ||
2808 | |||
2809 | rc = 0; | ||
2810 | fat_error: | ||
2811 | return rc; | ||
2812 | } | ||
2813 | |||
2814 | int fat_unmount(IF_MV_NONVOID(int volume)) | ||
2815 | { | ||
2816 | struct bpb * const fat_bpb = FAT_BPB(volume); | ||
2817 | if (!fat_bpb) | ||
2818 | return -1; /* not mounted */ | ||
2819 | |||
2820 | /* free the entries for this volume */ | ||
2821 | cache_discard(IF_MV(fat_bpb)); | ||
2822 | fat_bpb->mounted = false; | ||
2823 | |||
2652 | return 0; | 2824 | return 0; |
2653 | } | 2825 | } |
2654 | 2826 | ||
2827 | |||
2828 | /** Debug screen stuff **/ | ||
2829 | |||
2830 | #ifdef MAX_LOG_SECTOR_SIZE | ||
2831 | int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)) | ||
2832 | { | ||
2833 | int bytes = 0; | ||
2834 | |||
2835 | struct bpb * const fat_bpb = FAT_BPB(volume); | ||
2836 | if (fat_bpb) | ||
2837 | bytes = fat_bpb->bpb_bytspersec; | ||
2838 | |||
2839 | return bytes; | ||
2840 | } | ||
2841 | #endif /* MAX_LOG_SECTOR_SIZE */ | ||
2842 | |||
2655 | unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)) | 2843 | unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)) |
2656 | { | 2844 | { |
2657 | #ifndef HAVE_MULTIVOLUME | 2845 | unsigned int size = 0; |
2658 | const int volume = 0; | 2846 | |
2659 | #endif | 2847 | struct bpb * const fat_bpb = FAT_BPB(volume); |
2660 | struct bpb* fat_bpb = &fat_bpbs[volume]; | 2848 | if (fat_bpb) |
2661 | return fat_bpb->bpb_secperclus * SECTOR_SIZE; | 2849 | size = fat_bpb->bpb_secperclus * SECTOR_SIZE; |
2850 | |||
2851 | return size; | ||
2662 | } | 2852 | } |
2663 | 2853 | ||
2664 | #ifdef HAVE_MULTIVOLUME | 2854 | void fat_recalc_free(IF_MV_NONVOID(int volume)) |
2665 | bool fat_ismounted(int volume) | ||
2666 | { | 2855 | { |
2667 | return (volume<NUM_VOLUMES && fat_bpbs[volume].mounted); | 2856 | struct bpb * const fat_bpb = FAT_BPB(volume); |
2857 | if (!fat_bpb) | ||
2858 | return; | ||
2859 | |||
2860 | dc_lock_cache(); | ||
2861 | fat_recalc_free_internal(fat_bpb); | ||
2862 | dc_unlock_cache(); | ||
2863 | } | ||
2864 | |||
2865 | bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free) | ||
2866 | { | ||
2867 | struct bpb * const fat_bpb = FAT_BPB(volume); | ||
2868 | if (!fat_bpb) | ||
2869 | return false; | ||
2870 | |||
2871 | unsigned long factor = fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024; | ||
2872 | |||
2873 | if (size) *size = fat_bpb->dataclusters * factor; | ||
2874 | if (free) *free = fat_bpb->fsinfo.freecount * factor; | ||
2875 | |||
2876 | return true; | ||
2877 | } | ||
2878 | |||
2879 | |||
2880 | /** Misc. **/ | ||
2881 | |||
2882 | void fat_empty_fat_direntry(struct fat_direntry *entry) | ||
2883 | { | ||
2884 | entry->name[0] = 0; | ||
2885 | entry->shortname[0] = 0; | ||
2886 | entry->attr = 0; | ||
2887 | entry->crttimetenth = 0; | ||
2888 | entry->crttime = 0; | ||
2889 | entry->crtdate = 0; | ||
2890 | entry->lstaccdate = 0; | ||
2891 | entry->wrttime = 0; | ||
2892 | entry->wrtdate = 0; | ||
2893 | entry->filesize = 0; | ||
2894 | entry->firstcluster = 0; | ||
2895 | } | ||
2896 | |||
2897 | time_t fattime_mktime(uint16_t fatdate, uint16_t fattime) | ||
2898 | { | ||
2899 | /* this knows our mktime() only uses these struct tm fields */ | ||
2900 | struct tm tm; | ||
2901 | tm.tm_sec = ((fattime ) & 0x1f) * 2; | ||
2902 | tm.tm_min = ((fattime >> 5) & 0x3f); | ||
2903 | tm.tm_hour = ((fattime >> 11) ); | ||
2904 | tm.tm_mday = ((fatdate ) & 0x1f); | ||
2905 | tm.tm_mon = ((fatdate >> 5) & 0x0f) - 1; | ||
2906 | tm.tm_year = ((fatdate >> 9) ) + 80; | ||
2907 | |||
2908 | return mktime(&tm); | ||
2909 | } | ||
2910 | |||
2911 | void fat_init(void) | ||
2912 | { | ||
2913 | dc_lock_cache(); | ||
2914 | |||
2915 | /* mark the possible volumes as not mounted */ | ||
2916 | for (unsigned int i = 0; i < NUM_VOLUMES; i++) | ||
2917 | { | ||
2918 | dc_discard_all(IF_MV(i)); | ||
2919 | fat_bpbs[i].mounted = false; | ||
2920 | } | ||
2921 | |||
2922 | dc_unlock_cache(); | ||
2668 | } | 2923 | } |
2669 | #endif | ||
diff --git a/firmware/export/config.h b/firmware/export/config.h index 5e4178cd4c..2a1cdb3416 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h | |||
@@ -27,12 +27,21 @@ | |||
27 | /* symbolic names for multiple choice configurations: */ | 27 | /* symbolic names for multiple choice configurations: */ |
28 | 28 | ||
29 | /* CONFIG_STORAGE (note these are combineable bit-flags) */ | 29 | /* CONFIG_STORAGE (note these are combineable bit-flags) */ |
30 | #define STORAGE_ATA 0x01 | 30 | #define STORAGE_ATA_NUM 0 |
31 | #define STORAGE_MMC 0x02 | 31 | #define STORAGE_MMC_NUM 1 |
32 | #define STORAGE_SD 0x04 | 32 | #define STORAGE_SD_NUM 2 |
33 | #define STORAGE_NAND 0x08 | 33 | #define STORAGE_NAND_NUM 3 |
34 | #define STORAGE_RAMDISK 0x10 | 34 | #define STORAGE_RAMDISK_NUM 4 |
35 | #define STORAGE_HOSTFS 0x20 /* meant for APPLICATION targets (implicit for SIMULATOR) */ | 35 | #define STORAGE_HOSTFS_NUM 5 |
36 | #define STORAGE_NUM_TYPES 6 | ||
37 | |||
38 | #define STORAGE_ATA (1 << STORAGE_ATA_NUM) | ||
39 | #define STORAGE_MMC (1 << STORAGE_MMC_NUM) | ||
40 | #define STORAGE_SD (1 << STORAGE_SD_NUM) | ||
41 | #define STORAGE_NAND (1 << STORAGE_NAND_NUM) | ||
42 | #define STORAGE_RAMDISK (1 << STORAGE_RAMDISK_NUM) | ||
43 | /* meant for APPLICATION targets (implicit for SIMULATOR) */ | ||
44 | #define STORAGE_HOSTFS (1 << STORAGE_HOSTFS_NUM) | ||
36 | 45 | ||
37 | /* CONFIG_TUNER (note these are combineable bit-flags) */ | 46 | /* CONFIG_TUNER (note these are combineable bit-flags) */ |
38 | #define S1A0903X01 0x01 /* Samsung */ | 47 | #define S1A0903X01 0x01 /* Samsung */ |
@@ -573,6 +582,8 @@ Lyre prototype 1 */ | |||
573 | #ifdef __PCTOOL__ | 582 | #ifdef __PCTOOL__ |
574 | #undef CONFIG_CPU | 583 | #undef CONFIG_CPU |
575 | #define CONFIG_CPU 0 | 584 | #define CONFIG_CPU 0 |
585 | #undef HAVE_MULTIVOLUME | ||
586 | #undef HAVE_MULTIDRIVE | ||
576 | #endif | 587 | #endif |
577 | 588 | ||
578 | #ifdef APPLICATION | 589 | #ifdef APPLICATION |
@@ -831,9 +842,11 @@ Lyre prototype 1 */ | |||
831 | * plenty of RAM. Both features can be enabled independently. */ | 842 | * plenty of RAM. Both features can be enabled independently. */ |
832 | #if (MEMORYSIZE >= 8) && !defined(BOOTLOADER) && !defined(__PCTOOL__) \ | 843 | #if (MEMORYSIZE >= 8) && !defined(BOOTLOADER) && !defined(__PCTOOL__) \ |
833 | && !defined(APPLICATION) | 844 | && !defined(APPLICATION) |
845 | #ifndef SIMULATOR | ||
834 | #define HAVE_DIRCACHE | 846 | #define HAVE_DIRCACHE |
847 | #endif | ||
835 | #ifdef HAVE_TAGCACHE | 848 | #ifdef HAVE_TAGCACHE |
836 | #define HAVE_TC_RAMCACHE | 849 | //#define HAVE_TC_RAMCACHE |
837 | #endif | 850 | #endif |
838 | #endif | 851 | #endif |
839 | 852 | ||
@@ -1178,7 +1191,7 @@ Lyre prototype 1 */ | |||
1178 | /* This attribute can be used to enable to detection of plugin file handles leaks. | 1191 | /* This attribute can be used to enable to detection of plugin file handles leaks. |
1179 | * When enabled, the plugin core will monitor open/close/creat and when the plugin exits | 1192 | * When enabled, the plugin core will monitor open/close/creat and when the plugin exits |
1180 | * will display an error message if the plugin leaked some file handles */ | 1193 | * will display an error message if the plugin leaked some file handles */ |
1181 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 1194 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined (SIMULATOR) |
1182 | #define HAVE_PLUGIN_CHECK_OPEN_CLOSE | 1195 | #define HAVE_PLUGIN_CHECK_OPEN_CLOSE |
1183 | #endif | 1196 | #endif |
1184 | 1197 | ||
diff --git a/firmware/export/config/gigabeats.h b/firmware/export/config/gigabeats.h index 11fc02be3d..6657134f9d 100644 --- a/firmware/export/config/gigabeats.h +++ b/firmware/export/config/gigabeats.h | |||
@@ -16,6 +16,9 @@ | |||
16 | /* define this if you use an ATA controller */ | 16 | /* define this if you use an ATA controller */ |
17 | #define CONFIG_STORAGE STORAGE_ATA | 17 | #define CONFIG_STORAGE STORAGE_ATA |
18 | 18 | ||
19 | /* For the Gigabeat S, we mount the second partition */ | ||
20 | #define CONFIG_DEFAULT_PARTNUM 1 | ||
21 | |||
19 | /*define this if the ATA controller and method of USB access support LBA48 */ | 22 | /*define this if the ATA controller and method of USB access support LBA48 */ |
20 | #define HAVE_LBA48 | 23 | #define HAVE_LBA48 |
21 | 24 | ||
diff --git a/firmware/export/disk.h b/firmware/export/disk.h index 8d6b41b5bd..c66028fe45 100644 --- a/firmware/export/disk.h +++ b/firmware/export/disk.h | |||
@@ -24,7 +24,8 @@ | |||
24 | #include "config.h" | 24 | #include "config.h" |
25 | #include "mv.h" /* for volume definitions */ | 25 | #include "mv.h" /* for volume definitions */ |
26 | 26 | ||
27 | struct partinfo { | 27 | struct partinfo |
28 | { | ||
28 | unsigned long start; /* first sector (LBA) */ | 29 | unsigned long start; /* first sector (LBA) */ |
29 | unsigned long size; /* number of sectors */ | 30 | unsigned long size; /* number of sectors */ |
30 | unsigned char type; | 31 | unsigned char type; |
@@ -35,11 +36,9 @@ struct partinfo { | |||
35 | #define PARTITION_TYPE_FAT16 0x06 | 36 | #define PARTITION_TYPE_FAT16 0x06 |
36 | #define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84 | 37 | #define PARTITION_TYPE_OS2_HIDDEN_C_DRIVE 0x84 |
37 | 38 | ||
38 | /* returns a pointer to an array of 8 partinfo structs */ | 39 | bool disk_init(IF_MD_NONVOID(int drive)); |
39 | struct partinfo* disk_init(IF_MD_NONVOID(int drive)); | 40 | bool disk_partinfo(int partition, struct partinfo *info); |
40 | struct partinfo* disk_partinfo(int partition); | ||
41 | 41 | ||
42 | void disk_init_subsystem(void) INIT_ATTR; /* Initialises mutexes */ | ||
43 | int disk_mount_all(void); /* returns the # of successful mounts */ | 42 | int disk_mount_all(void); /* returns the # of successful mounts */ |
44 | int disk_mount(int drive); | 43 | int disk_mount(int drive); |
45 | int disk_unmount_all(void); | 44 | int disk_unmount_all(void); |
@@ -50,4 +49,6 @@ int disk_unmount(int drive); | |||
50 | int disk_get_sector_multiplier(IF_MD_NONVOID(int drive)); | 49 | int disk_get_sector_multiplier(IF_MD_NONVOID(int drive)); |
51 | #endif | 50 | #endif |
52 | 51 | ||
53 | #endif | 52 | bool disk_present(IF_MD_NONVOID(int drive)); |
53 | |||
54 | #endif /* _DISK_H_ */ | ||
diff --git a/firmware/export/fat.h b/firmware/export/fat.h index a0d52acc35..3aa1e254dc 100644 --- a/firmware/export/fat.h +++ b/firmware/export/fat.h | |||
@@ -18,123 +18,168 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | |||
22 | #ifndef FAT_H | 21 | #ifndef FAT_H |
23 | #define FAT_H | 22 | #define FAT_H |
24 | 23 | ||
25 | #include <stdbool.h> | 24 | #include <stdbool.h> |
26 | #include "mv.h" /* for volume definitions */ | 25 | #include <sys/types.h> |
26 | #include <time.h> | ||
27 | #include "config.h" | 27 | #include "config.h" |
28 | #include "system.h" | 28 | #include "system.h" |
29 | #include "mv.h" /* for volume definitions */ | ||
30 | |||
31 | /******** | ||
32 | **** DO NOT use these functions directly unless otherwise noted. Required | ||
33 | **** synchronization is done by higher-level interfaces to minimize locking | ||
34 | **** overhead. | ||
35 | **** | ||
36 | **** Volume, drive, string, etc. parameters should also be checked by | ||
37 | **** callers for gross violations-- NULL strings, out-of-bounds values, | ||
38 | **** etc. | ||
39 | ****/ | ||
40 | |||
41 | /**************************************************************************** | ||
42 | ** Values that can be overridden by a target in config-[target].h | ||
43 | **/ | ||
44 | |||
45 | /* if your ATA implementation can do better, go right ahead and increase this | ||
46 | * value */ | ||
47 | #ifndef FAT_MAX_TRANSFER_SIZE | ||
48 | #define FAT_MAX_TRANSFER_SIZE 256 | ||
49 | #endif | ||
29 | 50 | ||
30 | /* This value can be overwritten by a target in config-[target].h, but | 51 | /* still experimental? */ |
31 | that behaviour is still experimental */ | 52 | /* increasing this will increase the total memory used by the cache; the |
53 | cache, as noted in disk_cache.h, has other minimum requirements that may | ||
54 | prevent reducing its number of entries in order to compensate */ | ||
32 | #ifndef SECTOR_SIZE | 55 | #ifndef SECTOR_SIZE |
33 | #define SECTOR_SIZE 512 | 56 | #define SECTOR_SIZE 512 |
34 | #endif | 57 | #endif |
35 | 58 | ||
59 | /** | ||
60 | ****************************************************************************/ | ||
61 | |||
62 | #define INVALID_SECNUM (0xfffffffeul) /* sequential, not FAT */ | ||
63 | #define FAT_MAX_FILE_SIZE (0xfffffffful) /* 2^32-1 bytes */ | ||
64 | #define MAX_DIRENTRIES 65536 | ||
65 | #define MAX_DIRECTORY_SIZE (MAX_DIRENTRIES*32) /* 2MB max size */ | ||
66 | |||
67 | /* these aren't I/O error conditions, so define specially; failure rc's | ||
68 | * shouldn't return the last digit as "0", therefore this is unambiguous */ | ||
69 | #define FAT_RC_ENOSPC (-10) | ||
70 | #define FAT_SEEK_EOF (-20) | ||
71 | |||
36 | /* Number of bytes reserved for a file name (including the trailing \0). | 72 | /* Number of bytes reserved for a file name (including the trailing \0). |
37 | Since names are stored in the entry as UTF-8, we won't be able to | 73 | Since names are stored in the entry as UTF-8, we won't be ble to |
38 | store all names allowed by FAT. In FAT, a name can have max 255 | 74 | store all names allowed by FAT. In FAT, a name can have max 255 |
39 | characters (not bytes!). Since the UTF-8 encoding of a char may take | 75 | characters (not bytes!). Since the UTF-8 encoding of a char may take |
40 | up to 4 bytes, there will be names that we won't be able to store | 76 | up to 4 bytes, there will be names that we won't be able to store |
41 | completely. For such names, the short DOS name is used. */ | 77 | completely. For such names, the short DOS name is used. */ |
42 | #define FAT_FILENAME_BYTES 256 | 78 | #define FAT_DIRENTRY_NAME_MAX 255 |
43 | |||
44 | struct fat_direntry | 79 | struct fat_direntry |
45 | { | 80 | { |
46 | unsigned char name[FAT_FILENAME_BYTES]; /* UTF-8 encoded name plus \0 */ | 81 | union { |
47 | unsigned short attr; /* Attributes */ | 82 | uint8_t name[255+1+4]; /* UTF-8 name plus \0 plus parse slop */ |
48 | unsigned char crttimetenth; /* Millisecond creation | 83 | uint16_t ucssegs[5+20][13]; /* UTF-16 segment buffer - layout saves... */ |
49 | time stamp (0-199) */ | 84 | }; /* ...130 bytes (important if stacked) */ |
50 | unsigned short crttime; /* Creation time */ | 85 | uint16_t ucsterm; /* allow one NULL-term after ucssegs */ |
51 | unsigned short crtdate; /* Creation date */ | 86 | uint8_t shortname[13]; /* DOS filename (OEM charset) */ |
52 | unsigned short lstaccdate; /* Last access date */ | 87 | uint8_t attr; /* file attributes */ |
53 | unsigned short wrttime; /* Last write time */ | 88 | uint8_t crttimetenth; /* millisecond creation time stamp (0-199) */ |
54 | unsigned short wrtdate; /* Last write date */ | 89 | uint16_t crttime; /* creation time */ |
55 | unsigned long filesize; /* File size in bytes */ | 90 | uint16_t crtdate; /* creation date */ |
56 | long firstcluster; /* fstclusterhi<<16 + fstcluslo */ | 91 | uint16_t lstaccdate; /* last access date */ |
92 | uint16_t wrttime; /* last write time */ | ||
93 | uint16_t wrtdate; /* last write date */ | ||
94 | uint32_t filesize; /* file size in bytes */ | ||
95 | int32_t firstcluster; /* first FAT cluster of file, 0 if empty */ | ||
57 | }; | 96 | }; |
58 | 97 | ||
59 | #define FAT_ATTR_READ_ONLY 0x01 | 98 | /* cursor structure used for scanning directories; holds the last-returned |
60 | #define FAT_ATTR_HIDDEN 0x02 | 99 | entry information */ |
61 | #define FAT_ATTR_SYSTEM 0x04 | 100 | struct fat_dirscan_info |
62 | #define FAT_ATTR_VOLUME_ID 0x08 | 101 | { |
63 | #define FAT_ATTR_DIRECTORY 0x10 | 102 | unsigned int entry; /* short dir entry index in parent */ |
64 | #define FAT_ATTR_ARCHIVE 0x20 | 103 | unsigned int entries; /* number of dir entries used */ |
65 | #define FAT_ATTR_VOLUME 0x40 /* this is a volume, not a real directory */ | 104 | }; |
105 | |||
106 | #define FAT_RW_VAL (0u - 1) | ||
66 | 107 | ||
108 | /* basic FAT file information about where to find a file and who houses it */ | ||
67 | struct fat_file | 109 | struct fat_file |
68 | { | 110 | { |
69 | long firstcluster; /* first cluster in file */ | ||
70 | long lastcluster; /* cluster of last access */ | ||
71 | long lastsector; /* sector of last access */ | ||
72 | long clusternum; /* current clusternum */ | ||
73 | long sectornum; /* sector number in this cluster */ | ||
74 | unsigned int direntry; /* short dir entry index from start of dir */ | ||
75 | unsigned int direntries; /* number of dir entries used by this file */ | ||
76 | long dircluster; /* first cluster of dir */ | ||
77 | bool eof; | ||
78 | #ifdef HAVE_MULTIVOLUME | 111 | #ifdef HAVE_MULTIVOLUME |
79 | int volume; /* file resides on which volume */ | 112 | int volume; /* file resides on which volume (first!) */ |
80 | #endif | 113 | #endif |
114 | long firstcluster; /* first cluster in file */ | ||
115 | long dircluster; /* first cluster of parent directory */ | ||
116 | struct fat_dirscan_info e; /* entry information */ | ||
81 | }; | 117 | }; |
82 | 118 | ||
83 | struct fat_dir | 119 | /* this stores what was last accessed when read or writing a file's data */ |
120 | struct fat_filestr | ||
84 | { | 121 | { |
85 | unsigned char sectorcache[SECTOR_SIZE] CACHEALIGN_ATTR; | 122 | struct fat_file *fatfilep; /* common file information */ |
86 | unsigned int entry; | 123 | long lastcluster; /* cluster of last access */ |
87 | unsigned int entrycount; | 124 | unsigned long lastsector; /* sector of last access */ |
88 | long sector; | 125 | long clusternum; /* cluster number of last access */ |
89 | struct fat_file file; | 126 | unsigned long sectornum; /* sector number within current cluster */ |
90 | /* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are | 127 | bool eof; /* end-of-file reached */ |
91 | * at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most | 128 | }; |
92 | * 13 characters, that a total of 260 characters. So we keep a buffer of that size. | 129 | |
93 | * Keep coherent with fat.c code. */ | 130 | /** File entity functions **/ |
94 | unsigned char longname[260 * 2]; | 131 | int fat_create_file(struct fat_file *parent, const char *name, |
95 | } CACHEALIGN_ATTR; | 132 | uint8_t attr, struct fat_file *file, |
96 | 133 | struct fat_direntry *fatent); | |
97 | #ifdef HAVE_HOTSWAP | 134 | bool fat_dir_is_parent(const struct fat_file *dir, const struct fat_file *file); |
98 | extern void fat_lock(void); | 135 | bool fat_file_is_same(const struct fat_file *file1, const struct fat_file *file2); |
99 | extern void fat_unlock(void); | 136 | int fat_fstat(struct fat_file *file, struct fat_direntry *entry); |
100 | #endif | 137 | int fat_open(const struct fat_file *parent, long startcluster, |
138 | struct fat_file *file); | ||
139 | int fat_open_rootdir(IF_MV(int volume,) struct fat_file *dir); | ||
140 | enum fat_remove_op /* what should fat_remove(), remove? */ | ||
141 | { | ||
142 | FAT_RM_DIRENTRIES = 0x1, /* remove only directory entries */ | ||
143 | FAT_RM_DATA = 0x2, /* remove only file data */ | ||
144 | FAT_RM_ALL = 0x3, /* remove all of above */ | ||
145 | }; | ||
146 | int fat_remove(struct fat_file *file, enum fat_remove_op what); | ||
147 | int fat_rename(struct fat_file *parent, struct fat_file *file, | ||
148 | const unsigned char *newname); | ||
101 | 149 | ||
102 | extern void fat_init(void); | 150 | /** File stream functions **/ |
103 | extern int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)); | 151 | int fat_closewrite(struct fat_filestr *filestr, uint32_t size, |
104 | extern int fat_mount(IF_MV(int volume,) IF_MD(int drive,) long startsector); | 152 | struct fat_direntry *fatentp); |
105 | extern int fat_unmount(int volume, bool flush); | 153 | void fat_filestr_init(struct fat_filestr *filestr, struct fat_file *file); |
106 | extern void fat_size(IF_MV(int volume,) /* public for info */ | 154 | unsigned long fat_query_sectornum(const struct fat_filestr *filestr); |
107 | unsigned long* size, | 155 | long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount, |
108 | unsigned long* free); | 156 | void *buf, bool write); |
109 | extern void fat_recalc_free(IF_MV_NONVOID(int volume)); /* public for debug info screen */ | 157 | void fat_rewind(struct fat_filestr *filestr); |
110 | extern int fat_create_dir(const char* name, | 158 | int fat_seek(struct fat_filestr *filestr, unsigned long sector); |
111 | struct fat_dir* newdir, | 159 | int fat_truncate(const struct fat_filestr *filestr); |
112 | struct fat_dir* dir); | ||
113 | extern int fat_open(IF_MV(int volume,) | ||
114 | long cluster, | ||
115 | struct fat_file* ent, | ||
116 | const struct fat_dir* dir); | ||
117 | extern int fat_create_file(const char* name, | ||
118 | struct fat_file* ent, | ||
119 | struct fat_dir* dir); | ||
120 | extern long fat_readwrite(struct fat_file *ent, long sectorcount, | ||
121 | void* buf, bool write ); | ||
122 | extern int fat_closewrite(struct fat_file *ent, long size, int attr); | ||
123 | extern int fat_seek(struct fat_file *ent, unsigned long sector ); | ||
124 | extern int fat_remove(struct fat_file *ent); | ||
125 | extern int fat_truncate(const struct fat_file *ent); | ||
126 | extern int fat_rename(struct fat_file* file, | ||
127 | struct fat_dir* dir, | ||
128 | const unsigned char* newname, | ||
129 | long size, int attr); | ||
130 | |||
131 | extern int fat_opendir(IF_MV(int volume,) | ||
132 | struct fat_dir *ent, unsigned long startcluster, | ||
133 | const struct fat_dir *parent_dir); | ||
134 | extern int fat_getnext(struct fat_dir *ent, struct fat_direntry *entry); | ||
135 | extern unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); /* public for debug info screen */ | ||
136 | extern bool fat_ismounted(int volume); | ||
137 | extern void* fat_get_sector_buffer(void); | ||
138 | extern void fat_release_sector_buffer(void); | ||
139 | 160 | ||
140 | #endif | 161 | /** Directory stream functions **/ |
162 | struct filestr_cache; | ||
163 | int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan, | ||
164 | struct filestr_cache *cachep, struct fat_direntry *entry); | ||
165 | void fat_rewinddir(struct fat_dirscan_info *scan); | ||
166 | |||
167 | /** Mounting and unmounting functions **/ | ||
168 | bool fat_ismounted(IF_MV_NONVOID(int volume)); | ||
169 | int fat_mount(IF_MV(int volume,) IF_MD(int drive,) unsigned long startsector); | ||
170 | int fat_unmount(IF_MV_NONVOID(int volume)); | ||
171 | |||
172 | /** Debug screen stuff **/ | ||
173 | #ifdef MAX_LOG_SECTOR_SIZE | ||
174 | int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)); | ||
175 | #endif /* MAX_LOG_SECTOR_SIZE */ | ||
176 | unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume)); | ||
177 | void fat_recalc_free(IF_MV_NONVOID(int volume)); | ||
178 | bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free); | ||
179 | |||
180 | /** Misc. **/ | ||
181 | time_t fattime_mktime(uint16_t fatdate, uint16_t fattime); | ||
182 | void fat_empty_fat_direntry(struct fat_direntry *entry); | ||
183 | void fat_init(void); | ||
184 | |||
185 | #endif /* FAT_H */ | ||
diff --git a/firmware/export/hostfs.h b/firmware/export/hostfs.h index bbadecec31..a24d009ca9 100644 --- a/firmware/export/hostfs.h +++ b/firmware/export/hostfs.h | |||
@@ -41,4 +41,29 @@ extern bool hostfs_removable(int drive); | |||
41 | extern bool hostfs_present(int drive); | 41 | extern bool hostfs_present(int drive); |
42 | #endif | 42 | #endif |
43 | 43 | ||
44 | /* This has to be repeated here for now for sim's sake since HAVE_HOSTFS | ||
45 | eats all the other stuff in storage.h. The sim probably shouldn't use | ||
46 | this. */ | ||
47 | #ifdef CONFIG_STORAGE_MULTI | ||
48 | extern int hostfs_driver_type(int drive); | ||
49 | #else | ||
50 | # ifdef APPLICATION | ||
51 | # define hostfs_driver_type(drive) (STORAGE_HOSTFS_NUM) | ||
52 | # else /* !APPLICATION */ | ||
53 | # if (CONFIG_STORAGE & STORAGE_ATA) | ||
54 | # define hostfs_driver_type(drive) (STORAGE_ATA_NUM) | ||
55 | # elif (CONFIG_STORAGE & STORAGE_SD) | ||
56 | # define hostfs_driver_type(drive) (STORAGE_SD_NUM) | ||
57 | # elif (CONFIG_STORAGE & STORAGE_MMC) | ||
58 | # define hostfs_driver_type(drive) (STORAGE_MMC_NUM) | ||
59 | # elif (CONFIG_STORAGE & STORAGE_NAND) | ||
60 | # define hostfs_driver_type(drive) (STORAGE_NAND_NUM) | ||
61 | # elif (CONFIG_STORAGE & STORAGE_RAMDISK) | ||
62 | # define hostfs_driver_type(drive) (STORAGE_RAMDISK_NUM) | ||
63 | # else | ||
64 | # error Unknown storage driver | ||
65 | # endif /* CONFIG_STORAGE */ | ||
66 | # endif /* APPLICATION */ | ||
67 | #endif /* CONFIG_STORAGE_MULTI */ | ||
68 | |||
44 | #endif /* HOSTFS_H */ | 69 | #endif /* HOSTFS_H */ |
diff --git a/firmware/export/load_code.h b/firmware/export/load_code.h index cca577044e..df85a81f82 100644 --- a/firmware/export/load_code.h +++ b/firmware/export/load_code.h | |||
@@ -45,7 +45,17 @@ static inline void lc_close(void *handle) { (void)handle; } | |||
45 | 45 | ||
46 | #elif (CONFIG_PLATFORM & PLATFORM_HOSTED) | 46 | #elif (CONFIG_PLATFORM & PLATFORM_HOSTED) |
47 | 47 | ||
48 | #ifdef APPLICATION | ||
49 | /* App doesn't simulate code loading from a buffer */ | ||
50 | static inline void * lc_open_from_mem(void *addr, size_t blob_size) | ||
51 | { | ||
52 | return NULL; | ||
53 | (void)addr; (void)blob_size; | ||
54 | } | ||
55 | #else | ||
48 | extern void *lc_open_from_mem(void* addr, size_t blob_size); | 56 | extern void *lc_open_from_mem(void* addr, size_t blob_size); |
57 | #endif | ||
58 | |||
49 | extern void *lc_get_header(void *handle); | 59 | extern void *lc_get_header(void *handle); |
50 | extern void lc_close(void *handle); | 60 | extern void lc_close(void *handle); |
51 | 61 | ||
diff --git a/firmware/export/mv.h b/firmware/export/mv.h index 1d0a536663..620d77d30d 100644 --- a/firmware/export/mv.h +++ b/firmware/export/mv.h | |||
@@ -45,23 +45,44 @@ | |||
45 | #define IF_MV(x...) x /* valist contents or empty */ | 45 | #define IF_MV(x...) x /* valist contents or empty */ |
46 | #define IF_MV_NONVOID(x...) x /* valist contents or 'void' */ | 46 | #define IF_MV_NONVOID(x...) x /* valist contents or 'void' */ |
47 | #define IF_MV_VOL(v) v /* volume argument or '0' */ | 47 | #define IF_MV_VOL(v) v /* volume argument or '0' */ |
48 | /* how to name volumes, first char must be outside of legal file names, | ||
49 | a number gets appended to enumerate, if applicable */ | ||
50 | #if (CONFIG_STORAGE & STORAGE_MMC) | ||
51 | #define VOL_NAMES "<MMC%d>" | ||
52 | #define VOL_ENUM_POS 4 /* position of %d, to avoid runtime calculation */ | ||
53 | #elif (CONFIG_STORAGE & STORAGE_SD) | ||
54 | #define VOL_NAMES "<microSD%d>" | ||
55 | #define VOL_ENUM_POS 8 /* position of %d, to avoid runtime calculation */ | ||
56 | #else | ||
57 | #define VOL_NAMES "<HD%d>" | ||
58 | #define VOL_ENUM_POS 3 | ||
59 | #endif /* CONFIG_STORAGE */ | ||
60 | 48 | ||
61 | #ifdef HAVE_HOTSWAP | 49 | /* Format: "/<DEC###>/foo/bar" |
62 | bool volume_removable(int volume); | 50 | * The "DEC" is pure decoration and treated as a comment. Only an unbroken |
63 | bool volume_present(int volume); | 51 | * trailing string of digits within the brackets is parsed as the volume |
52 | * number. | ||
53 | * | ||
54 | * IMPORTANT!: Adjust VOL_DEC_MAX_LEN if needed to the longest of these | ||
55 | */ | ||
56 | #define DEFAULT_VOL_DEC "Volume" | ||
57 | |||
58 | #if (CONFIG_STORAGE & STORAGE_ATA) | ||
59 | #define ATA_VOL_DEC "HDD" | ||
60 | #endif | ||
61 | #if (CONFIG_STORAGE & STORAGE_MMC) | ||
62 | #define MMC_VOL_DEC "MMC" | ||
63 | #endif | ||
64 | #if (CONFIG_STORAGE & STORAGE_SD) | ||
65 | #define SD_VOL_DEC "microSD" | ||
66 | #endif | ||
67 | #if (CONFIG_STORAGE & STORAGE_NAND) | ||
68 | #define NAND_VOL_DEC "NAND" | ||
69 | #endif | ||
70 | #if (CONFIG_STORAGE & STORAGE_RAMDISK) | ||
71 | #define RAMDISK_VOL_DEC "RAMDisk" | ||
72 | #endif | ||
73 | #if (CONFIG_STORAGE & STORAGE_HOSTFS) | ||
74 | #ifndef HOSTFS_VOL_DEC /* overridable */ | ||
75 | #define HOSTFS_VOL_DEC DEFAULT_VOL_DEC | ||
64 | #endif | 76 | #endif |
77 | #endif | ||
78 | |||
79 | /* Characters that delimit a volume specifier at any root point in the path. | ||
80 | The tokens must be outside of legal filename characters */ | ||
81 | #define VOL_START_TOK '<' | ||
82 | #define VOL_END_TOK '>' | ||
83 | #define VOL_DEC_MAX_LEN 7 /* biggest of all xxx_VOL_DEC defines */ | ||
84 | #define VOL_MAX_LEN (1 + VOL_DEC_MAX_LEN + 2 + 1) | ||
85 | #define VOL_NUM_MAX 100 | ||
65 | 86 | ||
66 | #else /* empty definitions if no multi-volume */ | 87 | #else /* empty definitions if no multi-volume */ |
67 | #define IF_MV(x...) | 88 | #define IF_MV(x...) |
@@ -69,4 +90,30 @@ bool volume_present(int volume); | |||
69 | #define IF_MV_VOL(v) 0 | 90 | #define IF_MV_VOL(v) 0 |
70 | #endif /* HAVE_MULTIVOLUME */ | 91 | #endif /* HAVE_MULTIVOLUME */ |
71 | 92 | ||
93 | #define CHECK_VOL(volume) \ | ||
94 | ((unsigned int)IF_MV_VOL(volume) < NUM_VOLUMES) | ||
95 | |||
96 | #define CHECK_DRV(drive) \ | ||
97 | ((unsigned int)IF_MD_DRV(drive) < NUM_DRIVES) | ||
98 | |||
99 | /* Volume-centric functions (in disk.c) */ | ||
100 | void volume_recalc_free(IF_MV_NONVOID(int volume)); | ||
101 | unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume)); | ||
102 | void volume_size(IF_MV(int volume,) unsigned long *size, unsigned long *free); | ||
103 | bool volume_ismounted(IF_MV_NONVOID(int volume)); | ||
104 | #ifdef HAVE_HOTSWAP | ||
105 | bool volume_removable(int volume); | ||
106 | bool volume_present(int volume); | ||
107 | #endif /* HAVE_HOTSWAP */ | ||
108 | |||
109 | #ifdef HAVE_MULTIDRIVE | ||
110 | int volume_drive(int volume); | ||
111 | #else /* !HAVE_MULTIDRIVE */ | ||
112 | static inline int volume_drive(int volume) | ||
113 | { | ||
114 | return 0; | ||
115 | (void)volume; | ||
116 | } | ||
117 | #endif /* HAVE_MULTIDRIVE */ | ||
118 | |||
72 | #endif /* __MV_H__ */ | 119 | #endif /* __MV_H__ */ |
diff --git a/firmware/export/pathfuncs.h b/firmware/export/pathfuncs.h new file mode 100644 index 0000000000..26eb4a1067 --- /dev/null +++ b/firmware/export/pathfuncs.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _PATHFUNCS_H_ | ||
22 | #define _PATHFUNCS_H_ | ||
23 | |||
24 | #include <sys/types.h> | ||
25 | #define __need_size_t | ||
26 | #include <stddef.h> | ||
27 | #include <stdbool.h> | ||
28 | #include "config.h" | ||
29 | |||
30 | /* useful char constants that could be reconfigured if desired */ | ||
31 | #define PATH_SEPCH '/' | ||
32 | #define PATH_SEPSTR "/" | ||
33 | #define PATH_ROOTSTR "/" | ||
34 | #define PATH_BADSEPCH '\\' | ||
35 | #define PATH_DRVSEPCH ':' | ||
36 | |||
37 | /* a nicer way to check for "." and ".." than two strcmp() calls */ | ||
38 | static inline bool is_dotdir_name(const char *name) | ||
39 | { | ||
40 | return name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2])); | ||
41 | } | ||
42 | |||
43 | static inline bool name_is_dot(const char *name) | ||
44 | { | ||
45 | return name[0] == '.' && !name[1]; | ||
46 | } | ||
47 | |||
48 | static inline bool name_is_dot_dot(const char *name) | ||
49 | { | ||
50 | return name[0] == '.' && name[1] == '.' && !name[2]; | ||
51 | } | ||
52 | |||
53 | /* return a pointer to the character following path separators */ | ||
54 | #define GOBBLE_PATH_SEPCH(p) \ | ||
55 | ({ int _c; \ | ||
56 | const char *_p = (p); \ | ||
57 | while ((_c = *_p) == PATH_SEPCH) \ | ||
58 | ++_p; \ | ||
59 | _p; }) | ||
60 | |||
61 | /* return a pointer to the character following a path component which may | ||
62 | be a separator or the terminating nul */ | ||
63 | #define GOBBLE_PATH_COMP(p) \ | ||
64 | ({ int _c; \ | ||
65 | const char *_p = (p); \ | ||
66 | while ((_c = *_p) && _c != PATH_SEPCH) \ | ||
67 | ++_p; \ | ||
68 | _p; }) | ||
69 | |||
70 | /* does the character terminate a path component? */ | ||
71 | #define IS_COMP_TERMINATOR(c) \ | ||
72 | ({ int _c = (c); \ | ||
73 | !_c || _c == PATH_SEPCH; }) | ||
74 | |||
75 | #ifdef HAVE_MULTIVOLUME | ||
76 | int path_strip_volume(const char *name, const char **nameptr, bool greedy); | ||
77 | int get_volume_name(int volume, char *name); | ||
78 | #endif | ||
79 | |||
80 | int path_strip_drive(const char *name, const char **nameptr, bool greedy); | ||
81 | size_t path_trim_whitespace(const char *name, const char **nameptr); | ||
82 | size_t path_basename(const char *name, const char **nameptr); | ||
83 | size_t path_dirname(const char *name, const char **nameptr); | ||
84 | size_t path_strip_trailing_separators(const char *name, const char **nameptr); | ||
85 | void path_correct_separators(char *dstpath, const char *path); | ||
86 | |||
87 | /* constants useable in basepath and component */ | ||
88 | #define PA_SEP_HARD NULL /* separate even if base is empty */ | ||
89 | #define PA_SEP_SOFT "" /* separate only if base is nonempty */ | ||
90 | size_t path_append(char *buffer, const char *basepath, const char *component, | ||
91 | size_t bufsize); | ||
92 | ssize_t parse_path_component(const char **pathp, const char **namep); | ||
93 | |||
94 | /* return true if path begins with a root '/' component and is not NULL */ | ||
95 | static inline bool path_is_absolute(const char *path) | ||
96 | { | ||
97 | return path && path[0] == PATH_SEPCH; | ||
98 | } | ||
99 | |||
100 | #endif /* _PATHFUNCS_H_ */ | ||
diff --git a/firmware/export/rbpaths.h b/firmware/export/rbpaths.h index 3600709482..b04d669716 100644 --- a/firmware/export/rbpaths.h +++ b/firmware/export/rbpaths.h | |||
@@ -70,27 +70,6 @@ | |||
70 | 70 | ||
71 | #define HOME_DIR_LEN (sizeof(HOME_DIR)-1) | 71 | #define HOME_DIR_LEN (sizeof(HOME_DIR)-1) |
72 | 72 | ||
73 | #ifdef APPLICATION | ||
74 | |||
75 | #include <dirent.h> | ||
76 | #include <fcntl.h> | ||
77 | #include <unistd.h> | ||
78 | |||
79 | int app_open(const char *name, int o, ...); | ||
80 | int app_creat(const char* name, mode_t mode); | ||
81 | int app_remove(const char *name); | ||
82 | int app_rename(const char *old, const char *new); | ||
83 | DIR* app_opendir(const char *_name); | ||
84 | int app_closedir(DIR *dir); | ||
85 | struct dirent* app_readdir(DIR* dir); | ||
86 | int app_mkdir(const char* name); | ||
87 | int app_rmdir(const char* name); | ||
88 | ssize_t app_readlink(const char *path, char *buf, size_t bufsiz); | ||
89 | |||
90 | extern void paths_init(void); | ||
91 | |||
92 | #endif | ||
93 | |||
94 | #define REC_BASE_DIR HOME_DIR | 73 | #define REC_BASE_DIR HOME_DIR |
95 | #define PLAYLIST_CATALOG_DEFAULT_DIR HOME_DIR "/Playlists" | 74 | #define PLAYLIST_CATALOG_DEFAULT_DIR HOME_DIR "/Playlists" |
96 | 75 | ||
diff --git a/firmware/export/storage.h b/firmware/export/storage.h index 8e7281d523..14cba09b35 100644 --- a/firmware/export/storage.h +++ b/firmware/export/storage.h | |||
@@ -93,6 +93,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; } | |||
93 | #define storage_removable(drive) hostfs_removable(IF_MD(drive)) | 93 | #define storage_removable(drive) hostfs_removable(IF_MD(drive)) |
94 | #define storage_present(drive) hostfs_present(IF_MD(drive)) | 94 | #define storage_present(drive) hostfs_present(IF_MD(drive)) |
95 | #endif | 95 | #endif |
96 | #define storage_driver_type(drive) hostfs_driver_type(IF_MV(drive)) | ||
96 | #elif (CONFIG_STORAGE & STORAGE_ATA) | 97 | #elif (CONFIG_STORAGE & STORAGE_ATA) |
97 | #define STORAGE_FUNCTION(NAME) (ata_## NAME) | 98 | #define STORAGE_FUNCTION(NAME) (ata_## NAME) |
98 | #define storage_spindown ata_spindown | 99 | #define storage_spindown ata_spindown |
@@ -119,6 +120,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; } | |||
119 | #define storage_removable(drive) ata_removable(IF_MD(drive)) | 120 | #define storage_removable(drive) ata_removable(IF_MD(drive)) |
120 | #define storage_present(drive) ata_present(IF_MD(drive)) | 121 | #define storage_present(drive) ata_present(IF_MD(drive)) |
121 | #endif | 122 | #endif |
123 | #define storage_driver_type(drive) (STORAGE_ATA_NUM) | ||
122 | #elif (CONFIG_STORAGE & STORAGE_SD) | 124 | #elif (CONFIG_STORAGE & STORAGE_SD) |
123 | #define STORAGE_FUNCTION(NAME) (sd_## NAME) | 125 | #define STORAGE_FUNCTION(NAME) (sd_## NAME) |
124 | #define storage_spindown sd_spindown | 126 | #define storage_spindown sd_spindown |
@@ -145,6 +147,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; } | |||
145 | #define storage_removable(drive) sd_removable(IF_MD(drive)) | 147 | #define storage_removable(drive) sd_removable(IF_MD(drive)) |
146 | #define storage_present(drive) sd_present(IF_MD(drive)) | 148 | #define storage_present(drive) sd_present(IF_MD(drive)) |
147 | #endif | 149 | #endif |
150 | #define storage_driver_type(drive) (STORAGE_SD_NUM) | ||
148 | #elif (CONFIG_STORAGE & STORAGE_MMC) | 151 | #elif (CONFIG_STORAGE & STORAGE_MMC) |
149 | #define STORAGE_FUNCTION(NAME) (mmc_## NAME) | 152 | #define STORAGE_FUNCTION(NAME) (mmc_## NAME) |
150 | #define storage_spindown mmc_spindown | 153 | #define storage_spindown mmc_spindown |
@@ -170,6 +173,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; } | |||
170 | #define storage_removable(drive) mmc_removable(IF_MD(drive)) | 173 | #define storage_removable(drive) mmc_removable(IF_MD(drive)) |
171 | #define storage_present(drive) mmc_present(IF_MD(drive)) | 174 | #define storage_present(drive) mmc_present(IF_MD(drive)) |
172 | #endif | 175 | #endif |
176 | #define storage_driver_type(drive) (STORAGE_MMC_NUM) | ||
173 | #elif (CONFIG_STORAGE & STORAGE_NAND) | 177 | #elif (CONFIG_STORAGE & STORAGE_NAND) |
174 | #define STORAGE_FUNCTION(NAME) (nand_## NAME) | 178 | #define STORAGE_FUNCTION(NAME) (nand_## NAME) |
175 | #define storage_spindown nand_spindown | 179 | #define storage_spindown nand_spindown |
@@ -195,6 +199,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; } | |||
195 | #define storage_removable(drive) nand_removable(IF_MD(drive)) | 199 | #define storage_removable(drive) nand_removable(IF_MD(drive)) |
196 | #define storage_present(drive) nand_present(IF_MD(drive)) | 200 | #define storage_present(drive) nand_present(IF_MD(drive)) |
197 | #endif | 201 | #endif |
202 | #define storage_driver_type(drive) (STORAGE_NAND_NUM) | ||
198 | #elif (CONFIG_STORAGE & STORAGE_RAMDISK) | 203 | #elif (CONFIG_STORAGE & STORAGE_RAMDISK) |
199 | #define STORAGE_FUNCTION(NAME) (ramdisk_## NAME) | 204 | #define STORAGE_FUNCTION(NAME) (ramdisk_## NAME) |
200 | #define storage_spindown ramdisk_spindown | 205 | #define storage_spindown ramdisk_spindown |
@@ -220,6 +225,7 @@ static inline void stub_storage_spindown(int timeout) { (void)timeout; } | |||
220 | #define storage_removable(drive) ramdisk_removable(IF_MD(drive)) | 225 | #define storage_removable(drive) ramdisk_removable(IF_MD(drive)) |
221 | #define storage_present(drive) ramdisk_present(IF_MD(drive)) | 226 | #define storage_present(drive) ramdisk_present(IF_MD(drive)) |
222 | #endif | 227 | #endif |
228 | #define storage_driver_type(drive) (STORAGE_RAMDISK_NUM) | ||
223 | #else | 229 | #else |
224 | //#error No storage driver! | 230 | //#error No storage driver! |
225 | #endif | 231 | #endif |
@@ -246,6 +252,7 @@ void storage_get_info(int drive, struct storage_info *info); | |||
246 | bool storage_removable(int drive); | 252 | bool storage_removable(int drive); |
247 | bool storage_present(int drive); | 253 | bool storage_present(int drive); |
248 | #endif | 254 | #endif |
255 | int storage_driver_type(int drive); | ||
249 | 256 | ||
250 | #endif /* NOT CONFIG_STORAGE_MULTI and NOT SIMULATOR*/ | 257 | #endif /* NOT CONFIG_STORAGE_MULTI and NOT SIMULATOR*/ |
251 | 258 | ||
diff --git a/firmware/export/system.h b/firmware/export/system.h index 4442eb96d7..47dd858d81 100644 --- a/firmware/export/system.h +++ b/firmware/export/system.h | |||
@@ -104,6 +104,10 @@ int get_cpu_boost_counter(void); | |||
104 | /* return number of elements in array a */ | 104 | /* return number of elements in array a */ |
105 | #define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0])) | 105 | #define ARRAYLEN(a) (sizeof(a)/sizeof((a)[0])) |
106 | 106 | ||
107 | /* is the given pointer "p" inside the said bounds of array "a"? */ | ||
108 | #define PTR_IN_ARRAY(a, p, numelem) \ | ||
109 | ((uintptr_t)(p) - (uintptr_t)(a) < (uintptr_t)(numelem)*sizeof ((a)[0])) | ||
110 | |||
107 | /* return p incremented by specified number of bytes */ | 111 | /* return p incremented by specified number of bytes */ |
108 | #define SKIPBYTES(p, count) ((typeof (p))((char *)(p) + (count))) | 112 | #define SKIPBYTES(p, count) ((typeof (p))((char *)(p) + (count))) |
109 | 113 | ||
@@ -188,9 +192,13 @@ enum { | |||
188 | #include "system-target.h" | 192 | #include "system-target.h" |
189 | #elif defined(HAVE_SDL) /* SDL build */ | 193 | #elif defined(HAVE_SDL) /* SDL build */ |
190 | #include "system-sdl.h" | 194 | #include "system-sdl.h" |
195 | #ifdef SIMULATOR | ||
196 | #include "system-sim.h" | ||
197 | #endif | ||
191 | #elif defined(__PCTOOL__) | 198 | #elif defined(__PCTOOL__) |
192 | #include "system-sdl.h" | 199 | #include "system-hosted.h" |
193 | #endif | 200 | #endif |
201 | |||
194 | #include "bitswap.h" | 202 | #include "bitswap.h" |
195 | #include "rbendian.h" | 203 | #include "rbendian.h" |
196 | 204 | ||
diff --git a/firmware/include/dir.h b/firmware/include/dir.h index 6e8b70588e..f7719823a9 100644 --- a/firmware/include/dir.h +++ b/firmware/include/dir.h | |||
@@ -18,46 +18,71 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | |||
22 | #ifndef _DIR_H_ | 21 | #ifndef _DIR_H_ |
23 | #define _DIR_H_ | 22 | #define _DIR_H_ |
24 | 23 | ||
24 | #include <sys/types.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <time.h> | ||
25 | #include "config.h" | 27 | #include "config.h" |
28 | #include "fs_attr.h" | ||
26 | 29 | ||
27 | #define ATTR_READ_ONLY 0x01 | 30 | #if defined (APPLICATION) |
28 | #define ATTR_HIDDEN 0x02 | 31 | #include "filesystem-app.h" |
29 | #define ATTR_SYSTEM 0x04 | 32 | #elif defined(SIMULATOR) || defined(__PCTOOL__) |
30 | #define ATTR_VOLUME_ID 0x08 | 33 | #include "../../uisimulator/common/filesystem-sim.h" |
31 | #define ATTR_DIRECTORY 0x10 | ||
32 | #define ATTR_ARCHIVE 0x20 | ||
33 | #define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */ | ||
34 | #define ATTR_LINK 0x80 | ||
35 | |||
36 | #ifdef HAVE_DIRCACHE | ||
37 | # include "dircache.h" | ||
38 | # define DIR DIR_CACHED | ||
39 | # define dirent dirent_cached | ||
40 | # define opendir opendir_cached | ||
41 | # define closedir closedir_cached | ||
42 | # define readdir readdir_cached | ||
43 | # define closedir closedir_cached | ||
44 | # define mkdir mkdir_cached | ||
45 | # define rmdir rmdir_cached | ||
46 | #else | 34 | #else |
47 | # include "dir_uncached.h" | 35 | #include "filesystem-native.h" |
48 | # define DIR DIR_UNCACHED | 36 | #endif |
49 | # define dirent dirent_uncached | 37 | |
50 | # define opendir opendir_uncached | 38 | #ifndef DIRFUNCTIONS_DEFINED |
51 | # define closedir closedir_uncached | 39 | #ifndef opendir |
52 | # define readdir readdir_uncached | 40 | #define opendir FS_PREFIX(opendir) |
53 | # define closedir closedir_uncached | 41 | #endif |
54 | # define mkdir mkdir_uncached | 42 | #ifndef readdir |
55 | # define rmdir rmdir_uncached | 43 | #define readdir FS_PREFIX(readdir) |
44 | #endif | ||
45 | #ifndef readdir_r | ||
46 | #define readdir_r FS_PREFIX(readdir_r) | ||
47 | #endif | ||
48 | #ifndef rewinddir | ||
49 | #define rewinddir FS_PREFIX(rewinddir) | ||
56 | #endif | 50 | #endif |
51 | #ifndef closedir | ||
52 | #define closedir FS_PREFIX(closedir) | ||
53 | #endif | ||
54 | #ifndef mkdir | ||
55 | #define mkdir FS_PREFIX(mkdir) | ||
56 | #endif | ||
57 | #ifndef rmdir | ||
58 | #define rmdir FS_PREFIX(rmdir) | ||
59 | #endif | ||
60 | #ifndef samedir | ||
61 | #define samedir FS_PREFIX(samedir) | ||
62 | #endif | ||
63 | #ifndef dir_exists | ||
64 | #define dir_exists FS_PREFIX(dir_exists) | ||
65 | #endif | ||
66 | #endif /* !DIRFUNCTIONS_DEFINED */ | ||
57 | 67 | ||
68 | #ifndef DIRENT_DEFINED | ||
69 | struct DIRENT | ||
70 | { | ||
71 | struct dirinfo_native info; /* platform extra info */ | ||
72 | char d_name[MAX_PATH]; /* UTF-8 name of entry (last!) */ | ||
73 | }; | ||
74 | #endif /* DIRENT_DEFINED */ | ||
58 | 75 | ||
59 | typedef DIR* (*opendir_func)(const char* name); | 76 | struct dirinfo |
60 | typedef int (*closedir_func)(DIR* dir); | 77 | { |
61 | typedef struct dirent* (*readdir_func)(DIR* dir); | 78 | unsigned int attribute; /* attribute bits of file */ |
79 | off_t size; /* binary size of file */ | ||
80 | time_t mtime; /* local file time */ | ||
81 | }; | ||
62 | 82 | ||
63 | #endif | 83 | #ifndef DIRFUNCTIONS_DECLARED |
84 | /* TIP: set errno to zero before calling to see if anything failed */ | ||
85 | struct dirinfo dir_get_info(DIR *dirp, struct DIRENT *entry); | ||
86 | #endif /* !DIRFUNCTIONS_DECLARED */ | ||
87 | |||
88 | #endif /* _DIR_H_ */ | ||
diff --git a/firmware/include/dir_uncached.h b/firmware/include/dir_uncached.h deleted file mode 100644 index 6443d5ba97..0000000000 --- a/firmware/include/dir_uncached.h +++ /dev/null | |||
@@ -1,107 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: dir.h 13741 2007-06-30 02:08:27Z jethead71 $ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _DIR_UNCACHED_H_ | ||
22 | #define _DIR_UNCACHED_H_ | ||
23 | |||
24 | #include "config.h" | ||
25 | |||
26 | struct dirinfo { | ||
27 | int attribute; | ||
28 | long size; | ||
29 | unsigned short wrtdate; | ||
30 | unsigned short wrttime; | ||
31 | }; | ||
32 | |||
33 | #include <stdbool.h> | ||
34 | #include "file.h" | ||
35 | |||
36 | #if defined(SIMULATOR) || defined(__PCTOOL__) | ||
37 | # define dirent_uncached sim_dirent | ||
38 | # define DIR_UNCACHED SIM_DIR | ||
39 | # define opendir_uncached sim_opendir | ||
40 | # define readdir_uncached sim_readdir | ||
41 | # define closedir_uncached sim_closedir | ||
42 | # define mkdir_uncached sim_mkdir | ||
43 | # define rmdir_uncached sim_rmdir | ||
44 | #elif defined(APPLICATION) | ||
45 | # include "rbpaths.h" | ||
46 | # define DIRENT_DEFINED | ||
47 | # define DIR_DEFINED | ||
48 | # define dirent_uncached dirent | ||
49 | # define DIR_UNCACHED DIR | ||
50 | # define opendir_uncached app_opendir | ||
51 | # define readdir_uncached app_readdir | ||
52 | # define closedir_uncached app_closedir | ||
53 | # define mkdir_uncached app_mkdir | ||
54 | # define rmdir_uncached app_rmdir | ||
55 | #endif | ||
56 | |||
57 | |||
58 | #ifndef DIRENT_DEFINED | ||
59 | struct dirent_uncached { | ||
60 | unsigned char d_name[MAX_PATH]; | ||
61 | struct dirinfo info; | ||
62 | long startcluster; | ||
63 | }; | ||
64 | #endif | ||
65 | |||
66 | #include "fat.h" | ||
67 | |||
68 | #ifndef DIR_DEFINED | ||
69 | typedef struct { | ||
70 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
71 | struct fat_dir fatdir CACHEALIGN_ATTR; | ||
72 | bool busy; | ||
73 | long startcluster; | ||
74 | struct dirent_uncached theent; | ||
75 | #ifdef HAVE_MULTIVOLUME | ||
76 | int volumecounter; /* running counter for faked volume entries */ | ||
77 | #endif | ||
78 | #else | ||
79 | /* simulator/application: */ | ||
80 | void *dir; /* actually a DIR* dir */ | ||
81 | char *name; | ||
82 | #endif | ||
83 | } DIR_UNCACHED CACHEALIGN_ATTR; | ||
84 | #endif | ||
85 | |||
86 | #ifdef HAVE_HOTSWAP | ||
87 | char *get_volume_name(int volume); | ||
88 | #endif | ||
89 | |||
90 | #ifdef HAVE_MULTIVOLUME | ||
91 | int strip_volume(const char*, char*); | ||
92 | #endif | ||
93 | |||
94 | #ifndef DIRFUNCTIONS_DEFINED | ||
95 | |||
96 | extern DIR_UNCACHED* opendir_uncached(const char* name); | ||
97 | extern int closedir_uncached(DIR_UNCACHED* dir); | ||
98 | extern int mkdir_uncached(const char* name); | ||
99 | extern int rmdir_uncached(const char* name); | ||
100 | |||
101 | extern struct dirent_uncached* readdir_uncached(DIR_UNCACHED* dir); | ||
102 | |||
103 | extern int release_dirs(int volume); | ||
104 | |||
105 | #endif /* DIRFUNCTIONS_DEFINED */ | ||
106 | |||
107 | #endif | ||
diff --git a/firmware/include/dircache.h b/firmware/include/dircache.h index 019ccf49b7..7e8c764e7f 100644 --- a/firmware/include/dircache.h +++ b/firmware/include/dircache.h | |||
@@ -8,6 +8,7 @@ | |||
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2005 by Miika Pekkarinen | 10 | * Copyright (C) 2005 by Miika Pekkarinen |
11 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | 12 | * |
12 | * This program is free software; you can redistribute it and/or | 13 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 14 | * modify it under the terms of the GNU General Public License |
@@ -21,84 +22,154 @@ | |||
21 | #ifndef _DIRCACHE_H | 22 | #ifndef _DIRCACHE_H |
22 | #define _DIRCACHE_H | 23 | #define _DIRCACHE_H |
23 | 24 | ||
24 | #include "config.h" | 25 | #include "mv.h" |
25 | #include "dir_uncached.h" | 26 | #include <string.h> /* size_t */ |
26 | #include <string.h> /* size_t */ | 27 | #include <sys/types.h> /* ssize_t */ |
27 | 28 | ||
28 | #ifdef HAVE_DIRCACHE | 29 | #ifdef HAVE_DIRCACHE |
29 | 30 | ||
30 | #define DIRCACHE_RESERVE (1024*64) | 31 | /**************************************************************************** |
31 | #define DIRCACHE_LIMIT (1024*1024*6) | 32 | ** Configurable values |
32 | 33 | **/ | |
33 | #define DIRCACHE_APPFLAG_TAGCACHE 0x0001 | 34 | |
34 | #define DIRCACHE_APPFLAG_PLAYLIST 0x0002 | 35 | #if 0 |
35 | 36 | /* enable dumping code */ | |
36 | /* Internal structures. */ | 37 | #define DIRCACHE_DUMPSTER |
37 | struct travel_data { | 38 | #define DIRCACHE_DUMPSTER_BIN "/dircache_dump.bin" |
38 | struct dircache_entry *first; | 39 | #define DIRCACHE_DUMPSTER_CSV "/dircache_dump.csv" |
39 | struct dircache_entry *ce; | ||
40 | struct dircache_entry *down_entry; | ||
41 | #if (CONFIG_PLATFORM & PLATFORM_HOSTED) | ||
42 | DIR_UNCACHED *dir, *newdir; | ||
43 | struct dirent_uncached *entry; | ||
44 | #else | ||
45 | struct fat_dir *dir; | ||
46 | struct fat_dir newdir; | ||
47 | struct fat_direntry entry; | ||
48 | #endif | 40 | #endif |
49 | int pathpos; | 41 | |
42 | /* dircache builds won't search below this but will work down to this point | ||
43 | while below it the cache will just pass requests through to the storage; | ||
44 | the limiting factor is the scanning thread stack size, not the | ||
45 | implementation -- tune the two together */ | ||
46 | #define DIRCACHE_MAX_DEPTH 15 | ||
47 | #define DIRCACHE_STACK_SIZE (DEFAULT_STACK_SIZE + 0x100) | ||
48 | |||
49 | /* memory buffer constants that control allocation */ | ||
50 | #define DIRCACHE_RESERVE (1024*64) /* 64 KB - new entry slack */ | ||
51 | #define DIRCACHE_MIN (1024*1024*1) /* 1 MB - provision min size */ | ||
52 | #define DIRCACHE_LIMIT (1024*1024*6) /* 6 MB - provision max size */ | ||
53 | |||
54 | /* make it easy to change serialnumber size without modifying anything else; | ||
55 | 32 bits allows 21845 builds before wrapping in a 6MB cache that is filled | ||
56 | exclusively with entries and nothing else (32 byte entries), making that | ||
57 | figure pessimistic */ | ||
58 | typedef uint32_t dc_serial_t; | ||
59 | |||
60 | /** | ||
61 | ****************************************************************************/ | ||
62 | |||
63 | #if CONFIG_PLATFORM & PLATFORM_NATIVE | ||
64 | /* native dircache is lower-level than on a hosted target */ | ||
65 | #define DIRCACHE_NATIVE | ||
66 | #endif | ||
67 | |||
68 | struct dircache_file | ||
69 | { | ||
70 | int idx; /* this file's cache index */ | ||
71 | dc_serial_t serialnum; /* this file's serial number */ | ||
50 | }; | 72 | }; |
51 | 73 | ||
52 | struct dirent_cached { | 74 | enum dircache_status |
53 | struct dirinfo info; | 75 | { |
54 | char *d_name; | 76 | DIRCACHE_IDLE = 0, /* no volume is initialized */ |
55 | long startcluster; | 77 | DIRCACHE_SCANNING = 1, /* dircache is scanning a volume */ |
78 | DIRCACHE_READY = 2, /* dircache is ready to be used */ | ||
79 | }; | ||
80 | |||
81 | /** Dircache control **/ | ||
82 | void dircache_wait(void); | ||
83 | void dircache_suspend(void); | ||
84 | int dircache_resume(void); | ||
85 | int dircache_enable(void); | ||
86 | void dircache_disable(void); | ||
87 | void dircache_free_buffer(void); | ||
88 | |||
89 | /** Volume mounting **/ | ||
90 | void dircache_mount(void); /* always tries building everything it can */ | ||
91 | void dircache_unmount(IF_MV_NONVOID(int volume)); | ||
92 | |||
93 | |||
94 | /** File API service functions **/ | ||
95 | |||
96 | /* avoid forcing #include of file_internal.h, fat.h and dir.h */ | ||
97 | struct filestr_base; | ||
98 | struct file_base_info; | ||
99 | struct file_base_binding; | ||
100 | struct dirent; | ||
101 | struct dirscan_info; | ||
102 | struct dirinfo_native; | ||
103 | |||
104 | int dircache_readdir_dirent(struct filestr_base *stream, | ||
105 | struct dirscan_info *scanp, | ||
106 | struct dirent *entry); | ||
107 | void dircache_rewinddir_dirent(struct dirscan_info *scanp); | ||
108 | |||
109 | #ifdef DIRCACHE_NATIVE | ||
110 | struct fat_direntry; | ||
111 | int dircache_readdir_internal(struct filestr_base *stream, | ||
112 | struct file_base_info *infop, | ||
113 | struct fat_direntry *fatent); | ||
114 | void dircache_rewinddir_internal(struct file_base_info *info); | ||
115 | #endif /* DIRCACHE_NATIVE */ | ||
116 | |||
117 | |||
118 | /** Dircache live updating **/ | ||
119 | |||
120 | void dircache_get_rootinfo(struct file_base_info *infop); | ||
121 | void dircache_bind_file(struct file_base_binding *bindp); | ||
122 | void dircache_unbind_file(struct file_base_binding *bindp); | ||
123 | void dircache_fileop_create(struct file_base_info *dirinfop, | ||
124 | struct file_base_binding *bindp, | ||
125 | const char *basename, | ||
126 | const struct dirinfo_native *dinp); | ||
127 | void dircache_fileop_rename(struct file_base_info *dirinfop, | ||
128 | struct file_base_binding *bindp, | ||
129 | const char *basename); | ||
130 | void dircache_fileop_remove(struct file_base_binding *bindp); | ||
131 | void dircache_fileop_sync(struct file_base_binding *infop, | ||
132 | const struct dirinfo_native *dinp); | ||
133 | |||
134 | |||
135 | /** Dircache paths and files **/ | ||
136 | ssize_t dircache_get_path(const struct dircache_file *dcfilep, char *buf, | ||
137 | size_t size); | ||
138 | int dircache_get_file(const char *path, struct dircache_file *dcfilep); | ||
139 | |||
140 | |||
141 | /** Debug screen/info stuff **/ | ||
142 | |||
143 | struct dircache_info | ||
144 | { | ||
145 | enum dircache_status status; /* current composite status value */ | ||
146 | const char *statusdesc; /* pointer to string describing 'status' */ | ||
147 | size_t last_size; /* cache size after last build */ | ||
148 | size_t size; /* total size of entries (with holes) */ | ||
149 | size_t sizeused; /* bytes of 'size' actually utilized */ | ||
150 | size_t size_limit; /* maximum possible size */ | ||
151 | size_t reserve; /* size of reserve area */ | ||
152 | size_t reserve_used; /* amount of reserve used */ | ||
153 | unsigned int entry_count; /* number of cache entries */ | ||
154 | long build_ticks; /* total time used to build cache */ | ||
56 | }; | 155 | }; |
57 | 156 | ||
58 | typedef struct { | 157 | void dircache_get_info(struct dircache_info *info); |
59 | bool busy; | 158 | #ifdef DIRCACHE_DUMPSTER |
60 | struct dirent_cached theent; /* .attribute is set to -1 on init(opendir) */ | 159 | void dircache_dump(void); |
61 | int internal_entry; /* the current entry in the directory */ | 160 | #endif /* DIRCACHE_DUMPSTER */ |
62 | DIR_UNCACHED *regulardir; | 161 | |
63 | } DIR_CACHED; | ||
64 | 162 | ||
65 | void dircache_init(void) INIT_ATTR; | 163 | /** Misc. stuff **/ |
164 | void dircache_dcfile_init(struct dircache_file *dcfilep); | ||
165 | |||
166 | #ifdef HAVE_EEPROM_SETTINGS | ||
66 | int dircache_load(void); | 167 | int dircache_load(void); |
67 | int dircache_save(void); | 168 | int dircache_save(void); |
68 | int dircache_build(int last_size); | 169 | #endif /* HAVE_EEPROM_SETTINGS */ |
69 | void* dircache_steal_buffer(size_t *size); | ||
70 | bool dircache_is_enabled(void); | ||
71 | bool dircache_is_initializing(void); | ||
72 | void dircache_set_appflag(long mask); | ||
73 | bool dircache_get_appflag(long mask); | ||
74 | int dircache_get_entry_count(void); | ||
75 | int dircache_get_cache_size(void); | ||
76 | int dircache_get_reserve_used(void); | ||
77 | int dircache_get_build_ticks(void); | ||
78 | void dircache_disable(void); | ||
79 | void dircache_suspend(void); | ||
80 | bool dircache_resume(void); | ||
81 | int dircache_get_entry_id(const char *filename); | ||
82 | size_t dircache_copy_path(int index, char *buf, size_t size); | ||
83 | |||
84 | /* the next two are internal for file.c */ | ||
85 | long _dircache_get_entry_startcluster(int id); | ||
86 | struct dirinfo* _dircache_get_entry_dirinfo(int id); | ||
87 | |||
88 | void dircache_bind(int fd, const char *path); | ||
89 | void dircache_update_filesize(int fd, long newsize, long startcluster); | ||
90 | void dircache_update_filetime(int fd); | ||
91 | void dircache_mkdir(const char *path); | ||
92 | void dircache_rmdir(const char *path); | ||
93 | void dircache_remove(const char *name); | ||
94 | void dircache_rename(const char *oldpath, const char *newpath); | ||
95 | void dircache_add_file(const char *path, long startcluster); | ||
96 | |||
97 | DIR_CACHED* opendir_cached(const char* name); | ||
98 | struct dirent_cached* readdir_cached(DIR_CACHED* dir); | ||
99 | int closedir_cached(DIR_CACHED *dir); | ||
100 | int mkdir_cached(const char *name); | ||
101 | int rmdir_cached(const char* name); | ||
102 | #endif /* !HAVE_DIRCACHE */ | ||
103 | 170 | ||
104 | #endif | 171 | void dircache_init(size_t last_size) INIT_ATTR; |
172 | |||
173 | #endif /* HAVE_DIRCACHE */ | ||
174 | |||
175 | #endif /* _DIRCACHE_H */ | ||
diff --git a/firmware/include/dircache_redirect.h b/firmware/include/dircache_redirect.h new file mode 100644 index 0000000000..15fb4bc38d --- /dev/null +++ b/firmware/include/dircache_redirect.h | |||
@@ -0,0 +1,198 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _DIRCACHE_REDIRECT_H_ | ||
22 | |||
23 | #include "dir.h" | ||
24 | |||
25 | /*** | ||
26 | ** Internal redirects that depend upon whether or not dircache is made | ||
27 | **/ | ||
28 | |||
29 | /** File binding **/ | ||
30 | |||
31 | static inline void get_rootinfo_internal(struct file_base_info *infop) | ||
32 | { | ||
33 | #ifdef HAVE_DIRCACHE | ||
34 | dircache_get_rootinfo(infop); | ||
35 | #else | ||
36 | (void)infop; | ||
37 | #endif | ||
38 | } | ||
39 | |||
40 | static inline void fileobj_bind_file(struct file_base_binding *bindp) | ||
41 | { | ||
42 | #ifdef HAVE_DIRCACHE | ||
43 | dircache_bind_file(bindp); | ||
44 | #else | ||
45 | file_binding_insert_last(bindp); | ||
46 | #endif | ||
47 | } | ||
48 | |||
49 | static inline void fileobj_unbind_file(struct file_base_binding *bindp) | ||
50 | { | ||
51 | #ifdef HAVE_DIRCACHE | ||
52 | dircache_unbind_file(bindp); | ||
53 | #else | ||
54 | file_binding_remove(bindp); | ||
55 | #endif | ||
56 | } | ||
57 | |||
58 | |||
59 | /** File event handlers **/ | ||
60 | |||
61 | static inline void fileop_onopen_internal(struct filestr_base *stream, | ||
62 | struct file_base_info *srcinfop, | ||
63 | unsigned int callflags) | ||
64 | { | ||
65 | fileobj_fileop_open(stream, srcinfop, callflags); | ||
66 | } | ||
67 | |||
68 | static inline void fileop_onclose_internal(struct filestr_base *stream) | ||
69 | { | ||
70 | fileobj_fileop_close(stream); | ||
71 | } | ||
72 | |||
73 | static inline void fileop_oncreate_internal(struct filestr_base *stream, | ||
74 | struct file_base_info *srcinfop, | ||
75 | unsigned int callflags, | ||
76 | struct file_base_info *dirinfop, | ||
77 | const char *basename) | ||
78 | { | ||
79 | #ifdef HAVE_DIRCACHE | ||
80 | dircache_dcfile_init(&srcinfop->dcfile); | ||
81 | #endif | ||
82 | fileobj_fileop_create(stream, srcinfop, callflags); | ||
83 | #ifdef HAVE_DIRCACHE | ||
84 | struct dirinfo_native din; | ||
85 | fill_dirinfo_native(&din); | ||
86 | dircache_fileop_create(dirinfop, stream->bindp, basename, &din); | ||
87 | #endif | ||
88 | (void)dirinfop; (void)basename; | ||
89 | } | ||
90 | |||
91 | static inline void fileop_onremove_internal(struct filestr_base *stream, | ||
92 | struct file_base_info *oldinfop) | ||
93 | { | ||
94 | fileobj_fileop_remove(stream, oldinfop); | ||
95 | #ifdef HAVE_DIRCACHE | ||
96 | dircache_fileop_remove(stream->bindp); | ||
97 | #endif | ||
98 | } | ||
99 | |||
100 | static inline void fileop_onrename_internal(struct filestr_base *stream, | ||
101 | struct file_base_info *oldinfop, | ||
102 | struct file_base_info *dirinfop, | ||
103 | const char *basename) | ||
104 | { | ||
105 | fileobj_fileop_rename(stream, oldinfop); | ||
106 | #ifdef HAVE_DIRCACHE | ||
107 | dircache_fileop_rename(dirinfop, stream->bindp, basename); | ||
108 | #endif | ||
109 | (void)dirinfop; (void)basename; | ||
110 | } | ||
111 | |||
112 | static inline void fileop_onsync_internal(struct filestr_base *stream) | ||
113 | { | ||
114 | fileobj_fileop_sync(stream); | ||
115 | #ifdef HAVE_DIRCACHE | ||
116 | struct dirinfo_native din; | ||
117 | fill_dirinfo_native(&din); | ||
118 | dircache_fileop_sync(stream->bindp, &din); | ||
119 | #endif | ||
120 | } | ||
121 | |||
122 | static inline void fileop_ontruncate_internal(struct filestr_base *stream) | ||
123 | { | ||
124 | fileobj_fileop_truncate(stream); | ||
125 | } | ||
126 | |||
127 | static inline void volume_onmount_internal(IF_MV_NONVOID(int volume)) | ||
128 | { | ||
129 | #ifdef HAVE_DIRCACHE | ||
130 | dircache_mount(); | ||
131 | #endif | ||
132 | IF_MV( (void)volume; ) | ||
133 | } | ||
134 | |||
135 | static inline void volume_onunmount_internal(IF_MV_NONVOID(int volume)) | ||
136 | { | ||
137 | fileobj_mgr_unmount(IF_MV(volume)); | ||
138 | #ifdef HAVE_DIRCACHE | ||
139 | dircache_unmount(IF_MV(volume)); | ||
140 | #endif | ||
141 | } | ||
142 | |||
143 | |||
144 | /** Directory reading **/ | ||
145 | |||
146 | static inline int readdir_dirent(struct filestr_base *stream, | ||
147 | struct dirscan_info *scanp, | ||
148 | struct dirent *entry) | ||
149 | { | ||
150 | #ifdef HAVE_DIRCACHE | ||
151 | return dircache_readdir_dirent(stream, scanp, entry); | ||
152 | #else | ||
153 | return uncached_readdir_dirent(stream, scanp, entry); | ||
154 | #endif | ||
155 | } | ||
156 | |||
157 | static inline void rewinddir_dirent(struct dirscan_info *scanp) | ||
158 | { | ||
159 | #ifdef HAVE_DIRCACHE | ||
160 | dircache_rewinddir_dirent(scanp); | ||
161 | #else | ||
162 | uncached_rewinddir_dirent(scanp); | ||
163 | #endif | ||
164 | } | ||
165 | |||
166 | static inline int readdir_internal(struct filestr_base *stream, | ||
167 | struct file_base_info *infop, | ||
168 | struct fat_direntry *fatent) | ||
169 | { | ||
170 | #ifdef HAVE_DIRCACHE | ||
171 | return dircache_readdir_internal(stream, infop, fatent); | ||
172 | #else | ||
173 | return uncached_readdir_internal(stream, infop, fatent); | ||
174 | #endif | ||
175 | } | ||
176 | |||
177 | static inline void rewinddir_internal(struct file_base_info *infop) | ||
178 | { | ||
179 | #ifdef HAVE_DIRCACHE | ||
180 | dircache_rewinddir_internal(infop); | ||
181 | #else | ||
182 | uncached_rewinddir_internal(infop); | ||
183 | #endif | ||
184 | } | ||
185 | |||
186 | |||
187 | /** Misc. stuff **/ | ||
188 | |||
189 | static inline struct fat_direntry *get_dir_fatent_dircache(void) | ||
190 | { | ||
191 | #ifdef HAVE_DIRCACHE | ||
192 | return get_dir_fatent(); | ||
193 | #else | ||
194 | return NULL; | ||
195 | #endif | ||
196 | } | ||
197 | |||
198 | #endif /* _DIRCACHE_REDIRECT_H_ */ | ||
diff --git a/firmware/include/disk_cache.h b/firmware/include/disk_cache.h new file mode 100644 index 0000000000..725b3778cc --- /dev/null +++ b/firmware/include/disk_cache.h | |||
@@ -0,0 +1,83 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef DISK_CACHE_H | ||
22 | #define DISK_CACHE_H | ||
23 | |||
24 | /* This needs enough for all file handles to have a buffer in the worst case | ||
25 | * plus at least one reserved exclusively for the cache client and a couple | ||
26 | * for other file system code. The buffers are put to use by the cache if not | ||
27 | * taken for another purpose (meaning nothing is wasted sitting fallow). | ||
28 | * | ||
29 | * One map per volume is maintained in order to avoid collisions between | ||
30 | * volumes that would slow cache probing. DC_MAP_NUM_ENTRIES is the number | ||
31 | * for each map per volume. The buffers themselves are shared. | ||
32 | */ | ||
33 | #if MEMORYSIZE < 8 | ||
34 | #define DC_NUM_ENTRIES 32 | ||
35 | #define DC_MAP_NUM_ENTRIES 128 | ||
36 | #elif MEMORYSIZE <= 32 | ||
37 | #define DC_NUM_ENTRIES 48 | ||
38 | #define DC_MAP_NUM_ENTRIES 128 | ||
39 | #else /* MEMORYSIZE > 32 */ | ||
40 | #define DC_NUM_ENTRIES 64 | ||
41 | #define DC_MAP_NUM_ENTRIES 256 | ||
42 | #endif /* MEMORYSIZE */ | ||
43 | |||
44 | /* this _could_ be larger than a sector if that would ever be useful */ | ||
45 | #define DC_CACHE_BUFSIZE SECTOR_SIZE | ||
46 | |||
47 | #include "mutex.h" | ||
48 | #include "mv.h" | ||
49 | |||
50 | static inline void dc_lock_cache(void) | ||
51 | { | ||
52 | extern struct mutex disk_cache_mutex; | ||
53 | mutex_lock(&disk_cache_mutex); | ||
54 | } | ||
55 | |||
56 | static inline void dc_unlock_cache(void) | ||
57 | { | ||
58 | extern struct mutex disk_cache_mutex; | ||
59 | mutex_unlock(&disk_cache_mutex); | ||
60 | } | ||
61 | |||
62 | void * dc_cache_probe(IF_MV(int volume,) unsigned long secnum, | ||
63 | unsigned int *flags); | ||
64 | void dc_dirty_buf(void *buf); | ||
65 | void dc_discard_buf(void *buf); | ||
66 | void dc_commit_all(IF_MV_NONVOID(int volume)); | ||
67 | void dc_discard_all(IF_MV_NONVOID(int volume)); | ||
68 | |||
69 | void dc_init(void) INIT_ATTR; | ||
70 | |||
71 | /* in addition to filling, writeback is implemented by the client */ | ||
72 | extern void dc_writeback_callback(IF_MV(int volume, ) unsigned long sector, | ||
73 | void *buf); | ||
74 | |||
75 | |||
76 | /** These synchronize and can be called by anyone **/ | ||
77 | |||
78 | /* expropriate a buffer from the cache of DC_CACHE_BUFSIZE bytes */ | ||
79 | void * dc_get_buffer(void); | ||
80 | /* return buffer to the cache by buffer */ | ||
81 | void dc_release_buffer(void *buf); | ||
82 | |||
83 | #endif /* DISK_CACHE_H */ | ||
diff --git a/firmware/include/file.h b/firmware/include/file.h index 77930864c7..8e5bacec0e 100644 --- a/firmware/include/file.h +++ b/firmware/include/file.h | |||
@@ -18,81 +18,88 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | |||
22 | #ifndef _FILE_H_ | 21 | #ifndef _FILE_H_ |
23 | #define _FILE_H_ | 22 | #define _FILE_H_ |
24 | 23 | ||
25 | #include <sys/types.h> | 24 | #include <sys/types.h> |
26 | #include "config.h" | 25 | #include <stdbool.h> |
27 | #include "gcc_extensions.h" | ||
28 | #include <fcntl.h> | 26 | #include <fcntl.h> |
29 | #ifdef WIN32 | 27 | #ifdef WIN32 |
30 | /* this has SEEK_SET et al */ | 28 | /* this has SEEK_SET et al */ |
31 | #include <stdio.h> | 29 | #include <stdio.h> |
32 | #endif | 30 | #endif |
33 | 31 | #include "config.h" | |
32 | #include "gcc_extensions.h" | ||
34 | 33 | ||
35 | #undef MAX_PATH /* this avoids problems when building simulator */ | 34 | #undef MAX_PATH /* this avoids problems when building simulator */ |
36 | #define MAX_PATH 260 | 35 | #define MAX_PATH 260 |
37 | #define MAX_OPEN_FILES 11 | ||
38 | 36 | ||
39 | #if !defined(PLUGIN) && !defined(CODEC) | 37 | enum relate_result |
40 | #if defined(APPLICATION) && !defined(__PCTOOL__) | 38 | { |
41 | #include "rbpaths.h" | 39 | /* < 0 == failure */ |
42 | # define open(x, ...) app_open(x, __VA_ARGS__) | 40 | RELATE_DIFFERENT = 0, /* the two paths are different objects */ |
43 | # define creat(x,m) app_creat(x, m) | 41 | RELATE_SAME, /* the two paths are the same object */ |
44 | # define remove(x) app_remove(x) | 42 | RELATE_PREFIX, /* the path2 contains path1 as a prefix */ |
45 | # define rename(x,y) app_rename(x,y) | 43 | }; |
46 | # define readlink(x,y,z) app_readlink(x,y,z) | ||
47 | # if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) | ||
48 | /* SDL overrides a few more */ | ||
49 | # define read(x,y,z) sim_read(x,y,z) | ||
50 | # define write(x,y,z) sim_write(x,y,z) | ||
51 | # endif | ||
52 | #elif defined(SIMULATOR) || defined(DBTOOL) | ||
53 | # define open(x, ...) sim_open(x, __VA_ARGS__) | ||
54 | # define creat(x,m) sim_creat(x,m) | ||
55 | # define remove(x) sim_remove(x) | ||
56 | # define rename(x,y) sim_rename(x,y) | ||
57 | # define fsync(x) sim_fsync(x) | ||
58 | # define ftruncate(x,y) sim_ftruncate(x,y) | ||
59 | # define lseek(x,y,z) sim_lseek(x,y,z) | ||
60 | # define read(x,y,z) sim_read(x,y,z) | ||
61 | # define write(x,y,z) sim_write(x,y,z) | ||
62 | # define close(x) sim_close(x) | ||
63 | /* readlink() not used in the sim yet */ | ||
64 | extern int sim_open(const char *name, int o, ...); | ||
65 | extern int sim_creat(const char *name, mode_t mode); | ||
66 | #endif | ||
67 | 44 | ||
68 | typedef int (*open_func)(const char* pathname, int flags, ...); | 45 | #if defined(APPLICATION) |
69 | typedef ssize_t (*read_func)(int fd, void *buf, size_t count); | 46 | #include "filesystem-app.h" |
70 | typedef int (*creat_func)(const char *pathname, mode_t mode); | 47 | #elif defined(SIMULATOR) || defined(__PCTOOL__) |
71 | typedef ssize_t (*write_func)(int fd, const void *buf, size_t count); | 48 | #include "../../uisimulator/common/filesystem-sim.h" |
72 | typedef void (*qsort_func)(void *base, size_t nmemb, size_t size, | 49 | #else |
73 | int(*_compar)(const void *, const void *)); | 50 | #include "filesystem-native.h" |
51 | #endif | ||
74 | 52 | ||
75 | extern int file_open(const char* pathname, int flags); | 53 | #ifndef FILEFUNCTIONS_DECLARED |
76 | extern int close(int fd); | 54 | int fdprintf(int fildes, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); |
77 | extern int fsync(int fd); | 55 | #endif /* FILEFUNCTIONS_DECLARED */ |
78 | extern ssize_t read(int fd, void *buf, size_t count); | ||
79 | extern off_t lseek(int fildes, off_t offset, int whence); | ||
80 | extern int file_creat(const char *pathname); | ||
81 | #if ((CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(__PCTOOL__)) || \ | ||
82 | defined(TEST_FAT) | ||
83 | #define creat(x, y) file_creat(x) | ||
84 | 56 | ||
85 | #if !defined(CODEC) && !defined(PLUGIN) | 57 | #ifndef FILEFUNCTIONS_DEFINED |
86 | #define open(x, y, ...) file_open(x,y) | 58 | #ifndef open |
59 | #define open FS_PREFIX(open) | ||
87 | #endif | 60 | #endif |
61 | #ifndef creat | ||
62 | #define creat FS_PREFIX(creat) | ||
88 | #endif | 63 | #endif |
89 | 64 | #ifndef close | |
90 | extern ssize_t write(int fd, const void *buf, size_t count); | 65 | #define close FS_PREFIX(close) |
91 | extern int remove(const char* pathname); | 66 | #endif |
92 | extern int rename(const char* path, const char* newname); | 67 | #ifndef ftruncate |
93 | extern int ftruncate(int fd, off_t length); | 68 | #define ftruncate FS_PREFIX(ftruncate) |
94 | extern off_t filesize(int fd); | 69 | #endif |
95 | extern int release_files(int volume); | 70 | #ifndef fsync |
96 | int fdprintf (int fd, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3); | 71 | #define fsync FS_PREFIX(fsync) |
97 | #endif /* !CODEC && !PLUGIN */ | ||
98 | #endif | 72 | #endif |
73 | #ifndef lseek | ||
74 | #define lseek FS_PREFIX(lseek) | ||
75 | #endif | ||
76 | #ifndef read | ||
77 | #define read FS_PREFIX(read) | ||
78 | #endif | ||
79 | #ifndef write | ||
80 | #define write FS_PREFIX(write) | ||
81 | #endif | ||
82 | #ifndef remove | ||
83 | #define remove FS_PREFIX(remove) | ||
84 | #endif | ||
85 | #ifndef rename | ||
86 | #define rename FS_PREFIX(rename) | ||
87 | #endif | ||
88 | #ifndef filesize | ||
89 | #define filesize FS_PREFIX(filesize) | ||
90 | #endif | ||
91 | #ifndef fsamefile | ||
92 | #define fsamefile FS_PREFIX(fsamefile) | ||
93 | #endif | ||
94 | #ifndef file_exists | ||
95 | #define file_exists FS_PREFIX(file_exists) | ||
96 | #endif | ||
97 | #ifndef relate | ||
98 | #define relate FS_PREFIX(relate) | ||
99 | #endif | ||
100 | #ifndef readlink | ||
101 | #define readlink FS_PREFIX(readlink) | ||
102 | #endif | ||
103 | #endif /* FILEFUNCTIONS_DEFINED */ | ||
104 | |||
105 | #endif /* _FILE_H_ */ | ||
diff --git a/firmware/include/file_internal.h b/firmware/include/file_internal.h new file mode 100644 index 0000000000..d1bb67406a --- /dev/null +++ b/firmware/include/file_internal.h | |||
@@ -0,0 +1,371 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILE_INTERNAL_H_ | ||
22 | #define _FILE_INTERNAL_H_ | ||
23 | |||
24 | #include <sys/types.h> | ||
25 | #include <stdlib.h> | ||
26 | #include "mv.h" | ||
27 | #include "linked_list.h" | ||
28 | #include "mutex.h" | ||
29 | #include "mrsw_lock.h" | ||
30 | #include "fs_attr.h" | ||
31 | #include "fat.h" | ||
32 | #ifdef HAVE_DIRCACHE | ||
33 | #include "dircache.h" | ||
34 | #endif | ||
35 | |||
36 | /** Tuneable parameters **/ | ||
37 | |||
38 | /* limits for number of open descriptors - if you increase these values, make | ||
39 | certain that the disk cache has enough available buffers */ | ||
40 | #define MAX_OPEN_FILES 11 | ||
41 | #define MAX_OPEN_DIRS 12 | ||
42 | #define MAX_OPEN_HANDLES (MAX_OPEN_FILES+MAX_OPEN_DIRS) | ||
43 | |||
44 | /* internal functions open streams as well; make sure they don't fail if all | ||
45 | user descs are busy; this needs to be at least the greatest quantity needed | ||
46 | at once by all internal functions */ | ||
47 | #ifdef HAVE_DIRCACHE | ||
48 | #define AUX_FILEOBJS 3 | ||
49 | #else | ||
50 | #define AUX_FILEOBJS 2 | ||
51 | #endif | ||
52 | |||
53 | /* number of components statically allocated to handle the vast majority | ||
54 | of path depths; should maybe be tuned for >= 90th percentile but for now, | ||
55 | imma just guessing based on something like: | ||
56 | root + 'Music' + 'Artist' + 'Album' + 'Disc N' + filename */ | ||
57 | #define STATIC_PATHCOMP_NUM 6 | ||
58 | |||
59 | #define MAX_NAME 255 | ||
60 | |||
61 | /* unsigned value that will also hold the off_t range we need without | ||
62 | overflow */ | ||
63 | #define file_size_t uint32_t | ||
64 | |||
65 | #ifdef __USE_FILE_OFFSET64 | ||
66 | /* if we want, we can deal with files up to 2^32-1 bytes-- the full FAT16/32 | ||
67 | range */ | ||
68 | #define FILE_SIZE_MAX (0xffffffffu) | ||
69 | #else | ||
70 | /* file contents and size will be preserved by the APIs so long as ftruncate() | ||
71 | isn't used; bytes passed 2^31-1 will not accessible nor will writes succeed | ||
72 | that would extend the file beyond the max for a 32-bit off_t */ | ||
73 | #define FILE_SIZE_MAX (0x7fffffffu) | ||
74 | #endif | ||
75 | |||
76 | /* if file is "large(ish)", then get rid of the contents now rather than | ||
77 | lazily when the file is synced or closed in order to free-up space */ | ||
78 | #define O_TRUNC_THRESH 65536 | ||
79 | |||
80 | /* default attributes when creating new files and directories */ | ||
81 | #define ATTR_NEW_FILE (ATTR_ARCHIVE) | ||
82 | #define ATTR_NEW_DIRECTORY (ATTR_DIRECTORY) | ||
83 | |||
84 | #define ATTR_MOUNT_POINT (ATTR_VOLUME | ATTR_DIRECTORY) | ||
85 | |||
86 | /** File sector cache **/ | ||
87 | |||
88 | enum filestr_cache_flags | ||
89 | { | ||
90 | FSC_DIRTY = 0x1, /* buffer is dirty (needs writeback) */ | ||
91 | FSC_NEW = 0x2, /* buffer is new (never yet written) */ | ||
92 | }; | ||
93 | |||
94 | struct filestr_cache | ||
95 | { | ||
96 | uint8_t *buffer; /* buffer to hold sector */ | ||
97 | unsigned long sector; /* file sector that is in buffer */ | ||
98 | unsigned int flags; /* FSC_* bits */ | ||
99 | }; | ||
100 | |||
101 | void file_cache_init(struct filestr_cache *cachep); | ||
102 | void file_cache_reset(struct filestr_cache *cachep); | ||
103 | void file_cache_alloc(struct filestr_cache *cachep); | ||
104 | void file_cache_free(struct filestr_cache *cachep); | ||
105 | |||
106 | |||
107 | /** Common bitflags used throughout **/ | ||
108 | |||
109 | /* bitflags used by open files and descriptors */ | ||
110 | enum fildes_and_obj_flags | ||
111 | { | ||
112 | /* used in descriptor and common */ | ||
113 | FDO_BUSY = 0x0001, /* descriptor/object is in use */ | ||
114 | /* only used in individual stream descriptor */ | ||
115 | FD_WRITE = 0x0002, /* descriptor has write mode */ | ||
116 | FD_WRONLY = 0x0004, /* descriptor is write mode only */ | ||
117 | FD_APPEND = 0x0008, /* descriptor is append mode */ | ||
118 | /* only used as common flags */ | ||
119 | FO_DIRECTORY = 0x0010, /* fileobj is a directory */ | ||
120 | FO_TRUNC = 0x0020, /* fileobj is opened to be truncated */ | ||
121 | FO_REMOVED = 0x0040, /* fileobj was deleted while open */ | ||
122 | FO_SINGLE = 0x0080, /* fileobj has only one stream open */ | ||
123 | FDO_MASK = 0x00ff, | ||
124 | /* bitflags that instruct various 'open' functions how to behave */ | ||
125 | FF_FILE = 0x0000, /* expect file; accept file only */ | ||
126 | FF_DIR = 0x0100, /* expect dir; accept dir only */ | ||
127 | FF_ANYTYPE = 0x0200, /* succeed if either file or dir */ | ||
128 | FF_TYPEMASK = 0x0300, /* mask of typeflags */ | ||
129 | FF_CREAT = 0x0400, /* create if file doesn't exist */ | ||
130 | FF_EXCL = 0x0800, /* fail if creating and file exists */ | ||
131 | FF_CHECKPREFIX = 0x1000, /* detect if file is prefix of path */ | ||
132 | FF_NOISO = 0x2000, /* do not decode ISO filenames to UTF-8 */ | ||
133 | FF_MASK = 0x3f00, | ||
134 | /* special values used in isolation */ | ||
135 | FV_NONEXIST = 0x8000, /* closed but not freed (unmounted) */ | ||
136 | FV_OPENSYSROOT = 0xc001, /* open sysroot, volume 0 not mounted */ | ||
137 | }; | ||
138 | |||
139 | |||
140 | /** Common data structures used throughout **/ | ||
141 | |||
142 | /* basic file information about its location */ | ||
143 | struct file_base_info | ||
144 | { | ||
145 | union { | ||
146 | #ifdef HAVE_MULTIVOLUME | ||
147 | int volume; /* file's volume (overlaps fatfile.volume) */ | ||
148 | #endif | ||
149 | #if CONFIG_PLATFORM & PLATFORM_NATIVE | ||
150 | struct fat_file fatfile; /* FS driver file info */ | ||
151 | #endif | ||
152 | }; | ||
153 | #ifdef HAVE_DIRCACHE | ||
154 | struct dircache_file dcfile; /* dircache file info */ | ||
155 | #endif | ||
156 | }; | ||
157 | |||
158 | #define BASEINFO_VOL(infop) \ | ||
159 | IF_MV_VOL((infop)->volume) | ||
160 | |||
161 | /* open files binding item */ | ||
162 | struct file_base_binding | ||
163 | { | ||
164 | struct ll_node node; /* list item node (first!) */ | ||
165 | struct file_base_info info; /* basic file info */ | ||
166 | }; | ||
167 | |||
168 | #define BASEBINDING_VOL(bindp) \ | ||
169 | BASEINFO_VOL(&(bindp)->info) | ||
170 | |||
171 | /* directory scanning position info */ | ||
172 | struct dirscan_info | ||
173 | { | ||
174 | #if CONFIG_PLATFORM & PLATFORM_NATIVE | ||
175 | struct fat_dirscan_info fatscan; /* FS driver scan info */ | ||
176 | #endif | ||
177 | #ifdef HAVE_DIRCACHE | ||
178 | struct dircache_file dcscan; /* dircache scan info */ | ||
179 | #endif | ||
180 | }; | ||
181 | |||
182 | /* describes the file as an open stream */ | ||
183 | struct filestr_base | ||
184 | { | ||
185 | struct ll_node node; /* list item node (first!) */ | ||
186 | uint16_t flags; /* FD_* bits of this stream */ | ||
187 | uint16_t unused; /* not used */ | ||
188 | struct filestr_cache cache; /* stream-local cache */ | ||
189 | struct filestr_cache *cachep; /* the cache in use (local or shared) */ | ||
190 | struct file_base_info *infop; /* base file information */ | ||
191 | struct fat_filestr fatstr; /* FS driver information */ | ||
192 | struct file_base_binding *bindp; /* common binding for file/dir */ | ||
193 | struct mutex *mtx; /* serialization for this stream */ | ||
194 | }; | ||
195 | |||
196 | void filestr_base_init(struct filestr_base *stream); | ||
197 | void filestr_base_destroy(struct filestr_base *stream); | ||
198 | void filestr_alloc_cache(struct filestr_base *stream); | ||
199 | void filestr_free_cache(struct filestr_base *stream); | ||
200 | void filestr_assign_cache(struct filestr_base *stream, | ||
201 | struct filestr_cache *cachep); | ||
202 | void filestr_copy_cache(struct filestr_base *stream, | ||
203 | struct filestr_cache *cachep); | ||
204 | void filestr_discard_cache(struct filestr_base *stream); | ||
205 | |||
206 | /* allocates a cache buffer if needed and returns the cache pointer */ | ||
207 | static inline struct filestr_cache * | ||
208 | filestr_get_cache(struct filestr_base *stream) | ||
209 | { | ||
210 | struct filestr_cache *cachep = stream->cachep; | ||
211 | |||
212 | if (!cachep->buffer) | ||
213 | filestr_alloc_cache(stream); | ||
214 | |||
215 | return cachep; | ||
216 | } | ||
217 | |||
218 | static inline void filestr_lock(struct filestr_base *stream) | ||
219 | { | ||
220 | mutex_lock(stream->mtx); | ||
221 | } | ||
222 | |||
223 | static inline void filestr_unlock(struct filestr_base *stream) | ||
224 | { | ||
225 | mutex_unlock(stream->mtx); | ||
226 | } | ||
227 | |||
228 | /* stream lock doesn't have to be used if getting RW lock writer access */ | ||
229 | #define FILESTR_WRITER 0 | ||
230 | #define FILESTR_READER 1 | ||
231 | |||
232 | #define FILESTR_LOCK(type, stream) \ | ||
233 | ({ if (FILESTR_##type) filestr_lock(stream); }) | ||
234 | |||
235 | #define FILESTR_UNLOCK(type, stream) \ | ||
236 | ({ if (FILESTR_##type) filestr_unlock(stream); }) | ||
237 | |||
238 | #define ATTR_PREFIX (0x8000) /* out of the way of all ATTR_* bits */ | ||
239 | |||
240 | /* structure to return detailed information about what you opened */ | ||
241 | struct path_component_info | ||
242 | { | ||
243 | const char *name; /* pointer to name within 'path' */ | ||
244 | size_t length; /* length of component within 'path' */ | ||
245 | file_size_t filesize; /* size of the opened file (0 if dir) */ | ||
246 | unsigned int attr; /* attributes of this component */ | ||
247 | struct file_base_info *prefixp; /* base info to check as prefix (IN) */ | ||
248 | struct file_base_info parentinfo; /* parent directory info of file */ | ||
249 | }; | ||
250 | |||
251 | int open_stream_internal(const char *path, unsigned int callflags, | ||
252 | struct filestr_base *stream, | ||
253 | struct path_component_info *compinfo); | ||
254 | int close_stream_internal(struct filestr_base *stream); | ||
255 | int create_stream_internal(struct file_base_info *parentinfop, | ||
256 | const char *basename, size_t length, | ||
257 | unsigned int attr, unsigned int callflags, | ||
258 | struct filestr_base *stream); | ||
259 | int remove_stream_internal(const char *path, struct filestr_base *stream, | ||
260 | unsigned int callflags); | ||
261 | int test_stream_exists_internal(const char *path, unsigned int callflags); | ||
262 | |||
263 | int open_noiso_internal(const char *path, int oflag); /* file.c */ | ||
264 | |||
265 | struct dirent; | ||
266 | int uncached_readdir_dirent(struct filestr_base *stream, | ||
267 | struct dirscan_info *scanp, | ||
268 | struct dirent *entry); | ||
269 | void uncached_rewinddir_dirent(struct dirscan_info *scanp); | ||
270 | |||
271 | int uncached_readdir_internal(struct filestr_base *stream, | ||
272 | struct file_base_info *infop, | ||
273 | struct fat_direntry *fatent); | ||
274 | void uncached_rewinddir_internal(struct file_base_info *infop); | ||
275 | |||
276 | int test_dir_empty_internal(struct filestr_base *stream); | ||
277 | |||
278 | struct dirinfo_internal | ||
279 | { | ||
280 | unsigned int attr; | ||
281 | file_size_t size; | ||
282 | uint16_t wrtdate; | ||
283 | uint16_t wrttime; | ||
284 | }; | ||
285 | |||
286 | /** Synchronization used throughout **/ | ||
287 | |||
288 | /* acquire the filesystem lock as READER */ | ||
289 | static inline void file_internal_lock_READER(void) | ||
290 | { | ||
291 | extern struct mrsw_lock file_internal_mrsw; | ||
292 | mrsw_read_acquire(&file_internal_mrsw); | ||
293 | } | ||
294 | |||
295 | /* release the filesystem lock as READER */ | ||
296 | static inline void file_internal_unlock_READER(void) | ||
297 | { | ||
298 | extern struct mrsw_lock file_internal_mrsw; | ||
299 | mrsw_read_release(&file_internal_mrsw); | ||
300 | } | ||
301 | |||
302 | /* acquire the filesystem lock as WRITER */ | ||
303 | static inline void file_internal_lock_WRITER(void) | ||
304 | { | ||
305 | extern struct mrsw_lock file_internal_mrsw; | ||
306 | mrsw_write_acquire(&file_internal_mrsw); | ||
307 | } | ||
308 | |||
309 | /* release the filesystem lock as WRITER */ | ||
310 | static inline void file_internal_unlock_WRITER(void) | ||
311 | { | ||
312 | extern struct mrsw_lock file_internal_mrsw; | ||
313 | mrsw_write_release(&file_internal_mrsw); | ||
314 | } | ||
315 | |||
316 | #define ERRNO 0 /* maintain errno value */ | ||
317 | #define RC 0 /* maintain rc value */ | ||
318 | |||
319 | /* NOTES: if _errno is a non-constant expression, it must set an error | ||
320 | * number and not return the ERRNO constant which will merely set | ||
321 | * errno to zero, not preserve the current value; if you must set | ||
322 | * errno to zero, set it explicitly, not in the macro | ||
323 | * | ||
324 | * if _rc is constant-expression evaluation to 'RC', then rc will | ||
325 | * NOT be altered; i.e. if you must set rc to zero, set it explicitly, | ||
326 | * not in the macro | ||
327 | */ | ||
328 | |||
329 | /* set errno and rc and proceed to the "file_error:" label */ | ||
330 | #define FILE_ERROR(_errno, _rc) \ | ||
331 | ({ __builtin_constant_p(_errno) ? \ | ||
332 | ({ if ((_errno) != ERRNO) errno = (_errno); }) : \ | ||
333 | ({ errno = (_errno); }); \ | ||
334 | __builtin_constant_p(_rc) ? \ | ||
335 | ({ if ((_rc) != RC) rc = (_rc); }) : \ | ||
336 | ({ rc = (_rc); }); \ | ||
337 | goto file_error; }) | ||
338 | |||
339 | /* set errno and return a value at the point of invocation */ | ||
340 | #define FILE_ERROR_RETURN(_errno, _rc...) \ | ||
341 | ({ __builtin_constant_p(_errno) ? \ | ||
342 | ({ if ((_errno) != ERRNO) errno = (_errno); }) : \ | ||
343 | ({ errno = (_errno); }); \ | ||
344 | return _rc; }) | ||
345 | |||
346 | |||
347 | /** Misc. stuff **/ | ||
348 | |||
349 | /* iterate through all the volumes if volume < 0, else just the given volume */ | ||
350 | #define FOR_EACH_VOLUME(volume, i) \ | ||
351 | for (int i = (IF_MV_VOL(volume) >= 0 ? IF_MV_VOL(volume) : 0), \ | ||
352 | _end = (IF_MV_VOL(volume) >= 0 ? i : NUM_VOLUMES-1); \ | ||
353 | i <= _end; i++) | ||
354 | |||
355 | /* return a pointer to the static struct fat_direntry */ | ||
356 | static inline struct fat_direntry *get_dir_fatent(void) | ||
357 | { | ||
358 | extern struct fat_direntry dir_fatent; | ||
359 | return &dir_fatent; | ||
360 | } | ||
361 | |||
362 | void iso_decode_d_name(char *d_name); | ||
363 | |||
364 | #ifdef HAVE_DIRCACHE | ||
365 | void empty_dirent(struct dirent *entry); | ||
366 | void fill_dirinfo_native(struct dirinfo_native *din); | ||
367 | #endif /* HAVE_DIRCACHE */ | ||
368 | |||
369 | void filesystem_init(void) INIT_ATTR; | ||
370 | |||
371 | #endif /* _FILE_INTERNAL_H_ */ | ||
diff --git a/firmware/include/fileobj_mgr.h b/firmware/include/fileobj_mgr.h new file mode 100644 index 0000000000..c90a59bea0 --- /dev/null +++ b/firmware/include/fileobj_mgr.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILEOBJ_MGR_H_ | ||
22 | #define _FILEOBJ_MGR_H_ | ||
23 | |||
24 | #include "file_internal.h" | ||
25 | |||
26 | void file_binding_insert_first(struct file_base_binding *bindp); | ||
27 | void file_binding_insert_last(struct file_base_binding *bindp); | ||
28 | void file_binding_remove(struct file_base_binding *bindp); | ||
29 | void file_binding_remove_next(struct file_base_binding *prevp, | ||
30 | struct file_base_binding *bindp); | ||
31 | |||
32 | void fileobj_fileop_open(struct filestr_base *stream, | ||
33 | const struct file_base_info *srcinfop, | ||
34 | unsigned int callflags); | ||
35 | void fileobj_fileop_close(struct filestr_base *stream); | ||
36 | void fileobj_fileop_create(struct filestr_base *stream, | ||
37 | const struct file_base_info *srcinfop, | ||
38 | unsigned int callflags); | ||
39 | void fileobj_fileop_rename(struct filestr_base *stream, | ||
40 | const struct file_base_info *oldinfop); | ||
41 | void fileobj_fileop_remove(struct filestr_base *stream, | ||
42 | const struct file_base_info *oldinfop); | ||
43 | void fileobj_fileop_sync(struct filestr_base *stream); | ||
44 | void fileobj_fileop_truncate(struct filestr_base *stream); | ||
45 | extern void ftruncate_internal_callback(struct filestr_base *stream, | ||
46 | struct filestr_base *s); | ||
47 | |||
48 | file_size_t * fileobj_get_sizep(const struct filestr_base *stream); | ||
49 | unsigned int fileobj_get_flags(const struct filestr_base *stream); | ||
50 | void fileobj_change_flags(struct filestr_base *stream, | ||
51 | unsigned int flags, unsigned int mask); | ||
52 | void fileobj_mgr_unmount(IF_MV_NONVOID(int volume)); | ||
53 | |||
54 | void fileobj_mgr_init(void) INIT_ATTR; | ||
55 | |||
56 | #endif /* _FILEOBJ_MGR_H_ */ | ||
diff --git a/firmware/include/filesystem-native.h b/firmware/include/filesystem-native.h new file mode 100644 index 0000000000..640e179890 --- /dev/null +++ b/firmware/include/filesystem-native.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Björn Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILESYSTEM_NATIVE_H_ | ||
22 | #define _FILESYSTEM_NATIVE_H_ | ||
23 | |||
24 | #if defined(PLUGIN) || defined(CODEC) | ||
25 | #define FILEFUNCTIONS_DECLARED | ||
26 | #define FILEFUNCTIONS_DEFINED | ||
27 | #define DIRFUNCTIONS_DECLARED | ||
28 | #define DIRFUNCTIONS_DEFINED | ||
29 | #endif /* PLUGIN || CODEC */ | ||
30 | |||
31 | #define FS_PREFIX(_x_) _x_ | ||
32 | #endif /* _FILESYSTEM_NATIVE_H_ */ | ||
33 | |||
34 | #ifdef _FILE_H_ | ||
35 | #ifndef _FILESYSTEM_NATIVE__FILE_H_ | ||
36 | #define _FILESYSTEM_NATIVE__FILE_H_ | ||
37 | |||
38 | #ifdef RB_FILESYSTEM_OS | ||
39 | #define FILEFUNCTIONS_DEFINED | ||
40 | #endif | ||
41 | |||
42 | #ifndef FILEFUNCTIONS_DECLARED | ||
43 | #define __OPEN_MODE_ARG | ||
44 | #define __CREAT_MODE_ARG | ||
45 | |||
46 | int open(const char *name, int oflag); | ||
47 | int creat(const char *name); | ||
48 | int close(int fildes); | ||
49 | int ftruncate(int fildes, off_t length); | ||
50 | int fsync(int fildes); | ||
51 | off_t lseek(int fildes, off_t offset, int whence); | ||
52 | ssize_t read(int fildes, void *buf, size_t nbyte); | ||
53 | ssize_t write(int fildes, const void *buf, size_t nbyte); | ||
54 | int remove(const char *path); | ||
55 | int rename(const char *old, const char *new); | ||
56 | off_t filesize(int fildes); | ||
57 | int fsamefile(int fildes1, int fildes2); | ||
58 | int relate(const char *path1, const char *path2); | ||
59 | bool file_exists(const char *path); | ||
60 | #endif /* !FILEFUNCTIONS_DECLARED */ | ||
61 | |||
62 | #if !defined(RB_FILESYSTEM_OS) && !defined (FILEFUNCTIONS_DEFINED) | ||
63 | #define open(path, oflag, ...) open(path, oflag) | ||
64 | #define creat(path, mode) creat(path) | ||
65 | #endif /* FILEFUNCTIONS_DEFINED */ | ||
66 | |||
67 | #endif /* _FILESYSTEM_NATIVE__FILE_H_ */ | ||
68 | #endif /* _FILE_H_ */ | ||
69 | |||
70 | #ifdef _DIR_H_ | ||
71 | #ifndef _FILESYSTEM_NATIVE__DIR_H_ | ||
72 | #define _FILESYSTEM_NATIVE__DIR_H_ | ||
73 | |||
74 | #define DIRENT dirent | ||
75 | struct dirent; | ||
76 | |||
77 | struct dirinfo_native | ||
78 | { | ||
79 | unsigned int attr; | ||
80 | off_t size; | ||
81 | uint16_t wrtdate; | ||
82 | uint16_t wrttime; | ||
83 | }; | ||
84 | |||
85 | typedef struct {} DIR; | ||
86 | |||
87 | #ifndef DIRFUNCTIONS_DECLARED | ||
88 | #define __MKDIR_MODE_ARG | ||
89 | |||
90 | #ifdef RB_FILESYSTEM_OS | ||
91 | #define DIRFUNCTIONS_DEFINED | ||
92 | #endif | ||
93 | |||
94 | DIR * opendir(const char *dirname); | ||
95 | struct dirent * readdir(DIR *dirp); | ||
96 | int readdir_r(DIR *dirp, struct dirent *entry, | ||
97 | struct dirent **result); | ||
98 | void rewinddir(DIR *dirp); | ||
99 | int closedir(DIR *dirp); | ||
100 | int mkdir(const char *path); | ||
101 | int rmdir(const char *path); | ||
102 | int samedir(DIR *dirp1, DIR *dirp2); | ||
103 | bool dir_exists(const char *dirname); | ||
104 | #endif /* !DIRFUNCTIONS_DECLARED */ | ||
105 | |||
106 | #endif /* _FILESYSTEM_NATIVE__DIR_H_ */ | ||
107 | #endif /* _DIR_H_ */ | ||
diff --git a/firmware/include/fs_attr.h b/firmware/include/fs_attr.h new file mode 100644 index 0000000000..0e1d25e1de --- /dev/null +++ b/firmware/include/fs_attr.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 by Kévin Ferrare | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FS_ATTR_H_ | ||
22 | #define _FS_ATTR_H_ | ||
23 | |||
24 | #include <stdbool.h> | ||
25 | |||
26 | #undef MAX_PATH /* this avoids problems when building simulator */ | ||
27 | #define MAX_PATH 260 | ||
28 | |||
29 | /* also used by fat.c so values must not change */ | ||
30 | #define ATTR_READ_ONLY 0x01 | ||
31 | #define ATTR_HIDDEN 0x02 | ||
32 | #define ATTR_SYSTEM 0x04 | ||
33 | #define ATTR_VOLUME_ID 0x08 | ||
34 | #define ATTR_DIRECTORY 0x10 | ||
35 | #define ATTR_ARCHIVE 0x20 | ||
36 | #define ATTR_VOLUME 0x40 /* this is a volume, not a real directory */ | ||
37 | #define ATTR_LINK 0x80 | ||
38 | |||
39 | #endif /* _FS_ATTR_H_ */ | ||
diff --git a/firmware/include/rb-loader.h b/firmware/include/rb-loader.h index e1e756dba9..86c5026af9 100644 --- a/firmware/include/rb-loader.h +++ b/firmware/include/rb-loader.h | |||
@@ -18,5 +18,4 @@ | |||
18 | * | 18 | * |
19 | ****************************************************************************/ | 19 | ****************************************************************************/ |
20 | 20 | ||
21 | const char *rb_strerror(int8_t errno); | ||
22 | int load_firmware(unsigned char* buf, const char* firmware, int buffer_size); | 21 | int load_firmware(unsigned char* buf, const char* firmware, int buffer_size); |
diff --git a/firmware/include/rbunicode.h b/firmware/include/rbunicode.h index 3c28b3031e..077029304d 100644 --- a/firmware/include/rbunicode.h +++ b/firmware/include/rbunicode.h | |||
@@ -51,7 +51,8 @@ enum codepages { | |||
51 | KSX_1001, /* Korean */ | 51 | KSX_1001, /* Korean */ |
52 | BIG_5, /* Trad. Chinese */ | 52 | BIG_5, /* Trad. Chinese */ |
53 | UTF_8, /* Unicode */ | 53 | UTF_8, /* Unicode */ |
54 | NUM_CODEPAGES | 54 | NUM_CODEPAGES, |
55 | INIT_CODEPAGE = ISO_8859_1, | ||
55 | }; | 56 | }; |
56 | 57 | ||
57 | #else /* !HAVE_LCD_BITMAP, reduced support */ | 58 | #else /* !HAVE_LCD_BITMAP, reduced support */ |
@@ -65,7 +66,8 @@ enum codepages { | |||
65 | WIN_1250, /* Central European */ | 66 | WIN_1250, /* Central European */ |
66 | WIN_1252, /* Western European */ | 67 | WIN_1252, /* Western European */ |
67 | UTF_8, /* Unicode */ | 68 | UTF_8, /* Unicode */ |
68 | NUM_CODEPAGES | 69 | NUM_CODEPAGES, |
70 | INIT_CODEPAGE = ISO_8859_1, | ||
69 | }; | 71 | }; |
70 | 72 | ||
71 | #endif | 73 | #endif |
@@ -78,9 +80,19 @@ unsigned char* utf16BEdecode(const unsigned char *utf16, unsigned char *utf8, in | |||
78 | unsigned long utf8length(const unsigned char *utf8); | 80 | unsigned long utf8length(const unsigned char *utf8); |
79 | const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs); | 81 | const unsigned char* utf8decode(const unsigned char *utf8, unsigned short *ucs); |
80 | void set_codepage(int cp); | 82 | void set_codepage(int cp); |
83 | int get_codepage(void); | ||
81 | int utf8seek(const unsigned char* utf8, int offset); | 84 | int utf8seek(const unsigned char* utf8, int offset); |
82 | const char* get_codepage_name(int cp); | 85 | const char* get_codepage_name(int cp); |
83 | #if defined(APPLICATION) && defined(__linux__) | 86 | #ifdef APPLICATION |
87 | #if defined(__linux__) | ||
84 | const char *get_current_codepage_name_linux(void); | 88 | const char *get_current_codepage_name_linux(void); |
85 | #endif | 89 | #endif |
90 | #endif /* APPLICATION */ | ||
91 | |||
92 | #if 0 /* not needed just now */ | ||
93 | void unicode_init(void); | ||
94 | #else | ||
95 | #define unicode_init() do {} while (0) | ||
96 | #endif | ||
97 | |||
86 | #endif /* _RBUNICODE_H_ */ | 98 | #endif /* _RBUNICODE_H_ */ |
diff --git a/firmware/kernel/include/mutex.h b/firmware/kernel/include/mutex.h index b74bfe23f5..4778eb7f11 100644 --- a/firmware/kernel/include/mutex.h +++ b/firmware/kernel/include/mutex.h | |||
@@ -31,21 +31,12 @@ struct mutex | |||
31 | struct blocker blocker; /* priority inheritance info | 31 | struct blocker blocker; /* priority inheritance info |
32 | for waiters and owner*/ | 32 | for waiters and owner*/ |
33 | IF_COP( struct corelock cl; ) /* multiprocessor sync */ | 33 | IF_COP( struct corelock cl; ) /* multiprocessor sync */ |
34 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
35 | bool no_preempt; | ||
36 | #endif | ||
37 | }; | 34 | }; |
38 | 35 | ||
39 | extern void mutex_init(struct mutex *m); | 36 | extern void mutex_init(struct mutex *m); |
40 | extern void mutex_lock(struct mutex *m); | 37 | extern void mutex_lock(struct mutex *m); |
41 | extern void mutex_unlock(struct mutex *m); | 38 | extern void mutex_unlock(struct mutex *m); |
42 | #ifdef HAVE_PRIORITY_SCHEDULING | 39 | #ifndef HAVE_PRIORITY_SCHEDULING |
43 | /* Deprecated temporary function to disable mutex preempting a thread on | ||
44 | * unlock - firmware/drivers/fat.c and a couple places in apps/buffering.c - | ||
45 | * reliance on it is a bug! */ | ||
46 | static inline void mutex_set_preempt(struct mutex *m, bool preempt) | ||
47 | { m->no_preempt = !preempt; } | ||
48 | #else | ||
49 | /* Deprecated but needed for now - firmware/drivers/ata_mmc.c */ | 40 | /* Deprecated but needed for now - firmware/drivers/ata_mmc.c */ |
50 | static inline bool mutex_test(const struct mutex *m) | 41 | static inline bool mutex_test(const struct mutex *m) |
51 | { return m->blocker.thread != NULL; } | 42 | { return m->blocker.thread != NULL; } |
diff --git a/firmware/kernel/mutex.c b/firmware/kernel/mutex.c index 876b704b51..cb9e6816b8 100644 --- a/firmware/kernel/mutex.c +++ b/firmware/kernel/mutex.c | |||
@@ -33,9 +33,6 @@ void mutex_init(struct mutex *m) | |||
33 | wait_queue_init(&m->queue); | 33 | wait_queue_init(&m->queue); |
34 | m->recursion = 0; | 34 | m->recursion = 0; |
35 | blocker_init(&m->blocker); | 35 | blocker_init(&m->blocker); |
36 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
37 | m->no_preempt = false; | ||
38 | #endif | ||
39 | corelock_init(&m->cl); | 36 | corelock_init(&m->cl); |
40 | } | 37 | } |
41 | 38 | ||
@@ -115,7 +112,7 @@ void mutex_unlock(struct mutex *m) | |||
115 | corelock_unlock(&m->cl); | 112 | corelock_unlock(&m->cl); |
116 | 113 | ||
117 | #ifdef HAVE_PRIORITY_SCHEDULING | 114 | #ifdef HAVE_PRIORITY_SCHEDULING |
118 | if((result & THREAD_SWITCH) && !m->no_preempt) | 115 | if(result & THREAD_SWITCH) |
119 | switch_thread(); | 116 | switch_thread(); |
120 | #endif | 117 | #endif |
121 | (void)result; | 118 | (void)result; |
diff --git a/firmware/libc/include/errno.h b/firmware/libc/include/errno.h index 9df261db9f..2ddf036c92 100644 --- a/firmware/libc/include/errno.h +++ b/firmware/libc/include/errno.h | |||
@@ -88,6 +88,7 @@ extern int * __errno(void); | |||
88 | #define ELBIN 75 /* Inode is remote (not really error) */ | 88 | #define ELBIN 75 /* Inode is remote (not really error) */ |
89 | #define EDOTDOT 76 /* Cross mount point (not really error) */ | 89 | #define EDOTDOT 76 /* Cross mount point (not really error) */ |
90 | #define EBADMSG 77 /* Trying to read unreadable message */ | 90 | #define EBADMSG 77 /* Trying to read unreadable message */ |
91 | #define EOVERFLOW 78 /* Value too large to be stored in data type */ | ||
91 | #define ENOTUNIQ 80 /* Given log. name not unique */ | 92 | #define ENOTUNIQ 80 /* Given log. name not unique */ |
92 | #define EBADFD 81 /* f.d. invalid for this operation */ | 93 | #define EBADFD 81 /* f.d. invalid for this operation */ |
93 | #define EREMCHG 82 /* Remote address changed */ | 94 | #define EREMCHG 82 /* Remote address changed */ |
diff --git a/firmware/libc/include/fcntl.h b/firmware/libc/include/fcntl.h index 34740c9ca2..ec53d728cf 100644 --- a/firmware/libc/include/fcntl.h +++ b/firmware/libc/include/fcntl.h | |||
@@ -23,18 +23,20 @@ | |||
23 | #define __FCNTL_H__ | 23 | #define __FCNTL_H__ |
24 | 24 | ||
25 | #ifndef O_RDONLY | 25 | #ifndef O_RDONLY |
26 | #define O_RDONLY 0 | 26 | #define O_RDONLY 0x0000 /* open for reading only */ |
27 | #define O_WRONLY 1 | 27 | #define O_WRONLY 0x0001 /* open for writing only */ |
28 | #define O_RDWR 2 | 28 | #define O_RDWR 0x0002 /* open for reading and writing */ |
29 | #define O_CREAT 4 | 29 | #define O_ACCMODE 0x0003 /* mask for above modes */ |
30 | #define O_APPEND 8 | 30 | #define O_APPEND 0x0008 /* set append mode */ |
31 | #define O_TRUNC 0x10 | 31 | #define O_CREAT 0x0200 /* create if nonexistent */ |
32 | #define O_TRUNC 0x0400 /* truncate to zero length */ | ||
33 | #define O_EXCL 0x0800 /* error if already exists */ | ||
32 | #endif | 34 | #endif |
33 | 35 | ||
34 | #ifndef SEEK_SET | 36 | #ifndef SEEK_SET |
35 | #define SEEK_SET 0 | 37 | #define SEEK_SET 0 /* set file offset to offset */ |
36 | #define SEEK_CUR 1 | 38 | #define SEEK_CUR 1 /* set file offset to current plus offset */ |
37 | #define SEEK_END 2 | 39 | #define SEEK_END 2 /* set file offset to EOF plus offset */ |
38 | #endif | 40 | #endif |
39 | 41 | ||
40 | #endif /* __FCNTL_H__ */ | 42 | #endif /* __FCNTL_H__ */ |
diff --git a/firmware/libc/mktime.c b/firmware/libc/mktime.c index a52381ede5..eaa017122a 100644 --- a/firmware/libc/mktime.c +++ b/firmware/libc/mktime.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <time.h> | 23 | #include <time.h> |
24 | #include "config.h" | 24 | #include "config.h" |
25 | 25 | ||
26 | #if CONFIG_RTC | ||
27 | /* mktime() code taken from lynx-2.8.5 source, written | 26 | /* mktime() code taken from lynx-2.8.5 source, written |
28 | by Philippe De Muyter <phdm@macqel.be> */ | 27 | by Philippe De Muyter <phdm@macqel.be> */ |
29 | time_t mktime(struct tm *t) | 28 | time_t mktime(struct tm *t) |
@@ -58,4 +57,3 @@ time_t mktime(struct tm *t) | |||
58 | result += t->tm_sec; | 57 | result += t->tm_sec; |
59 | return(result); | 58 | return(result); |
60 | } | 59 | } |
61 | #endif | ||
diff --git a/firmware/storage.c b/firmware/storage.c index 8740e4ebd2..7ef7428854 100644 --- a/firmware/storage.c +++ b/firmware/storage.c | |||
@@ -190,6 +190,15 @@ int storage_num_drives(void) | |||
190 | return num_drives; | 190 | return num_drives; |
191 | } | 191 | } |
192 | 192 | ||
193 | int storage_driver_type(int drive) | ||
194 | { | ||
195 | if (drive >= num_drives) | ||
196 | return -1; | ||
197 | |||
198 | unsigned int bit = (storage_drivers[drive] & DRIVER_MASK)>>DRIVER_OFFSET; | ||
199 | return bit ? find_first_set_bit(bit) : -1; | ||
200 | } | ||
201 | |||
193 | int storage_init(void) | 202 | int storage_init(void) |
194 | { | 203 | { |
195 | int rc=0; | 204 | int rc=0; |
diff --git a/firmware/target/arm/as3525/sd-as3525.c b/firmware/target/arm/as3525/sd-as3525.c index c80c7f7491..ead40eac3c 100644 --- a/firmware/target/arm/as3525/sd-as3525.c +++ b/firmware/target/arm/as3525/sd-as3525.c | |||
@@ -449,21 +449,12 @@ static void sd_thread(void) | |||
449 | { | 449 | { |
450 | #ifdef HAVE_HOTSWAP | 450 | #ifdef HAVE_HOTSWAP |
451 | case SYS_HOTSWAP_INSERTED: | 451 | case SYS_HOTSWAP_INSERTED: |
452 | case SYS_HOTSWAP_EXTRACTED: | 452 | case SYS_HOTSWAP_EXTRACTED:; |
453 | { | 453 | int success = 1; |
454 | int microsd_init = 1; | ||
455 | fat_lock(); /* lock-out FAT activity first - | ||
456 | prevent deadlocking via disk_mount that | ||
457 | would cause a reverse-order attempt with | ||
458 | another thread */ | ||
459 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
460 | into driver that bypass the fat cache */ | ||
461 | 454 | ||
462 | /* We now have exclusive control of fat cache and ata */ | 455 | disk_unmount(SD_SLOT_AS3525); /* release "by force" */ |
463 | 456 | ||
464 | disk_unmount(SD_SLOT_AS3525); /* release "by force", ensure file | 457 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
465 | descriptors aren't leaked and any busy | ||
466 | ones are invalid if mounting */ | ||
467 | 458 | ||
468 | /* Force card init for new card, re-init for re-inserted one or | 459 | /* Force card init for new card, re-init for re-inserted one or |
469 | * clear if the last attempt to init failed with an error. */ | 460 | * clear if the last attempt to init failed with an error. */ |
@@ -471,29 +462,32 @@ static void sd_thread(void) | |||
471 | 462 | ||
472 | if (ev.id == SYS_HOTSWAP_INSERTED) | 463 | if (ev.id == SYS_HOTSWAP_INSERTED) |
473 | { | 464 | { |
465 | success = 0; | ||
474 | sd_enable(true); | 466 | sd_enable(true); |
475 | init_pl180_controller(SD_SLOT_AS3525); | 467 | init_pl180_controller(SD_SLOT_AS3525); |
476 | microsd_init = sd_init_card(SD_SLOT_AS3525); | 468 | int rc = sd_init_card(SD_SLOT_AS3525); |
477 | if (microsd_init < 0) /* initialisation failed */ | 469 | sd_enable(false); |
478 | panicf("microSD init failed : %d", microsd_init); | 470 | if (rc >= 0) |
479 | 471 | success = 2; | |
480 | microsd_init = disk_mount(SD_SLOT_AS3525); /* 0 if fail */ | 472 | else /* initialisation failed */ |
473 | panicf("microSD init failed : %d", rc); | ||
481 | } | 474 | } |
482 | 475 | ||
476 | mutex_unlock(&sd_mtx); | ||
477 | |||
478 | if (success > 1) | ||
479 | success = disk_mount(SD_SLOT_AS3525); /* 0 if fail */ | ||
480 | |||
483 | /* | 481 | /* |
484 | * Mount succeeded, or this was an EXTRACTED event, | 482 | * Mount succeeded, or this was an EXTRACTED event, |
485 | * in both cases notify the system about the changed filesystems | 483 | * in both cases notify the system about the changed filesystems |
486 | */ | 484 | */ |
487 | if (microsd_init) | 485 | if (success) |
488 | queue_broadcast(SYS_FS_CHANGED, 0); | 486 | queue_broadcast(SYS_FS_CHANGED, 0); |
489 | 487 | ||
490 | /* Access is now safe */ | ||
491 | mutex_unlock(&sd_mtx); | ||
492 | fat_unlock(); | ||
493 | sd_enable(false); | ||
494 | } | ||
495 | break; | 488 | break; |
496 | #endif | 489 | #endif /* HAVE_HOTSWAP */ |
490 | |||
497 | case SYS_TIMEOUT: | 491 | case SYS_TIMEOUT: |
498 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 492 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
499 | { | 493 | { |
diff --git a/firmware/target/arm/as3525/sd-as3525v2.c b/firmware/target/arm/as3525/sd-as3525v2.c index ae3dde4495..b4ac40152b 100644 --- a/firmware/target/arm/as3525/sd-as3525v2.c +++ b/firmware/target/arm/as3525/sd-as3525v2.c | |||
@@ -598,21 +598,13 @@ static void sd_thread(void) | |||
598 | { | 598 | { |
599 | #ifdef HAVE_HOTSWAP | 599 | #ifdef HAVE_HOTSWAP |
600 | case SYS_HOTSWAP_INSERTED: | 600 | case SYS_HOTSWAP_INSERTED: |
601 | case SYS_HOTSWAP_EXTRACTED: | 601 | case SYS_HOTSWAP_EXTRACTED:; |
602 | { | 602 | int success = 1; |
603 | int changed = 1; | 603 | |
604 | fat_lock(); /* lock-out FAT activity first - | 604 | disk_unmount(SD_SLOT_AS3525); /* release "by force" */ |
605 | prevent deadlocking via disk_mount that | 605 | |
606 | would cause a reverse-order attempt with | 606 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
607 | another thread */ | 607 | |
608 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
609 | into driver that bypass the fat cache */ | ||
610 | |||
611 | /* We now have exclusive control of fat cache and ata */ | ||
612 | |||
613 | disk_unmount(SD_SLOT_AS3525); /* release "by force", ensure file | ||
614 | descriptors aren't leaked and any busy | ||
615 | ones are invalid if mounting */ | ||
616 | /* Force card init for new card, re-init for re-inserted one or | 608 | /* Force card init for new card, re-init for re-inserted one or |
617 | * clear if the last attempt to init failed with an error. */ | 609 | * clear if the last attempt to init failed with an error. */ |
618 | card_info[SD_SLOT_AS3525].initialized = 0; | 610 | card_info[SD_SLOT_AS3525].initialized = 0; |
@@ -620,24 +612,25 @@ static void sd_thread(void) | |||
620 | if (ev.id == SYS_HOTSWAP_INSERTED) | 612 | if (ev.id == SYS_HOTSWAP_INSERTED) |
621 | { | 613 | { |
622 | sd_enable(true); | 614 | sd_enable(true); |
623 | changed = (sd_init_card(SD_SLOT_AS3525) == 0) && disk_mount(SD_SLOT_AS3525); /* 0 if fail */ | 615 | success = sd_init_card(SD_SLOT_AS3525) == 0 ? 2 : 0; |
616 | sd_enable(false); | ||
624 | } | 617 | } |
625 | 618 | ||
619 | mutex_unlock(&sd_mtx); | ||
620 | |||
621 | if (success > 1) | ||
622 | success = disk_mount(SD_SLOT_AS3525); /* 0 if fail */ | ||
623 | |||
626 | /* | 624 | /* |
627 | * Mount succeeded, or this was an EXTRACTED event, | 625 | * Mount succeeded, or this was an EXTRACTED event, |
628 | * in both cases notify the system about the changed filesystems | 626 | * in both cases notify the system about the changed filesystems |
629 | */ | 627 | */ |
630 | if (changed) | 628 | if (success) |
631 | queue_broadcast(SYS_FS_CHANGED, 0); | 629 | queue_broadcast(SYS_FS_CHANGED, 0); |
632 | 630 | ||
633 | sd_enable(false); | ||
634 | |||
635 | /* Access is now safe */ | ||
636 | mutex_unlock(&sd_mtx); | ||
637 | fat_unlock(); | ||
638 | } | ||
639 | break; | 631 | break; |
640 | #endif | 632 | #endif /* HAVE_HOTSWAP */ |
633 | |||
641 | case SYS_TIMEOUT: | 634 | case SYS_TIMEOUT: |
642 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 635 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
643 | { | 636 | { |
diff --git a/firmware/target/arm/imx233/sdmmc-imx233.c b/firmware/target/arm/imx233/sdmmc-imx233.c index 8f293543ab..87548aef53 100644 --- a/firmware/target/arm/imx233/sdmmc-imx233.c +++ b/firmware/target/arm/imx233/sdmmc-imx233.c | |||
@@ -766,14 +766,7 @@ static void sdmmc_thread(void) | |||
766 | case SYS_HOTSWAP_INSERTED: | 766 | case SYS_HOTSWAP_INSERTED: |
767 | case SYS_HOTSWAP_EXTRACTED: | 767 | case SYS_HOTSWAP_EXTRACTED: |
768 | { | 768 | { |
769 | int microsd_init = 1; | 769 | int microsd_init = ev.id == SYS_HOTSWAP_INSERTED ? 0 : 1; |
770 | /* lock-out FAT activity first - | ||
771 | * prevent deadlocking via disk_mount that | ||
772 | * would cause a reverse-order attempt with | ||
773 | * another thread */ | ||
774 | #ifdef HAVE_HOTSWAP | ||
775 | fat_lock(); | ||
776 | #endif | ||
777 | 770 | ||
778 | /* We now have exclusive control of fat cache and sd. | 771 | /* We now have exclusive control of fat cache and sd. |
779 | * Release "by force", ensure file | 772 | * Release "by force", ensure file |
@@ -785,35 +778,37 @@ static void sdmmc_thread(void) | |||
785 | /* Skip non-removable drivers */ | 778 | /* Skip non-removable drivers */ |
786 | if(!sdmmc_removable(drive)) | 779 | if(!sdmmc_removable(drive)) |
787 | continue; | 780 | continue; |
788 | /* lock-out card activity - direct calls | 781 | |
789 | * into driver that bypass the fat cache */ | ||
790 | mutex_lock(&mutex[drive]); | ||
791 | disk_unmount(sd_first_drive + sd_drive); | 782 | disk_unmount(sd_first_drive + sd_drive); |
783 | |||
784 | mutex_lock(&mutex[drive]); /* lock-out card activity */ | ||
785 | |||
792 | /* Force card init for new card, re-init for re-inserted one or | 786 | /* Force card init for new card, re-init for re-inserted one or |
793 | * clear if the last attempt to init failed with an error. */ | 787 | * clear if the last attempt to init failed with an error. */ |
794 | SDMMC_INFO(sd_map[sd_drive]).initialized = 0; | 788 | SDMMC_INFO(sd_map[sd_drive]).initialized = 0; |
795 | 789 | ||
790 | int rc = -1; | ||
796 | if(ev.id == SYS_HOTSWAP_INSERTED) | 791 | if(ev.id == SYS_HOTSWAP_INSERTED) |
797 | { | 792 | { |
798 | microsd_init = init_drive(drive); | 793 | rc = init_drive(drive); |
799 | if(microsd_init < 0) /* initialisation failed */ | 794 | if(rc < 0) /* initialisation failed */ |
800 | panicf("%s init failed : %d", SDMMC_CONF(sd_map[sd_drive]).name, microsd_init); | 795 | panicf("%s init failed : %d", SDMMC_CONF(sd_map[sd_drive]).name, rc); |
801 | |||
802 | microsd_init = disk_mount(sd_first_drive + sd_drive); /* 0 if fail */ | ||
803 | } | 796 | } |
804 | /* | 797 | |
805 | * Mount succeeded, or this was an EXTRACTED event, | ||
806 | * in both cases notify the system about the changed filesystems | ||
807 | */ | ||
808 | if(microsd_init) | ||
809 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
810 | /* unlock card */ | 798 | /* unlock card */ |
811 | mutex_unlock(&mutex[drive]); | 799 | mutex_unlock(&mutex[drive]); |
800 | |||
801 | if (rc >= 0) | ||
802 | microsd_init += disk_mount(sd_first_drive + sd_drive); /* 0 if fail */ | ||
812 | } | 803 | } |
813 | /* Access is now safe */ | 804 | /* Access is now safe */ |
814 | #ifdef HAVE_HOTSWAP | 805 | /* |
815 | fat_unlock(); | 806 | * One or more mounts succeeded, or this was an EXTRACTED event, |
816 | #endif | 807 | * in both cases notify the system about the changed filesystems |
808 | */ | ||
809 | if(microsd_init) | ||
810 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
811 | |||
817 | break; | 812 | break; |
818 | } | 813 | } |
819 | #endif | 814 | #endif |
diff --git a/firmware/target/arm/pp/ata-sd-pp.c b/firmware/target/arm/pp/ata-sd-pp.c index bcf8a660c2..2a11b40fee 100644 --- a/firmware/target/arm/pp/ata-sd-pp.c +++ b/firmware/target/arm/pp/ata-sd-pp.c | |||
@@ -19,7 +19,6 @@ | |||
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include "config.h" /* for HAVE_MULTIDRIVE */ | 21 | #include "config.h" /* for HAVE_MULTIDRIVE */ |
22 | #include "fat.h" | ||
23 | #include "sdmmc.h" | 22 | #include "sdmmc.h" |
24 | #include "gcc_extensions.h" | 23 | #include "gcc_extensions.h" |
25 | #ifdef HAVE_HOTSWAP | 24 | #ifdef HAVE_HOTSWAP |
@@ -1125,35 +1124,28 @@ static void sd_thread(void) | |||
1125 | { | 1124 | { |
1126 | #ifdef HAVE_HOTSWAP | 1125 | #ifdef HAVE_HOTSWAP |
1127 | case SYS_HOTSWAP_INSERTED: | 1126 | case SYS_HOTSWAP_INSERTED: |
1128 | case SYS_HOTSWAP_EXTRACTED: | 1127 | case SYS_HOTSWAP_EXTRACTED:; |
1129 | fat_lock(); /* lock-out FAT activity first - | 1128 | int success = 1; |
1130 | prevent deadlocking via disk_mount that | ||
1131 | would cause a reverse-order attempt with | ||
1132 | another thread */ | ||
1133 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
1134 | into driver that bypass the fat cache */ | ||
1135 | 1129 | ||
1136 | /* We now have exclusive control of fat cache and ata */ | 1130 | disk_unmount(sd_first_drive+1); /* release "by force" */ |
1137 | 1131 | ||
1138 | disk_unmount(sd_first_drive+1); /* release "by force", ensure file | 1132 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
1139 | descriptors aren't leaked and any busy | ||
1140 | ones are invalid if mounting */ | ||
1141 | 1133 | ||
1142 | /* Force card init for new card, re-init for re-inserted one or | 1134 | /* Force card init for new card, re-init for re-inserted one or |
1143 | * clear if the last attempt to init failed with an error. */ | 1135 | * clear if the last attempt to init failed with an error. */ |
1144 | card_info[1].initialized = 0; | 1136 | card_info[1].initialized = 0; |
1145 | sd_status[1].retry = 0; | 1137 | sd_status[1].retry = 0; |
1146 | 1138 | ||
1147 | if (ev.id == SYS_HOTSWAP_INSERTED) | ||
1148 | disk_mount(sd_first_drive+1); | ||
1149 | |||
1150 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
1151 | |||
1152 | /* Access is now safe */ | 1139 | /* Access is now safe */ |
1153 | mutex_unlock(&sd_mtx); | 1140 | mutex_unlock(&sd_mtx); |
1154 | fat_unlock(); | 1141 | |
1142 | if (ev.id == SYS_HOTSWAP_INSERTED) | ||
1143 | success = disk_mount(sd_first_drive+1); /* 0 if fail */ | ||
1144 | |||
1145 | if (success) | ||
1146 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
1155 | break; | 1147 | break; |
1156 | #endif | 1148 | #endif /* HAVE_HOTSWAP */ |
1157 | case SYS_TIMEOUT: | 1149 | case SYS_TIMEOUT: |
1158 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 1150 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
1159 | { | 1151 | { |
diff --git a/firmware/target/arm/rk27xx/sd-rk27xx.c b/firmware/target/arm/rk27xx/sd-rk27xx.c index deca8a1fa7..9d6821ee38 100644 --- a/firmware/target/arm/rk27xx/sd-rk27xx.c +++ b/firmware/target/arm/rk27xx/sd-rk27xx.c | |||
@@ -22,7 +22,6 @@ | |||
22 | ****************************************************************************/ | 22 | ****************************************************************************/ |
23 | 23 | ||
24 | #include "config.h" /* for HAVE_MULTIVOLUME */ | 24 | #include "config.h" /* for HAVE_MULTIVOLUME */ |
25 | #include "fat.h" | ||
26 | #include "thread.h" | 25 | #include "thread.h" |
27 | #include "gcc_extensions.h" | 26 | #include "gcc_extensions.h" |
28 | #include "led.h" | 27 | #include "led.h" |
@@ -331,50 +330,45 @@ static void sd_thread(void) | |||
331 | { | 330 | { |
332 | #ifdef HAVE_HOTSWAP | 331 | #ifdef HAVE_HOTSWAP |
333 | case SYS_HOTSWAP_INSERTED: | 332 | case SYS_HOTSWAP_INSERTED: |
334 | case SYS_HOTSWAP_EXTRACTED: | 333 | case SYS_HOTSWAP_EXTRACTED:; |
335 | { | 334 | int success = 1; |
336 | int microsd_init = 1; | 335 | |
337 | fat_lock(); /* lock-out FAT activity first - | 336 | disk_unmount(sd_first_drive); /* release "by force" */ |
338 | prevent deadlocking via disk_mount that | 337 | |
339 | would cause a reverse-order attempt with | 338 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
340 | another thread */ | 339 | |
341 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
342 | into driver that bypass the fat cache */ | ||
343 | |||
344 | /* We now have exclusive control of fat cache and ata */ | ||
345 | |||
346 | disk_unmount(sd_first_drive); /* release "by force", ensure file | ||
347 | descriptors aren't leaked and any busy | ||
348 | ones are invalid if mounting */ | ||
349 | /* Force card init for new card, re-init for re-inserted one or | 340 | /* Force card init for new card, re-init for re-inserted one or |
350 | * clear if the last attempt to init failed with an error. */ | 341 | * clear if the last attempt to init failed with an error. */ |
351 | card_info.initialized = 0; | 342 | card_info.initialized = 0; |
352 | 343 | ||
353 | if (ev.id == SYS_HOTSWAP_INSERTED) | 344 | if (ev.id == SYS_HOTSWAP_INSERTED) |
354 | { | 345 | { |
346 | success = 0; | ||
355 | sd_enable(true); | 347 | sd_enable(true); |
356 | microsd_init = sd_init_card(sd_first_drive); | 348 | int rc = sd_init_card(sd_first_drive); |
357 | if (microsd_init < 0) /* initialisation failed */ | 349 | sd_enable(false); |
358 | panicf("microSD init failed : %d", microsd_init); | 350 | if (rc >= 0) |
359 | 351 | success = 2; | |
360 | microsd_init = disk_mount(sd_first_drive); /* 0 if fail */ | 352 | else /* initialisation failed */ |
353 | panicf("microSD init failed : %d", rc); | ||
361 | } | 354 | } |
362 | 355 | ||
356 | /* Access is now safe */ | ||
357 | mutex_unlock(&sd_mtx); | ||
358 | |||
359 | if (success > 1) | ||
360 | success = disk_mount(sd_first_drive); /* 0 if fail */ | ||
361 | |||
363 | /* | 362 | /* |
364 | * Mount succeeded, or this was an EXTRACTED event, | 363 | * Mount succeeded, or this was an EXTRACTED event, |
365 | * in both cases notify the system about the changed filesystems | 364 | * in both cases notify the system about the changed filesystems |
366 | */ | 365 | */ |
367 | if (microsd_init) | 366 | if (success) |
368 | queue_broadcast(SYS_FS_CHANGED, 0); | 367 | queue_broadcast(SYS_FS_CHANGED, 0); |
369 | 368 | ||
370 | sd_enable(false); | ||
371 | |||
372 | /* Access is now safe */ | ||
373 | mutex_unlock(&sd_mtx); | ||
374 | fat_unlock(); | ||
375 | } | ||
376 | break; | 369 | break; |
377 | #endif | 370 | #endif /* HAVE_HOTSWAP */ |
371 | |||
378 | case SYS_TIMEOUT: | 372 | case SYS_TIMEOUT: |
379 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 373 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
380 | { | 374 | { |
diff --git a/firmware/target/arm/s3c2440/sd-s3c2440.c b/firmware/target/arm/s3c2440/sd-s3c2440.c index 6658fa1515..e8de3ac78d 100644 --- a/firmware/target/arm/s3c2440/sd-s3c2440.c +++ b/firmware/target/arm/s3c2440/sd-s3c2440.c | |||
@@ -34,7 +34,6 @@ | |||
34 | #ifdef HAVE_HOTSWAP | 34 | #ifdef HAVE_HOTSWAP |
35 | #include "sdmmc.h" | 35 | #include "sdmmc.h" |
36 | #include "disk.h" | 36 | #include "disk.h" |
37 | #include "fat.h" | ||
38 | #endif | 37 | #endif |
39 | #include "dma-target.h" | 38 | #include "dma-target.h" |
40 | #include "system-target.h" | 39 | #include "system-target.h" |
@@ -585,48 +584,29 @@ static void sd_thread(void) | |||
585 | { | 584 | { |
586 | #ifdef HAVE_HOTSWAP | 585 | #ifdef HAVE_HOTSWAP |
587 | case SYS_HOTSWAP_INSERTED: | 586 | case SYS_HOTSWAP_INSERTED: |
588 | case SYS_HOTSWAP_EXTRACTED: | 587 | case SYS_HOTSWAP_EXTRACTED:; |
589 | { | ||
590 | int success = 1; | 588 | int success = 1; |
591 | fat_lock(); /* lock-out FAT activity first - | ||
592 | prevent deadlocking via disk_mount that | ||
593 | would cause a reverse-order attempt with | ||
594 | another thread */ | ||
595 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
596 | into driver that bypass the fat cache */ | ||
597 | 589 | ||
598 | /* We now have exclusive control of fat cache and ata */ | 590 | disk_unmount(0); /* release "by force" */ |
599 | 591 | ||
600 | disk_unmount(0); /* release "by force", ensure file | 592 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
601 | descriptors aren't leaked and any busy | ||
602 | ones are invalid if mounting */ | ||
603 | 593 | ||
604 | /* Force card init for new card, re-init for re-inserted one or | 594 | /* Force card init for new card, re-init for re-inserted one or |
605 | * clear if the last attempt to init failed with an error. */ | 595 | * clear if the last attempt to init failed with an error. */ |
606 | card_info[0].initialized = 0; | 596 | card_info[0].initialized = 0; |
607 | 597 | ||
598 | /* Access is now safe */ | ||
599 | mutex_unlock(&sd_mtx); | ||
600 | |||
608 | if (ev.id == SYS_HOTSWAP_INSERTED) | 601 | if (ev.id == SYS_HOTSWAP_INSERTED) |
609 | { | ||
610 | /* FIXME: once sd_enabled is implement properly, | ||
611 | * reinitializing the controllers might be needed */ | ||
612 | sd_enable(true); | ||
613 | if (success < 0) /* initialisation failed */ | ||
614 | panicf("SD init failed : %d", success); | ||
615 | success = disk_mount(0); /* 0 if fail */ | 602 | success = disk_mount(0); /* 0 if fail */ |
616 | } | ||
617 | 603 | ||
618 | /* notify the system about the changed filesystems | 604 | /* notify the system about the changed filesystems |
619 | */ | 605 | */ |
620 | if (success) | 606 | if (success) |
621 | queue_broadcast(SYS_FS_CHANGED, 0); | 607 | queue_broadcast(SYS_FS_CHANGED, 0); |
622 | |||
623 | /* Access is now safe */ | ||
624 | mutex_unlock(&sd_mtx); | ||
625 | fat_unlock(); | ||
626 | sd_enable(false); | ||
627 | } | ||
628 | break; | 608 | break; |
629 | #endif | 609 | #endif /* HAVE_HOTSWAP */ |
630 | } | 610 | } |
631 | } | 611 | } |
632 | } | 612 | } |
diff --git a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c index 395c0f49e6..0fd74787d1 100644 --- a/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/storage_ata-ipod6g.c | |||
@@ -32,7 +32,7 @@ | |||
32 | #include "s5l8702.h" | 32 | #include "s5l8702.h" |
33 | #include "led.h" | 33 | #include "led.h" |
34 | #include "ata_idle_notify.h" | 34 | #include "ata_idle_notify.h" |
35 | #include "fat.h" | 35 | #include "disk_cache.h" |
36 | #include "splash.h" | 36 | #include "splash.h" |
37 | 37 | ||
38 | 38 | ||
@@ -68,6 +68,7 @@ static struct semaphore mmc_wakeup; | |||
68 | static struct semaphore mmc_comp_wakeup; | 68 | static struct semaphore mmc_comp_wakeup; |
69 | static int spinup_time = 0; | 69 | static int spinup_time = 0; |
70 | static int dma_mode = 0; | 70 | static int dma_mode = 0; |
71 | static char aligned_buffer[SECTOR_SIZE] __attribute__((aligned(0x10))); | ||
71 | 72 | ||
72 | 73 | ||
73 | #ifdef ATA_HAVE_BBT | 74 | #ifdef ATA_HAVE_BBT |
@@ -857,8 +858,25 @@ int ata_bbt_translate(uint64_t sector, uint32_t count, uint64_t* phys, uint32_t* | |||
857 | static int ata_rw_sectors(uint64_t sector, uint32_t count, void* buffer, bool write) | 858 | static int ata_rw_sectors(uint64_t sector, uint32_t count, void* buffer, bool write) |
858 | { | 859 | { |
859 | if (((uint32_t)buffer) & 0xf) | 860 | if (((uint32_t)buffer) & 0xf) |
860 | panicf("ATA: Misaligned data buffer at %08X (sector %lu, count %lu)", | 861 | { |
861 | (unsigned int)buffer, (long unsigned int)sector, (long unsigned int)count); | 862 | while (count) |
863 | { | ||
864 | if (write) | ||
865 | memcpy(aligned_buffer, buffer, SECTOR_SIZE); | ||
866 | |||
867 | PASS_RC(ata_rw_sectors(sector, 1, aligned_buffer, write), 0, 0); | ||
868 | |||
869 | if (!write) | ||
870 | memcpy(buffer, aligned_buffer, SECTOR_SIZE); | ||
871 | |||
872 | buffer += SECTOR_SIZE; | ||
873 | sector++; | ||
874 | count--; | ||
875 | } | ||
876 | |||
877 | return 0; | ||
878 | } | ||
879 | |||
862 | #ifdef ATA_HAVE_BBT | 880 | #ifdef ATA_HAVE_BBT |
863 | if (sector + count > ata_virtual_sectors) RET_ERR(0); | 881 | if (sector + count > ata_virtual_sectors) RET_ERR(0); |
864 | if (ata_bbt) | 882 | if (ata_bbt) |
@@ -1117,14 +1135,13 @@ int ata_init(void) | |||
1117 | -- Michael Sparmann (theseven), 2011-10-22 */ | 1135 | -- Michael Sparmann (theseven), 2011-10-22 */ |
1118 | if (!ceata) | 1136 | if (!ceata) |
1119 | { | 1137 | { |
1120 | unsigned char* sector = fat_get_sector_buffer(); | 1138 | unsigned char* sector = aligned_buffer; |
1121 | ata_rw_sectors(0, 1, sector, false); | 1139 | ata_rw_sectors(0, 1, sector, false); |
1122 | if (sector[510] == 0xaa && sector[511] == 0x55) | 1140 | if (sector[510] == 0xaa && sector[511] == 0x55) |
1123 | { | 1141 | { |
1124 | ata_swap = true; | 1142 | ata_swap = true; |
1125 | splashf(5000, "Wrong HDD endianness, please update your emCORE version!"); | 1143 | splashf(5000, "Wrong HDD endianness, please update your emCORE version!"); |
1126 | } | 1144 | } |
1127 | fat_release_sector_buffer(); | ||
1128 | } | 1145 | } |
1129 | 1146 | ||
1130 | create_thread(ata_thread, ata_stack, | 1147 | create_thread(ata_thread, ata_stack, |
diff --git a/firmware/target/arm/tcc780x/sd-tcc780x.c b/firmware/target/arm/tcc780x/sd-tcc780x.c index 55ae4e7c70..b7abea8be4 100644 --- a/firmware/target/arm/tcc780x/sd-tcc780x.c +++ b/firmware/target/arm/tcc780x/sd-tcc780x.c | |||
@@ -28,7 +28,6 @@ | |||
28 | #include "led.h" | 28 | #include "led.h" |
29 | #include "thread.h" | 29 | #include "thread.h" |
30 | #include "disk.h" | 30 | #include "disk.h" |
31 | #include "fat.h" | ||
32 | #include "ata_idle_notify.h" | 31 | #include "ata_idle_notify.h" |
33 | #include "usb.h" | 32 | #include "usb.h" |
34 | 33 | ||
@@ -657,35 +656,30 @@ static void sd_thread(void) | |||
657 | { | 656 | { |
658 | #ifdef HAVE_HOTSWAP | 657 | #ifdef HAVE_HOTSWAP |
659 | case SYS_HOTSWAP_INSERTED: | 658 | case SYS_HOTSWAP_INSERTED: |
660 | case SYS_HOTSWAP_EXTRACTED: | 659 | case SYS_HOTSWAP_EXTRACTED:; |
661 | fat_lock(); /* lock-out FAT activity first - | 660 | int success = 1; |
662 | prevent deadlocking via disk_mount that | 661 | |
663 | would cause a reverse-order attempt with | 662 | /* Release "by force" */ |
664 | another thread */ | ||
665 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
666 | into driver that bypass the fat cache */ | ||
667 | |||
668 | /* We now have exclusive control of fat cache and ata */ | ||
669 | |||
670 | /* Release "by force", ensure file descriptors aren't leaked and | ||
671 | any busy ones are invalid if mounting */ | ||
672 | disk_unmount(sd_first_drive + CARD_NUM_SLOT); | 663 | disk_unmount(sd_first_drive + CARD_NUM_SLOT); |
673 | 664 | ||
665 | mutex_lock(&sd_mtx); /* lock-out card activity */ | ||
666 | |||
674 | /* Force card init for new card, re-init for re-inserted one or | 667 | /* Force card init for new card, re-init for re-inserted one or |
675 | * clear if the last attempt to init failed with an error. */ | 668 | * clear if the last attempt to init failed with an error. */ |
676 | card_info[CARD_NUM_SLOT].initialized = 0; | 669 | card_info[CARD_NUM_SLOT].initialized = 0; |
677 | sd_status[CARD_NUM_SLOT].retry = 0; | 670 | sd_status[CARD_NUM_SLOT].retry = 0; |
678 | 671 | ||
672 | mutex_unlock(&sd_mtx); | ||
673 | |||
679 | if (ev.id == SYS_HOTSWAP_INSERTED) | 674 | if (ev.id == SYS_HOTSWAP_INSERTED) |
680 | disk_mount(sd_first_drive + CARD_NUM_SLOT); | 675 | success = disk_mount(sd_first_drive + CARD_NUM_SLOT); |
681 | 676 | ||
682 | queue_broadcast(SYS_FS_CHANGED, 0); | 677 | if (success) |
678 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
683 | 679 | ||
684 | /* Access is now safe */ | ||
685 | mutex_unlock(&sd_mtx); | ||
686 | fat_unlock(); | ||
687 | break; | 680 | break; |
688 | #endif | 681 | #endif /* HAVE_HOTSWAP */ |
682 | |||
689 | case SYS_TIMEOUT: | 683 | case SYS_TIMEOUT: |
690 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 684 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
691 | { | 685 | { |
diff --git a/firmware/target/arm/tms320dm320/sdmmc-dm320.c b/firmware/target/arm/tms320dm320/sdmmc-dm320.c index 284061e1ad..d46dbf5e40 100644 --- a/firmware/target/arm/tms320dm320/sdmmc-dm320.c +++ b/firmware/target/arm/tms320dm320/sdmmc-dm320.c | |||
@@ -592,48 +592,29 @@ static void sd_thread(void) | |||
592 | { | 592 | { |
593 | #ifdef HAVE_HOTSWAP | 593 | #ifdef HAVE_HOTSWAP |
594 | case SYS_HOTSWAP_INSERTED: | 594 | case SYS_HOTSWAP_INSERTED: |
595 | case SYS_HOTSWAP_EXTRACTED: | 595 | case SYS_HOTSWAP_EXTRACTED:; |
596 | { | ||
597 | int success = 1; | 596 | int success = 1; |
598 | fat_lock(); /* lock-out FAT activity first - | ||
599 | prevent deadlocking via disk_mount that | ||
600 | would cause a reverse-order attempt with | ||
601 | another thread */ | ||
602 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
603 | into driver that bypass the fat cache */ | ||
604 | 597 | ||
605 | /* We now have exclusive control of fat cache and ata */ | 598 | disk_unmount(0); /* release "by force" */ |
606 | 599 | ||
607 | disk_unmount(0); /* release "by force", ensure file | 600 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
608 | descriptors aren't leaked and any busy | ||
609 | ones are invalid if mounting */ | ||
610 | 601 | ||
611 | /* Force card init for new card, re-init for re-inserted one or | 602 | /* Force card init for new card, re-init for re-inserted one or |
612 | * clear if the last attempt to init failed with an error. */ | 603 | * clear if the last attempt to init failed with an error. */ |
613 | card_info[0].initialized = 0; | 604 | card_info[0].initialized = 0; |
614 | 605 | ||
606 | mutex_unlock(&sd_mtx); | ||
607 | |||
615 | if (ev.id == SYS_HOTSWAP_INSERTED) | 608 | if (ev.id == SYS_HOTSWAP_INSERTED) |
616 | { | ||
617 | /* FIXME: once sd_enabled is implement properly, | ||
618 | * reinitializing the controllers might be needed */ | ||
619 | sd_enable(true); | ||
620 | if (success < 0) /* initialisation failed */ | ||
621 | panicf("SD init failed : %d", success); | ||
622 | success = disk_mount(0); /* 0 if fail */ | 609 | success = disk_mount(0); /* 0 if fail */ |
623 | } | ||
624 | 610 | ||
625 | /* notify the system about the changed filesystems | 611 | /* notify the system about the changed filesystems */ |
626 | */ | ||
627 | if (success) | 612 | if (success) |
628 | queue_broadcast(SYS_FS_CHANGED, 0); | 613 | queue_broadcast(SYS_FS_CHANGED, 0); |
629 | 614 | ||
630 | /* Access is now safe */ | ||
631 | mutex_unlock(&sd_mtx); | ||
632 | fat_unlock(); | ||
633 | sd_enable(false); | ||
634 | } | ||
635 | break; | 615 | break; |
636 | #endif | 616 | #endif /* HAVE_HOTSWAP */ |
617 | |||
637 | case SYS_TIMEOUT: | 618 | case SYS_TIMEOUT: |
638 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 619 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
639 | { | 620 | { |
diff --git a/firmware/target/hosted/filesystem-app.c b/firmware/target/hosted/filesystem-app.c new file mode 100644 index 0000000000..7ef8d3109b --- /dev/null +++ b/firmware/target/hosted/filesystem-app.c | |||
@@ -0,0 +1,562 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Thomas Martitz | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #define RB_FILESYSTEM_OS | ||
22 | #include <stdio.h> /* snprintf */ | ||
23 | #include <stdlib.h> | ||
24 | #include <stdarg.h> | ||
25 | #include <time.h> | ||
26 | #include <errno.h> | ||
27 | #include <string.h> | ||
28 | #include <limits.h> | ||
29 | #include "config.h" | ||
30 | #include "system.h" | ||
31 | #include "file.h" | ||
32 | #include "dir.h" | ||
33 | #include "file_internal.h" | ||
34 | #include "pathfuncs.h" | ||
35 | #include "string-extra.h" | ||
36 | #include "rbpaths.h" | ||
37 | #include "logf.h" | ||
38 | |||
39 | |||
40 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
41 | static const char rbhome[] = "/sdcard"; | ||
42 | #elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO|PLATFORM_PANDORA)) \ | ||
43 | && !defined(__PCTOOL__) | ||
44 | const char *rbhome; | ||
45 | #else | ||
46 | /* YPR0, YPR1 */ | ||
47 | static const char rbhome[] = HOME_DIR; | ||
48 | #endif | ||
49 | |||
50 | #if !(defined(SAMSUNG_YPR0) || defined(SAMSUNG_YPR1)) && !defined(__PCTOOL__) | ||
51 | /* Special dirs are user-accessible (and user-writable) dirs which take priority | ||
52 | * over the ones where Rockbox is installed to. Classic example would be | ||
53 | * $HOME/.config/rockbox.org vs /usr/share/rockbox */ | ||
54 | #define HAVE_SPECIAL_DIRS | ||
55 | #endif | ||
56 | |||
57 | #ifdef HAVE_MULTIDRIVE | ||
58 | /* This is to compare any opened directories with the home directory so that | ||
59 | the special drive links may be returned for it only */ | ||
60 | static int rbhome_fildes = -1; | ||
61 | |||
62 | /* A special link is created under e.g. HOME_DIR/<microSD1>, e.g. to access | ||
63 | * external storage in a convenient location, much similar to the mount | ||
64 | * point on our native targets. Here they are treated as symlink (one which | ||
65 | * doesn't actually exist in the filesystem and therefore we have to override | ||
66 | * readlink() */ | ||
67 | static const char *handle_special_links(const char* link, unsigned flags, | ||
68 | char *buf, const size_t bufsize) | ||
69 | { | ||
70 | (void) flags; | ||
71 | char vol_string[VOL_MAX_LEN + 1]; | ||
72 | get_volume_name(-1, vol_string); | ||
73 | |||
74 | /* link might be passed with or without HOME_DIR expanded. To handle | ||
75 | * both perform substring matching (VOL_NAMES is unique enough) */ | ||
76 | const char *begin = strstr(link, vol_string); | ||
77 | if (begin) | ||
78 | { | ||
79 | /* begin now points to the start of vol_string within link, | ||
80 | * we want to copy the remainder of the paths, prefixed by | ||
81 | * the actual mount point (the remainder might be "") */ | ||
82 | snprintf(buf, bufsize, MULTIDRIVE_DIR"%s", begin + len); | ||
83 | return buf; | ||
84 | } | ||
85 | |||
86 | return link; | ||
87 | } | ||
88 | #endif | ||
89 | |||
90 | #ifdef HAVE_MULTIDRIVE | ||
91 | /* we keep an open descriptor of the home directory to detect when it has been | ||
92 | opened by opendir() so that its "symlinks" may be enumerated */ | ||
93 | static void cleanup_rbhome(void) | ||
94 | { | ||
95 | os_close(rbhome_fildes); | ||
96 | rbhome_fildes = -1; | ||
97 | } | ||
98 | #endif /* HAVE_MULTIDRIVE */ | ||
99 | |||
100 | void paths_init(void) | ||
101 | { | ||
102 | #ifdef HAVE_SPECIAL_DIRS | ||
103 | /* make sure $HOME/.config/rockbox.org exists, it's needed for config.cfg */ | ||
104 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
105 | os_mkdir("/sdcard/rockbox" __MKDIR_MODE_ARG); | ||
106 | os_mkdir("/sdcard/rockbox/rocks.data" __MKDIR_MODE_ARG); | ||
107 | #else | ||
108 | char config_dir[MAX_PATH]; | ||
109 | |||
110 | const char *home = getenv("RBROOT"); | ||
111 | if (!home) | ||
112 | { | ||
113 | home = getenv("HOME"); | ||
114 | } | ||
115 | |||
116 | if (!home) | ||
117 | { | ||
118 | logf("HOME environment var not set. Can't write config"); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | rbhome = home; | ||
123 | snprintf(config_dir, sizeof(config_dir), "%s/.config", home); | ||
124 | os_mkdir(config_dir __MKDIR_MODE_ARG); | ||
125 | snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org", home); | ||
126 | os_mkdir(config_dir __MKDIR_MODE_ARG); | ||
127 | /* Plugin data directory */ | ||
128 | snprintf(config_dir, sizeof(config_dir), "%s/.config/rockbox.org/rocks.data", home); | ||
129 | os_mkdir(config_dir __MKDIR_MODE_ARG); | ||
130 | #endif | ||
131 | #endif /* HAVE_SPECIAL_DIRS */ | ||
132 | |||
133 | #ifdef HAVE_MULTIDRIVE | ||
134 | /* if this fails then alternate volumes will not work, but this function | ||
135 | cannot return that fact */ | ||
136 | rbhome_fildes = os_opendirfd(rbhome); | ||
137 | if (rbhome_fildes >= 0) | ||
138 | atexit(cleanup_rbhome); | ||
139 | #endif /* HAVE_MULTIDRIVE */ | ||
140 | } | ||
141 | |||
142 | #ifdef HAVE_SPECIAL_DIRS | ||
143 | static const char* _get_user_file_path(const char *path, | ||
144 | unsigned flags, | ||
145 | char* buf, | ||
146 | const size_t bufsize) | ||
147 | { | ||
148 | const char *ret = path; | ||
149 | const char *pos = path; | ||
150 | /* replace ROCKBOX_DIR in path with $HOME/.config/rockbox.org */ | ||
151 | pos += ROCKBOX_DIR_LEN; | ||
152 | if (*pos == '/') pos += 1; | ||
153 | |||
154 | #if (CONFIG_PLATFORM & PLATFORM_ANDROID) | ||
155 | if (path_append(buf, "/sdcard/rockbox", pos, bufsize) >= bufsize) | ||
156 | return NULL; | ||
157 | #else | ||
158 | if (path_append(buf, rbhome, ".config/rockbox.org", bufsize) >= bufsize || | ||
159 | path_append(buf, PA_SEP_SOFT, pos, bufsize) >= bufsize) | ||
160 | return NULL; | ||
161 | #endif | ||
162 | |||
163 | /* always return the replacement buffer (pointing to $HOME) if | ||
164 | * write access is needed */ | ||
165 | if (flags & NEED_WRITE) | ||
166 | ret = buf; | ||
167 | else if (os_file_exists(buf)) | ||
168 | ret = buf; | ||
169 | |||
170 | if (ret != buf) /* not found in $HOME, try ROCKBOX_BASE_DIR, !NEED_WRITE only */ | ||
171 | { | ||
172 | if (path_append(buf, ROCKBOX_SHARE_PATH, pos, bufsize) >= bufsize) | ||
173 | return NULL; | ||
174 | |||
175 | if (os_file_exists(buf)) | ||
176 | ret = buf; | ||
177 | } | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | #endif | ||
183 | |||
184 | const char * handle_special_dirs(const char *dir, unsigned flags, | ||
185 | char *buf, const size_t bufsize) | ||
186 | { | ||
187 | (void) flags; (void) buf; (void) bufsize; | ||
188 | #ifdef HAVE_SPECIAL_DIRS | ||
189 | if (!strncmp(HOME_DIR, dir, HOME_DIR_LEN)) | ||
190 | { | ||
191 | const char *p = dir + HOME_DIR_LEN; | ||
192 | while (*p == '/') p++; | ||
193 | snprintf(buf, bufsize, "%s/%s", rbhome, p); | ||
194 | dir = buf; | ||
195 | } | ||
196 | else if (!strncmp(ROCKBOX_DIR, dir, ROCKBOX_DIR_LEN)) | ||
197 | dir = _get_user_file_path(dir, flags, buf, bufsize); | ||
198 | #endif | ||
199 | #ifdef HAVE_MULTIDRIVE | ||
200 | dir = handle_special_links(dir, flags, buf, bufsize); | ||
201 | #endif | ||
202 | return dir; | ||
203 | } | ||
204 | |||
205 | int app_open(const char *path, int oflag, ...) | ||
206 | { | ||
207 | int flags = IS_FILE; | ||
208 | if (oflag & O_ACCMODE) | ||
209 | flags |= NEED_WRITE; | ||
210 | |||
211 | char realpath[MAX_PATH]; | ||
212 | const char *fpath = handle_special_dirs(path, flags, realpath, | ||
213 | sizeof (realpath)); | ||
214 | if (!fpath) | ||
215 | FILE_ERROR_RETURN(ENAMETOOLONG, -1); | ||
216 | |||
217 | return os_open(fpath, oflag __OPEN_MODE_ARG); | ||
218 | } | ||
219 | |||
220 | int app_creat(const char *path, mode_t mode) | ||
221 | { | ||
222 | return app_open(path, O_CREAT|O_WRONLY|O_TRUNC, mode); | ||
223 | } | ||
224 | |||
225 | int app_remove(const char *path) | ||
226 | { | ||
227 | char realpath[MAX_PATH]; | ||
228 | const char *fpath = handle_special_dirs(path, NEED_WRITE, realpath, | ||
229 | sizeof (realpath)); | ||
230 | if (!fpath) | ||
231 | FILE_ERROR_RETURN(ENAMETOOLONG, -1); | ||
232 | |||
233 | return os_remove(fpath); | ||
234 | } | ||
235 | |||
236 | int app_rename(const char *old, const char *new) | ||
237 | { | ||
238 | char realpath_old[MAX_PATH], realpath_new[MAX_PATH]; | ||
239 | const char *fold = handle_special_dirs(old, NEED_WRITE, realpath_old, | ||
240 | sizeof (realpath_old)); | ||
241 | const char *fnew = handle_special_dirs(new, NEED_WRITE, realpath_new, | ||
242 | sizeof (realpath_new)); | ||
243 | if (!fold || !fnew) | ||
244 | FILE_ERROR_RETURN(ENAMETOOLONG, -1); | ||
245 | |||
246 | return os_rename(fold, fnew); | ||
247 | } | ||
248 | |||
249 | #ifdef HAVE_SDL_THREADS | ||
250 | ssize_t app_read(int fd, void *buf, size_t nbyte) | ||
251 | { | ||
252 | return os_read(fd, buf, nbyte); | ||
253 | } | ||
254 | |||
255 | ssize_t app_write(int fd, const void *buf, size_t nbyte) | ||
256 | { | ||
257 | return os_write(fd, buf, nbyte); | ||
258 | } | ||
259 | #endif /* HAVE_SDL_THREADS */ | ||
260 | |||
261 | int app_relate(const char *path1, const char *path2) | ||
262 | { | ||
263 | char realpath_1[MAX_PATH], realpath_2[MAX_PATH]; | ||
264 | const char *fpath1 = handle_special_dirs(path1, 0, realpath_1, | ||
265 | sizeof (realpath_1)); | ||
266 | const char *fpath2 = handle_special_dirs(path2, 0, realpath_2, | ||
267 | sizeof (realpath_2)); | ||
268 | |||
269 | if (!fpath1 || !fpath2) | ||
270 | FILE_ERROR_RETURN(ENAMETOOLONG, -1); | ||
271 | |||
272 | return os_relate(fpath1, fpath2); | ||
273 | } | ||
274 | |||
275 | bool app_file_exists(const char *path) | ||
276 | { | ||
277 | char realpath[MAX_PATH]; | ||
278 | const char *fpath = handle_special_dirs(path, NEED_WRITE, realpath, | ||
279 | sizeof (realpath)); | ||
280 | if (!fpath) | ||
281 | FILE_ERROR_RETURN(ENAMETOOLONG, false); | ||
282 | |||
283 | return os_file_exists(fpath); | ||
284 | } | ||
285 | |||
286 | /* need to wrap around DIR* because we need to save the parent's directory | ||
287 | * path in order to determine dirinfo for volumes or convert the path to UTF-8; | ||
288 | * also is required to implement get_dir_info() */ | ||
289 | struct __dir | ||
290 | { | ||
291 | OS_DIR_T *osdirp; | ||
292 | #ifdef HAVE_MULTIDRIVE | ||
293 | int volumes_returned; | ||
294 | #endif | ||
295 | int osfd; | ||
296 | bool osfd_is_opened; | ||
297 | #if defined(OS_DIRENT_CONVERT) || defined (HAVE_MULTIDRIVE) | ||
298 | #define USE_DIRENTP | ||
299 | struct dirent *direntp; | ||
300 | size_t d_name_size; | ||
301 | #endif | ||
302 | char path[]; | ||
303 | }; | ||
304 | |||
305 | static void __dir_free(struct __dir *this) | ||
306 | { | ||
307 | if (!this) | ||
308 | return; | ||
309 | |||
310 | #ifdef USE_DIRENTP | ||
311 | free(this->direntp); | ||
312 | #endif | ||
313 | |||
314 | if (this->osfd_is_opened) | ||
315 | os_close(this->osfd); | ||
316 | |||
317 | free(this); | ||
318 | } | ||
319 | |||
320 | DIR * app_opendir(const char *dirname) | ||
321 | { | ||
322 | int rc; | ||
323 | char realpath[MAX_PATH]; | ||
324 | const char *fname = handle_special_dirs(dirname, 0, realpath, | ||
325 | sizeof (realpath)); | ||
326 | if (!fname) | ||
327 | FILE_ERROR_RETURN(ENAMETOOLONG, NULL); | ||
328 | |||
329 | size_t name_len = path_strip_trailing_separators(fname, &fname); | ||
330 | struct __dir *this = calloc(1, sizeof (*this) + name_len + 1); | ||
331 | if (!this) | ||
332 | FILE_ERROR(ENOMEM, RC); | ||
333 | |||
334 | #ifdef USE_DIRENTP | ||
335 | /* allocate what we're really going to return to callers, making certain | ||
336 | it has at least the d_name size we want */ | ||
337 | this->d_name_size = MAX(MAX_PATH, sizeof (this->direntp->d_name)); | ||
338 | this->direntp = calloc(1, offsetof(typeof (*this->direntp), d_name) + | ||
339 | this->d_name_size); | ||
340 | if (!this->direntp) | ||
341 | FILE_ERROR(ENOMEM, RC); | ||
342 | |||
343 | /* only the d_name field will be valid but that is all that anyone may | ||
344 | truely count on portably existing */ | ||
345 | #endif /* USE_DIRENTP */ | ||
346 | |||
347 | strmemcpy(this->path, fname, name_len); | ||
348 | |||
349 | rc = os_opendir_and_fd(this->path, &this->osdirp, &this->osfd); | ||
350 | if (rc < 0) | ||
351 | FILE_ERROR(ERRNO, RC); | ||
352 | |||
353 | this->osfd_is_opened = rc > 0; | ||
354 | |||
355 | #ifdef HAVE_MULTIDRIVE | ||
356 | this->volumes_returned = INT_MAX; /* assume NOT $HOME */ | ||
357 | if (rbhome_fildes >= 0 && os_samefile(rbhome_fildes, fd) > 0) | ||
358 | this->volumes_returned = 0; /* there's no place like $HOME */ | ||
359 | #endif /* HAVE_MULTIDRIVE */ | ||
360 | |||
361 | return (DIR *)this; | ||
362 | file_error: | ||
363 | __dir_free(this); | ||
364 | return NULL; | ||
365 | } | ||
366 | |||
367 | int app_closedir(DIR *dirp) | ||
368 | { | ||
369 | struct __dir *this = (struct __dir *)dirp; | ||
370 | if (!this) | ||
371 | FILE_ERROR_RETURN(EBADF, -1); | ||
372 | |||
373 | OS_DIR_T *osdirp = this->osdirp; | ||
374 | __dir_free(this); | ||
375 | |||
376 | return os_closedir(osdirp); | ||
377 | } | ||
378 | |||
379 | struct dirent * app_readdir(DIR *dirp) | ||
380 | { | ||
381 | struct __dir *this = (struct __dir *)dirp; | ||
382 | if (!this) | ||
383 | FILE_ERROR_RETURN(EBADF, NULL); | ||
384 | |||
385 | #ifdef HAVE_MULTIDRIVE | ||
386 | if (this->volumes_returned < NUM_VOLUMES) | ||
387 | { | ||
388 | while (++this->volumes_returned < NUM_VOLUMES) | ||
389 | { | ||
390 | if (!volume_present(this->volumes_returned)) | ||
391 | continue; | ||
392 | |||
393 | get_volume_name(this->volumes_returned, this->direntp->d_name); | ||
394 | return this->direntp; | ||
395 | } | ||
396 | } | ||
397 | /* do normal directory reads */ | ||
398 | #endif /* HAVE_MULTIDRIVE */ | ||
399 | |||
400 | OS_DIRENT_T *osdirent = os_readdir(this->osdirp); | ||
401 | |||
402 | #ifdef OS_DIRENT_CONVERT | ||
403 | if (strlcpy_from_os(this->direntp->d_name, osdirent->d_name, | ||
404 | this->d_name_size) >= this->d_name_size) | ||
405 | { | ||
406 | this->direntp->d_name[0] = '\0'; | ||
407 | errno = EOVERFLOW; | ||
408 | return NULL; | ||
409 | } | ||
410 | |||
411 | osdirent = (OS_DIRENT_T *)this->direntp; | ||
412 | #endif /* OS_DIRENT_CONVERT */ | ||
413 | |||
414 | return (struct dirent *)osdirent; | ||
415 | } | ||
416 | |||
417 | int app_mkdir(const char *path) | ||
418 | { | ||
419 | char realpath[MAX_PATH]; | ||
420 | const char *fname = handle_special_dirs(path, NEED_WRITE, realpath, | ||
421 | sizeof (realpath)); | ||
422 | if (!fname) | ||
423 | FILE_ERROR_RETURN(ENAMETOOLONG, -1); | ||
424 | |||
425 | return os_mkdir(fname __MKDIR_MODE_ARG); | ||
426 | } | ||
427 | |||
428 | int app_rmdir(const char *path) | ||
429 | { | ||
430 | char realpath[MAX_PATH]; | ||
431 | const char *fname = handle_special_dirs(path, NEED_WRITE, realpath, | ||
432 | sizeof (realpath)); | ||
433 | if (!fname) | ||
434 | FILE_ERROR_RETURN(ENAMETOOLONG, -1); | ||
435 | |||
436 | return os_rmdir(fname); | ||
437 | } | ||
438 | |||
439 | int app_samedir(DIR *dirp1, DIR *dirp2) | ||
440 | { | ||
441 | struct __dir *this1 = (struct __dir *)dirp1; | ||
442 | struct __dir *this2 = (struct __dir *)dirp2; | ||
443 | |||
444 | if (!this1 || !this2) | ||
445 | { | ||
446 | errno = EBADF; | ||
447 | return -1; | ||
448 | } | ||
449 | |||
450 | return os_fsamefile(this1->osfd, this2->osfd); | ||
451 | } | ||
452 | |||
453 | bool app_dir_exists(const char *dirname) | ||
454 | { | ||
455 | char realpath[MAX_PATH]; | ||
456 | const char *fname = handle_special_dirs(dirname, 0, realpath, | ||
457 | sizeof (realpath)); | ||
458 | if (!fname) | ||
459 | FILE_ERROR_RETURN(ENAMETOOLONG, false); | ||
460 | |||
461 | OS_DIR_T *osdirp = os_opendir(fname); | ||
462 | if (!osdirp) | ||
463 | return false; | ||
464 | |||
465 | os_closedir(osdirp); | ||
466 | return true; | ||
467 | } | ||
468 | |||
469 | struct dirinfo dir_get_info(DIR *dirp, struct dirent *entry) | ||
470 | { | ||
471 | struct __dir *this = (struct __dir *)dirp; | ||
472 | struct dirinfo ret = { .mtime = 0 }; | ||
473 | |||
474 | if (!this) | ||
475 | FILE_ERROR_RETURN(EBADF, ret); | ||
476 | |||
477 | if (!entry || entry->d_name[0] == '\0') | ||
478 | FILE_ERROR_RETURN(ENOENT, ret); | ||
479 | |||
480 | char path[MAX_PATH]; | ||
481 | |||
482 | #ifdef HAVE_MULTIDRIVE | ||
483 | if (this->volumes_returned < NUM_VOLUMES) | ||
484 | { | ||
485 | /* last thing read was a "symlink" */ | ||
486 | ret.attribute = ATTR_LINK; | ||
487 | strcpy(path, MULTIDRIVE_DIR); | ||
488 | } | ||
489 | else | ||
490 | #endif | ||
491 | if (path_append(path, this->path, entry->d_name, sizeof (path)) | ||
492 | >= sizeof (path)) | ||
493 | { | ||
494 | FILE_ERROR_RETURN(ENAMETOOLONG, ret); | ||
495 | } | ||
496 | |||
497 | struct stat s; | ||
498 | if (os_lstat(path, &s) < 0) | ||
499 | FILE_ERROR_RETURN(ERRNO, ret); | ||
500 | |||
501 | int err = 0; | ||
502 | if (S_ISLNK(s.st_mode)) | ||
503 | { | ||
504 | ret.attribute |= ATTR_LINK; | ||
505 | err = os_stat(path, &s); | ||
506 | } | ||
507 | |||
508 | if (err < 0) | ||
509 | FILE_ERROR_RETURN(ERRNO, ret); | ||
510 | |||
511 | if (S_ISDIR(s.st_mode)) | ||
512 | ret.attribute |= ATTR_DIRECTORY; | ||
513 | |||
514 | ret.size = s.st_size; | ||
515 | |||
516 | struct tm tm; | ||
517 | if (!localtime_r(&s.st_mtime, &tm)) | ||
518 | FILE_ERROR_RETURN(ERRNO, ret); | ||
519 | |||
520 | ret.mtime = mktime(&tm); | ||
521 | return ret; | ||
522 | } | ||
523 | |||
524 | /* On MD we create a virtual symlink for the external drive, | ||
525 | * for this we need to override readlink(). */ | ||
526 | ssize_t app_readlink(const char *path, char *buf, size_t bufsiz) | ||
527 | { | ||
528 | char _buf[MAX_PATH]; | ||
529 | path = handle_special_dirs(path, 0, _buf, sizeof(_buf)); | ||
530 | #ifdef HAVE_MULTIDRIVE | ||
531 | /* if path == _buf then we can be sure handle_special_dir() did something | ||
532 | * and path is not an ordinary directory */ | ||
533 | if (path == _buf && !strncmp(path, MULTIDRIVE_DIR, sizeof(MULTIDRIVE_DIR)-1)) | ||
534 | { | ||
535 | /* copying NUL is not required as per readlink specification */ | ||
536 | ssize_t len = strlen(path); | ||
537 | memcpy(buf, path, len); | ||
538 | return len; | ||
539 | } | ||
540 | #endif | ||
541 | /* does not append NUL !! */ | ||
542 | return os_readlink(path, buf, bufsiz); | ||
543 | (void) path; (void) buf; (void) bufsiz; | ||
544 | } | ||
545 | |||
546 | int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize) | ||
547 | { | ||
548 | #ifdef HAVE_MULTIVOLUME | ||
549 | char volname[VOL_MAX_LEN + 1]; | ||
550 | get_volume_name(volume, volname); | ||
551 | #else | ||
552 | const char *volname = "/"; | ||
553 | #endif | ||
554 | |||
555 | if (!handle_special_dirs(volname, NEED_WRITE, buffer, bufsize)) | ||
556 | { | ||
557 | errno = ENAMETOOLONG; | ||
558 | return -1; | ||
559 | } | ||
560 | |||
561 | return 0; | ||
562 | } | ||
diff --git a/firmware/target/hosted/filesystem-app.h b/firmware/target/hosted/filesystem-app.h new file mode 100644 index 0000000000..68b3f13b6e --- /dev/null +++ b/firmware/target/hosted/filesystem-app.h | |||
@@ -0,0 +1,117 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef _FILESYSTEM_APP_H_ | ||
23 | #define _FILESYSTEM_APP_H_ | ||
24 | |||
25 | #if defined(PLUGIN) || defined(CODEC) | ||
26 | /* Prevent often-problematic plugin namespace pollution */ | ||
27 | #define FILEFUNCTIONS_DECLARED | ||
28 | #define FILEFUNCTIONS_DEFINED | ||
29 | #define DIRFUNCTIONS_DECLARED | ||
30 | #define DIRFUNCTIONS_DEFINED | ||
31 | #define OSFUNCTIONS_DECLARED | ||
32 | #endif /* PLUGIN || CODEC */ | ||
33 | |||
34 | /* flags for get_user_file_path() */ | ||
35 | /* whether you need write access to that file/dir, especially true | ||
36 | * for runtime generated files (config.cfg) */ | ||
37 | #define NEED_WRITE (1<<0) | ||
38 | /* file or directory? */ | ||
39 | #define IS_FILE (1<<1) | ||
40 | |||
41 | #ifndef OSFUNCTIONS_DECLARED | ||
42 | #define FS_PREFIX(_x_) app_ ## _x_ | ||
43 | |||
44 | void paths_init(void); | ||
45 | const char * handle_special_dirs(const char *dir, unsigned flags, | ||
46 | char *buf, const size_t bufsize); | ||
47 | |||
48 | #endif /* !OSFUNCTIONS_DECLARED */ | ||
49 | #endif /* _FILESYSTEM_APP_H_ */ | ||
50 | |||
51 | #ifdef HAVE_SDL | ||
52 | #include "filesystem-sdl.h" | ||
53 | #endif /* HAVE_SDL */ | ||
54 | #ifdef WIN32 | ||
55 | #include "filesystem-win32.h" | ||
56 | #else /* !WIN32 */ | ||
57 | #include "filesystem-unix.h" | ||
58 | #endif /* WIN32 */ | ||
59 | #include "filesystem-hosted.h" | ||
60 | |||
61 | #ifdef _FILE_H_ | ||
62 | #ifndef _FILESYSTEM_APP__FILE_H_ | ||
63 | #define _FILESYSTEM_APP__FILE_H_ | ||
64 | |||
65 | #ifdef RB_FILESYSTEM_OS | ||
66 | #define FILEFUNCTIONS_DEFINED | ||
67 | #endif | ||
68 | |||
69 | #ifndef FILEFUNCTIONS_DECLARED | ||
70 | int app_open(const char *name, int oflag, ...); | ||
71 | int app_creat(const char *name, mode_t mode); | ||
72 | #define app_close os_close | ||
73 | #define app_ftruncate os_ftruncate | ||
74 | #define app_fsync os_fsync | ||
75 | #define app_lseek os_lseek | ||
76 | #ifdef HAVE_SDL_THREADS | ||
77 | ssize_t app_read(int fildes, void *buf, size_t nbyte); | ||
78 | ssize_t app_write(int fildes, const void *buf, size_t nbyte); | ||
79 | #else | ||
80 | #define app_read os_read | ||
81 | #define app_write os_write | ||
82 | #endif /* HAVE_SDL_THREADS */ | ||
83 | int app_remove(const char *path); | ||
84 | int app_rename(const char *old, const char *new); | ||
85 | #define app_filesize os_filesize | ||
86 | #define app_fsamefile os_fsamefile | ||
87 | int app_relate(const char *path1, const char *path2); | ||
88 | bool app_file_exists(const char *path); | ||
89 | ssize_t app_readlink(const char *path, char *buf, size_t bufsize); | ||
90 | #endif /* !FILEFUNCTIONS_DECLARED */ | ||
91 | |||
92 | #endif /* _FILESYSTEM_APP__FILE_H_ */ | ||
93 | #endif /* _FILE_H_ */ | ||
94 | |||
95 | #ifdef _DIR_H_ | ||
96 | #ifndef _FILESYSTEM_APP__DIR_H_ | ||
97 | #define _FILESYSTEM_APP__DIR_H_ | ||
98 | |||
99 | #ifdef RB_FILESYSTEM_OS | ||
100 | #define DIRFUNCTIONS_DEFINED | ||
101 | #endif | ||
102 | |||
103 | #define DIRENT dirent | ||
104 | #define DIRENT_DEFINED | ||
105 | |||
106 | #ifndef DIRFUNCTIONS_DECLARED | ||
107 | DIR * app_opendir(const char *dirname); | ||
108 | struct dirent * app_readdir(DIR *dirp); | ||
109 | int app_closedir(DIR *dirp); | ||
110 | int app_mkdir(const char *path); | ||
111 | int app_rmdir(const char *path); | ||
112 | int app_samedir(DIR *dirp1, DIR *dirp2); | ||
113 | bool app_dir_exists(const char *dirname); | ||
114 | #endif /* DIRFUNCTIONS_DECLARED */ | ||
115 | |||
116 | #endif /* _FILESYSTEM_APP__DIR_H_ */ | ||
117 | #endif /* _DIR_H_ */ | ||
diff --git a/firmware/target/hosted/filesystem-hosted.h b/firmware/target/hosted/filesystem-hosted.h new file mode 100644 index 0000000000..3d979eb19d --- /dev/null +++ b/firmware/target/hosted/filesystem-hosted.h | |||
@@ -0,0 +1,74 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILESYSTEM_HOSTED_H_ | ||
22 | #define _FILESYSTEM_HOSTED_H_ | ||
23 | |||
24 | #include "mv.h" | ||
25 | |||
26 | int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize); | ||
27 | void * os_lc_open(const char *ospath); | ||
28 | |||
29 | #endif /* _FILESYSTEM_HOSTED_H_ */ | ||
30 | |||
31 | #ifdef _FILE_H_ | ||
32 | #ifndef _FILESYSTEM_HOSTED__FILE_H_ | ||
33 | #define _FILESYSTEM_HOSTED__FILE_H_ | ||
34 | |||
35 | #ifndef OSFUNCTIONS_DECLARED | ||
36 | off_t os_filesize(int osfd); | ||
37 | int os_fsamefile(int osfd1, int osfd2); | ||
38 | int os_relate(const char *path1, const char *path2); | ||
39 | bool os_file_exists(const char *ospath); | ||
40 | |||
41 | #define __OPEN_MODE_ARG \ | ||
42 | , ({ \ | ||
43 | mode_t mode = 0; \ | ||
44 | if (oflag & O_CREAT) \ | ||
45 | { \ | ||
46 | va_list ap; \ | ||
47 | va_start(ap, oflag); \ | ||
48 | mode = va_arg(ap, unsigned int); \ | ||
49 | va_end(ap); \ | ||
50 | } \ | ||
51 | mode; }) | ||
52 | |||
53 | #define __CREAT_MODE_ARG \ | ||
54 | , mode | ||
55 | |||
56 | #endif /* OSFUNCTIONS_DECLARED */ | ||
57 | |||
58 | #endif /* _FILESYSTEM_HOSTED__FILE_H_ */ | ||
59 | #endif /* _FILE_H_ */ | ||
60 | |||
61 | #ifdef _DIR_H_ | ||
62 | #ifndef _FILESYSTEM_HOSTED__DIR_H_ | ||
63 | #define _FILESYSTEM_HOSTED__DIR_H_ | ||
64 | |||
65 | #ifndef OSFUNCTIONS_DECLARED | ||
66 | int os_opendir_and_fd(const char *osdirname, OS_DIR_T **osdirpp, | ||
67 | int *osfdp); | ||
68 | |||
69 | #define __MKDIR_MODE_ARG \ | ||
70 | , 0777 | ||
71 | #endif /* OSFUNCTIONS_DECLARED */ | ||
72 | |||
73 | #endif /* _FILESYSTEM_HOSTED__DIR_H_ */ | ||
74 | #endif /* _DIR_H_ */ | ||
diff --git a/firmware/target/hosted/filesystem-unix.c b/firmware/target/hosted/filesystem-unix.c index 8ac1d4ada9..907d6ab14e 100644 --- a/firmware/target/hosted/filesystem-unix.c +++ b/firmware/target/hosted/filesystem-unix.c | |||
@@ -18,24 +18,204 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #define RB_FILESYSTEM_OS | ||
22 | #include <sys/statfs.h> /* lowest common denominator */ | ||
23 | #include <sys/stat.h> | ||
24 | #include <string.h> | ||
25 | #include <errno.h> | ||
26 | #include "config.h" | ||
27 | #include "system.h" | ||
28 | #include "file.h" | ||
29 | #include "dir.h" | ||
30 | #include "mv.h" | ||
31 | #include "debug.h" | ||
32 | #include "pathfuncs.h" | ||
33 | #include "string-extra.h" | ||
21 | 34 | ||
22 | #include <sys/stat.h> /* stat() */ | 35 | #define SAME_FILE_INFO(sb1p, sb2p) \ |
23 | #include "mv.h" /* stat() */ | 36 | ((sb1p)->st_dev == (sb2p)->st_dev && (sb1p)->st_ino == (sb2p)->st_ino) |
24 | 37 | ||
25 | 38 | off_t os_filesize(int osfd) | |
26 | long filesize(int fd) | ||
27 | { | 39 | { |
28 | struct stat buf; | 40 | struct stat sb; |
29 | 41 | ||
30 | if (!fstat(fd, &buf)) | 42 | if (!os_fstat(osfd, &sb)) |
31 | return buf.st_size; | 43 | return sb.st_size; |
32 | else | 44 | else |
33 | return -1; | 45 | return -1; |
34 | } | 46 | } |
35 | 47 | ||
36 | /* do we really need this in the app? */ | 48 | int os_fsamefile(int osfd1, int osfd2) |
37 | void fat_size(IF_MV(int volume,) unsigned long* size, unsigned long* free) | 49 | { |
50 | struct stat sb1, sb2; | ||
51 | |||
52 | if (os_fstat(osfd1, &sb1)) | ||
53 | return -1; | ||
54 | |||
55 | if (os_fstat(osfd2, &sb2)) | ||
56 | return -1; | ||
57 | |||
58 | return SAME_FILE_INFO(&sb1, &sb2); | ||
59 | } | ||
60 | |||
61 | int os_relate(const char *ospath1, const char *ospath2) | ||
62 | { | ||
63 | DEBUGF("\"%s\" : \"%s\"\n", ospath1, ospath2); | ||
64 | |||
65 | if (!ospath2 || !*ospath2) | ||
66 | { | ||
67 | errno = ospath2 ? ENOENT : EFAULT; | ||
68 | return -1; | ||
69 | } | ||
70 | |||
71 | /* First file must stay open for duration so that its stats don't change */ | ||
72 | int fd1 = os_open(ospath1, O_RDONLY); | ||
73 | if (fd1 < 0) | ||
74 | return -2; | ||
75 | |||
76 | struct stat sb1; | ||
77 | if (os_fstat(fd1, &sb1)) | ||
78 | { | ||
79 | os_close(fd1); | ||
80 | return -3; | ||
81 | } | ||
82 | |||
83 | char path2buf[strlen(ospath2) + 1]; | ||
84 | *path2buf = 0; | ||
85 | |||
86 | ssize_t len = 0; | ||
87 | const char *p = ospath2; | ||
88 | const char *sepmo = path_is_absolute(ospath2) ? PA_SEP_HARD : PA_SEP_SOFT; | ||
89 | |||
90 | int rc = RELATE_DIFFERENT; | ||
91 | |||
92 | while (1) | ||
93 | { | ||
94 | if (sepmo != PA_SEP_HARD && | ||
95 | !(len = parse_path_component(&ospath2, &p))) | ||
96 | { | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | char compname[len + 1]; | ||
101 | strmemcpy(compname, p, len); | ||
102 | |||
103 | path_append(path2buf, sepmo, compname, sizeof (path2buf)); | ||
104 | sepmo = PA_SEP_SOFT; | ||
105 | |||
106 | int errnum = errno; /* save and restore if not actually failing */ | ||
107 | struct stat sb2; | ||
108 | |||
109 | if (!os_stat(path2buf, &sb2)) | ||
110 | { | ||
111 | if (SAME_FILE_INFO(&sb1, &sb2)) | ||
112 | { | ||
113 | rc = RELATE_SAME; | ||
114 | } | ||
115 | else if (rc == RELATE_SAME) | ||
116 | { | ||
117 | if (name_is_dot_dot(compname)) | ||
118 | rc = RELATE_DIFFERENT; | ||
119 | else if (!name_is_dot(compname)) | ||
120 | rc = RELATE_PREFIX; | ||
121 | } | ||
122 | } | ||
123 | else if (errno == ENOENT && !*GOBBLE_PATH_SEPCH(ospath2) && | ||
124 | !name_is_dot_dot(compname)) | ||
125 | { | ||
126 | if (rc == RELATE_SAME) | ||
127 | rc = RELATE_PREFIX; | ||
128 | |||
129 | errno = errnum; | ||
130 | break; | ||
131 | } | ||
132 | else | ||
133 | { | ||
134 | rc = -4; | ||
135 | break; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | if (os_close(fd1) && rc >= 0) | ||
140 | rc = -5; | ||
141 | |||
142 | return rc; | ||
143 | } | ||
144 | |||
145 | bool os_file_exists(const char *ospath) | ||
146 | { | ||
147 | int sim_fd = os_open(ospath, O_RDONLY, 0); | ||
148 | if (sim_fd < 0) | ||
149 | return false; | ||
150 | |||
151 | int errnum = errno; | ||
152 | os_close(sim_fd); | ||
153 | errno = errnum; | ||
154 | |||
155 | return true; | ||
156 | } | ||
157 | |||
158 | int os_opendirfd(const char *osdirname) | ||
159 | { | ||
160 | return os_open(osdirname, O_RDONLY); | ||
161 | } | ||
162 | |||
163 | int os_opendir_and_fd(const char *osdirname, DIR **osdirpp, int *osfdp) | ||
164 | { | ||
165 | /* another possible way is to use open() then fdopendir() */ | ||
166 | *osdirpp = NULL; | ||
167 | *osfdp = -1; | ||
168 | |||
169 | DIR *dirp = os_opendir(osdirname); | ||
170 | if (!dirp) | ||
171 | return -1; | ||
172 | |||
173 | int rc = 0; | ||
174 | int errnum = errno; | ||
175 | |||
176 | int fd = os_dirfd(dirp); | ||
177 | if (fd < 0) | ||
178 | { | ||
179 | fd = os_opendirfd(osdirname); | ||
180 | rc = 1; | ||
181 | } | ||
182 | |||
183 | if (fd < 0) | ||
184 | { | ||
185 | os_closedir(dirp); | ||
186 | return -2; | ||
187 | } | ||
188 | |||
189 | errno = errnum; | ||
190 | |||
191 | *osdirpp = dirp; | ||
192 | *osfdp = fd; | ||
193 | |||
194 | return rc; | ||
195 | } | ||
196 | |||
197 | /* do we really need this in the app? (in the sim, yes) */ | ||
198 | void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep) | ||
38 | { | 199 | { |
39 | IF_MV((void) volume); | 200 | unsigned long size = 0, free = 0; |
40 | *size = *free = 0; | 201 | char volpath[MAX_PATH]; |
202 | struct statfs fs; | ||
203 | |||
204 | if (os_volume_path(IF_MV(volume,) volpath, sizeof (volpath)) >= 0 | ||
205 | && !statfs(volpath, &fs)) | ||
206 | { | ||
207 | DEBUGF("statvfs: frsize=%d blocks=%ld bfree=%ld\n", | ||
208 | (int)fs.f_frsize, (long)fs.f_blocks, (long)fs.f_bfree); | ||
209 | if (sizep) | ||
210 | size = (fs.f_blocks / 2) * (fs.f_frsize / 512); | ||
211 | |||
212 | if (freep) | ||
213 | free = (fs.f_bfree / 2) * (fs.f_frsize / 512); | ||
214 | } | ||
215 | |||
216 | if (sizep) | ||
217 | *sizep = size; | ||
218 | |||
219 | if (freep) | ||
220 | *freep = free; | ||
41 | } | 221 | } |
diff --git a/firmware/target/hosted/filesystem-unix.h b/firmware/target/hosted/filesystem-unix.h new file mode 100644 index 0000000000..a09712d8b0 --- /dev/null +++ b/firmware/target/hosted/filesystem-unix.h | |||
@@ -0,0 +1,82 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILESYSTEM_UNIX_H_ | ||
22 | #define _FILESYSTEM_UNIX_H_ | ||
23 | |||
24 | /* Include for file.h and dir.h because mkdir and friends may be here */ | ||
25 | #include <sys/stat.h> | ||
26 | |||
27 | #define strlcpy_from_os strlcpy | ||
28 | #endif | ||
29 | |||
30 | #ifdef _FILE_H_ | ||
31 | #ifndef _FILESYSTEM_UNIX__FILE_H_ | ||
32 | #define _FILESYSTEM_UNIX__FILE_H_ | ||
33 | |||
34 | #include <unistd.h> | ||
35 | |||
36 | #define OS_STAT_T struct stat | ||
37 | |||
38 | #ifndef OSFUNCTIONS_DECLARED | ||
39 | #define os_open open | ||
40 | #define os_creat creat | ||
41 | #define os_close close | ||
42 | #define os_lseek lseek | ||
43 | #define os_stat stat | ||
44 | #define os_fstat fstat | ||
45 | #define os_fstatat fstatat | ||
46 | #define os_lstat lstat | ||
47 | #define os_fsync fsync | ||
48 | #define os_ftruncate ftruncate | ||
49 | #define os_remove remove | ||
50 | #define os_rename rename | ||
51 | #define os_readlink readlink | ||
52 | #ifndef os_read | ||
53 | #define os_read read | ||
54 | #endif | ||
55 | #ifndef os_write | ||
56 | #define os_write write | ||
57 | #endif | ||
58 | #endif /* !OSFUNCTIONS_DECLARED */ | ||
59 | |||
60 | #endif /* _FILESYSTEM_UNIX__FILE_H_ */ | ||
61 | #endif /* _FILE_H_ */ | ||
62 | |||
63 | #ifdef _DIR_H_ | ||
64 | #ifndef _FILESYSTEM_UNIX__DIR_H_ | ||
65 | #define _FILESYSTEM_UNIX__DIR_H_ | ||
66 | |||
67 | #include <dirent.h> | ||
68 | |||
69 | #define OS_DIR_T DIR | ||
70 | #define OS_DIRENT_T struct dirent | ||
71 | |||
72 | #ifndef OSFUNCTIONS_DECLARED | ||
73 | #define os_opendir opendir | ||
74 | #define os_readdir readdir | ||
75 | #define os_closedir closedir | ||
76 | #define os_mkdir mkdir | ||
77 | #define os_rmdir rmdir | ||
78 | #define os_dirfd dirfd /* NOTE: might have to wrap on some platforms */ | ||
79 | #endif /* !OSFUNCTIONS_DECLARED */ | ||
80 | |||
81 | #endif /* _FILESYSTEM_UNIX__DIR_H_ */ | ||
82 | #endif /* _DIR_H_ */ | ||
diff --git a/firmware/target/hosted/filesystem-win32.c b/firmware/target/hosted/filesystem-win32.c new file mode 100644 index 0000000000..19ef1c0aa7 --- /dev/null +++ b/firmware/target/hosted/filesystem-win32.c | |||
@@ -0,0 +1,479 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #define RB_FILESYSTEM_OS | ||
22 | #include <stdio.h> | ||
23 | #include <errno.h> | ||
24 | #include <ctype.h> | ||
25 | #include <stdlib.h> | ||
26 | #include "config.h" | ||
27 | #include "system.h" | ||
28 | #include "file.h" | ||
29 | #include "dir.h" | ||
30 | #include "debug.h" | ||
31 | #include "pathfuncs.h" | ||
32 | #include "string-extra.h" | ||
33 | |||
34 | #define SAME_FILE_INFO(lpInfo1, lpInfo2) \ | ||
35 | ((lpInfo1)->dwVolumeSerialNumber == (lpInfo2)->dwVolumeSerialNumber && \ | ||
36 | (lpInfo1)->nFileIndexHigh == (lpInfo2)->nFileIndexHigh && \ | ||
37 | (lpInfo1)->nFileIndexLow == (lpInfo2)->nFileIndexLow) | ||
38 | |||
39 | #define WIN32_LEAN_AND_MEAN | ||
40 | #include <windows.h> | ||
41 | |||
42 | static void win32_last_error_errno(void) | ||
43 | { | ||
44 | switch (GetLastError()) | ||
45 | { | ||
46 | case ERROR_FILE_NOT_FOUND: | ||
47 | case ERROR_PATH_NOT_FOUND: | ||
48 | errno = ENOENT; | ||
49 | break; | ||
50 | case ERROR_DIR_NOT_EMPTY: | ||
51 | errno = ENOTEMPTY; | ||
52 | break; | ||
53 | default: | ||
54 | errno = EIO; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | #ifdef __MINGW32__ | ||
59 | #include <wchar.h> | ||
60 | #include "rbunicode.h" | ||
61 | |||
62 | static HANDLE win32_open(const char *ospath); | ||
63 | static int win32_stat(const char *ospath, LPBY_HANDLE_FILE_INFORMATION lpInfo); | ||
64 | |||
65 | unsigned short * strcpy_utf8ucs2(unsigned short *buffer, | ||
66 | const unsigned char *utf8) | ||
67 | { | ||
68 | for (wchar_t *ucs2 = buffer; | ||
69 | ((utf8 = utf8decode(utf8, ucs2)), *ucs2); ucs2++); | ||
70 | return buffer; | ||
71 | } | ||
72 | |||
73 | #if 0 | ||
74 | unsigned char * strcpy_ucs2utf8(unsigned char *buffer, | ||
75 | const unsigned short *ucs2) | ||
76 | { | ||
77 | for (unsigned char *utf8 = buffer; | ||
78 | ((utf8 = utf8encode(*ucs2, utf8)), *ucs2); ucs2++); | ||
79 | return buffer; | ||
80 | } | ||
81 | |||
82 | size_t strlen_utf8ucs2(const unsigned char *utf8) | ||
83 | { | ||
84 | /* This won't properly count multiword ucs2 so use the alternative | ||
85 | below for now which doesn't either */ | ||
86 | size_t length = 0; | ||
87 | unsigned short ucschar[2]; | ||
88 | for (unsigned char c = *utf8; c; | ||
89 | ((utf8 = utf8decode(utf8, ucschar)), c = *utf8)) | ||
90 | length++; | ||
91 | |||
92 | return length; | ||
93 | } | ||
94 | #endif /* 0 */ | ||
95 | |||
96 | size_t strlen_utf8ucs2(const unsigned char *utf8) | ||
97 | { | ||
98 | return utf8length(utf8); | ||
99 | } | ||
100 | |||
101 | size_t strlen_ucs2utf8(const unsigned short *ucs2) | ||
102 | { | ||
103 | size_t length = 0; | ||
104 | unsigned char utf8char[4]; | ||
105 | |||
106 | for (unsigned short c = *ucs2; c; (c = *++ucs2)) | ||
107 | length += utf8encode(c, utf8char) - utf8char; | ||
108 | |||
109 | return length; | ||
110 | } | ||
111 | |||
112 | size_t strlcpy_ucs2utf8(char *buffer, const unsigned short *ucs2, | ||
113 | size_t bufsize) | ||
114 | { | ||
115 | if (!buffer) | ||
116 | bufsize = 0; | ||
117 | |||
118 | size_t length = 0; | ||
119 | unsigned char utf8char[4]; | ||
120 | |||
121 | for (unsigned short c = *ucs2; c; (c = *++ucs2)) | ||
122 | { | ||
123 | /* If the last character won't fit, this won't split it */ | ||
124 | size_t utf8size = utf8encode(c, utf8char) - utf8char; | ||
125 | if ((length += utf8size) < bufsize) | ||
126 | buffer = mempcpy(buffer, utf8char, utf8size); | ||
127 | } | ||
128 | |||
129 | /* Above won't ever copy to very end */ | ||
130 | if (bufsize) | ||
131 | *buffer = '\0'; | ||
132 | |||
133 | return length; | ||
134 | } | ||
135 | |||
136 | #define _toucs2(utf8) \ | ||
137 | ({ const char *_utf8 = (utf8); \ | ||
138 | size_t _l = strlen_utf8ucs2(_utf8); \ | ||
139 | void *_buffer = alloca((_l + 1)*2); \ | ||
140 | strcpy_utf8ucs2(_buffer, _utf8); }) | ||
141 | |||
142 | #define _toutf8(ucs2) \ | ||
143 | ({ const char *_ucs2 = (ucs2); \ | ||
144 | size_t _l = strlen_ucs2utf8(_ucs2); \ | ||
145 | void *_buffer = alloca(_l + 1); \ | ||
146 | strcpy_ucs2utf8(_buffer, _ucs2); }) | ||
147 | |||
148 | int os_open(const char *ospath, int oflag, ...) | ||
149 | { | ||
150 | return _wopen(_toucs2(ospath), oflag __OPEN_MODE_ARG); | ||
151 | } | ||
152 | |||
153 | int os_creat(const char *ospath, mode_t mode) | ||
154 | { | ||
155 | return _wcreat(_toucs2(ospath), mode); | ||
156 | } | ||
157 | |||
158 | int os_stat(const char *ospath, struct _stat *s) | ||
159 | { | ||
160 | return _wstat(_toucs2(ospath), s); | ||
161 | } | ||
162 | |||
163 | int os_remove(const char *ospath) | ||
164 | { | ||
165 | return _wremove(_toucs2(ospath)); | ||
166 | } | ||
167 | |||
168 | int os_rename(const char *osold, const char *osnew) | ||
169 | { | ||
170 | int errnum = errno; | ||
171 | |||
172 | const wchar_t *wchosold = _toucs2(osold); | ||
173 | const wchar_t *wchosnew = _toucs2(osnew); | ||
174 | |||
175 | int rc = _wrename(wchosold, wchosnew); | ||
176 | if (rc < 0 && errno == EEXIST) | ||
177 | { | ||
178 | /* That didn't work; do cheap POSIX mimic */ | ||
179 | BY_HANDLE_FILE_INFORMATION info; | ||
180 | if (win32_stat(osold, &info)) | ||
181 | return -1; | ||
182 | |||
183 | if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && | ||
184 | !RemoveDirectoryW(wchosnew)) | ||
185 | { | ||
186 | win32_last_error_errno(); | ||
187 | return -1; | ||
188 | } | ||
189 | |||
190 | if (MoveFileExW(wchosold, wchosnew, MOVEFILE_REPLACE_EXISTING | | ||
191 | MOVEFILE_WRITE_THROUGH)) | ||
192 | { | ||
193 | errno = errnum; | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | errno = EIO; | ||
198 | } | ||
199 | |||
200 | return rc; | ||
201 | } | ||
202 | |||
203 | bool os_file_exists(const char *ospath) | ||
204 | { | ||
205 | HANDLE h = win32_open(ospath); | ||
206 | if (h == INVALID_HANDLE_VALUE) | ||
207 | return false; | ||
208 | |||
209 | CloseHandle(h); | ||
210 | return true; | ||
211 | } | ||
212 | |||
213 | _WDIR * os_opendir(const char *osdirname) | ||
214 | { | ||
215 | return _wopendir(_toucs2(osdirname)); | ||
216 | } | ||
217 | |||
218 | int os_mkdir(const char *ospath, mode_t mode) | ||
219 | { | ||
220 | return _wmkdir(_toucs2(ospath)); | ||
221 | (void)mode; | ||
222 | } | ||
223 | |||
224 | int os_rmdir(const char *ospath) | ||
225 | { | ||
226 | return _wrmdir(_toucs2(ospath)); | ||
227 | } | ||
228 | |||
229 | int os_dirfd(_WDIR *osdirp) | ||
230 | { | ||
231 | #ifdef ENOTSUP | ||
232 | errno = ENOTSUP | ||
233 | #else | ||
234 | errno = ENOSYS; | ||
235 | #endif | ||
236 | return -1; | ||
237 | (void)osdirp; | ||
238 | } | ||
239 | |||
240 | int os_opendirfd(const char *osdirname) | ||
241 | { | ||
242 | HANDLE h = win32_open(osdirname); | ||
243 | if (h == INVALID_HANDLE_VALUE) | ||
244 | return -1; | ||
245 | |||
246 | BY_HANDLE_FILE_INFORMATION info; | ||
247 | if (!GetFileInformationByHandle(h, &info)) | ||
248 | errno = EIO; | ||
249 | else if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) | ||
250 | errno = ENOTDIR; | ||
251 | else | ||
252 | { | ||
253 | /* Convert OS handle to fd; the fd now owns it */ | ||
254 | int osfd = _open_osfhandle((long)h, O_RDONLY); | ||
255 | if (osfd >= 0) | ||
256 | return osfd; | ||
257 | } | ||
258 | |||
259 | CloseHandle(h); | ||
260 | return -2; | ||
261 | } | ||
262 | #endif /* __MINGW32__ */ | ||
263 | |||
264 | static size_t win32_path_strip_root(const char *ospath) | ||
265 | { | ||
266 | const char *p = ospath; | ||
267 | int c = toupper(*p); | ||
268 | |||
269 | if (c >= 'A' && c <= 'Z') | ||
270 | { | ||
271 | /* drive */ | ||
272 | if ((c = *++p) == ':') | ||
273 | return 2; | ||
274 | } | ||
275 | |||
276 | if (c == '\\' && *++p == '\\') | ||
277 | { | ||
278 | /* UNC */ | ||
279 | while ((c = *++p) && c != '/' && c != '\\'); | ||
280 | return p - ospath; | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static HANDLE win32_open(const char *ospath) | ||
287 | { | ||
288 | /* FILE_FLAG_BACKUP_SEMANTICS is required for this to succeed at opening | ||
289 | a directory */ | ||
290 | HANDLE h = CreateFileW(_toucs2(ospath), GENERIC_READ, | ||
291 | FILE_SHARE_READ | FILE_SHARE_WRITE | | ||
292 | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, | ||
293 | FILE_FLAG_BACKUP_SEMANTICS, NULL); | ||
294 | |||
295 | if (h == INVALID_HANDLE_VALUE) | ||
296 | win32_last_error_errno(); | ||
297 | |||
298 | return h; | ||
299 | } | ||
300 | |||
301 | static int win32_fstat(int osfd, HANDLE hFile, | ||
302 | LPBY_HANDLE_FILE_INFORMATION lpInfo) | ||
303 | { | ||
304 | /* The file descriptor takes precedence over the win32 file handle */ | ||
305 | if (osfd >= 0) | ||
306 | hFile = (HANDLE)_get_osfhandle(osfd); | ||
307 | |||
308 | int rc = GetFileInformationByHandle(hFile, lpInfo) ? 0 : -1; | ||
309 | if (rc < 0) | ||
310 | win32_last_error_errno(); | ||
311 | |||
312 | return rc; | ||
313 | } | ||
314 | |||
315 | static int win32_stat(const char *ospath, LPBY_HANDLE_FILE_INFORMATION lpInfo) | ||
316 | { | ||
317 | HANDLE h = win32_open(ospath); | ||
318 | if (h == INVALID_HANDLE_VALUE) | ||
319 | return -1; | ||
320 | |||
321 | int rc = win32_fstat(-1, h, lpInfo); | ||
322 | |||
323 | CloseHandle(h); | ||
324 | |||
325 | return rc; | ||
326 | } | ||
327 | |||
328 | int os_opendir_and_fd(const char *osdirname, _WDIR **osdirpp, | ||
329 | int *osfdp) | ||
330 | { | ||
331 | /* another possible way is to use open() then fdopendir() */ | ||
332 | *osdirpp = NULL; | ||
333 | *osfdp = -1; | ||
334 | |||
335 | _WDIR *dirp = os_opendir(osdirname); | ||
336 | if (!dirp) | ||
337 | return -1; | ||
338 | |||
339 | int rc = 0; | ||
340 | int errnum = errno; | ||
341 | |||
342 | int fd = os_dirfd(dirp); | ||
343 | if (fd < 0) | ||
344 | { | ||
345 | fd = os_opendirfd(osdirname); | ||
346 | rc = 1; | ||
347 | } | ||
348 | |||
349 | if (fd < 0) | ||
350 | { | ||
351 | os_closedir(dirp); | ||
352 | return -2; | ||
353 | } | ||
354 | |||
355 | errno = errnum; | ||
356 | |||
357 | *osdirpp = dirp; | ||
358 | *osfdp = fd; | ||
359 | |||
360 | return rc; | ||
361 | } | ||
362 | |||
363 | int os_fsamefile(int osfd1, int osfd2) | ||
364 | { | ||
365 | BY_HANDLE_FILE_INFORMATION info1, info2; | ||
366 | |||
367 | if (!win32_fstat(osfd1, INVALID_HANDLE_VALUE, &info1) || | ||
368 | !win32_fstat(osfd2, INVALID_HANDLE_VALUE, &info2)) | ||
369 | return -1; | ||
370 | |||
371 | return SAME_FILE_INFO(&info1, &info2) ? 1 : 0; | ||
372 | } | ||
373 | |||
374 | int os_relate(const char *ospath1, const char *ospath2) | ||
375 | { | ||
376 | DEBUGF("\"%s\" : \"%s\"\n", ospath1, ospath2); | ||
377 | |||
378 | if (!ospath2 || !*ospath2) | ||
379 | { | ||
380 | errno = ospath2 ? ENOENT : EFAULT; | ||
381 | return -1; | ||
382 | } | ||
383 | |||
384 | /* First file must stay open for duration so that its stats don't change */ | ||
385 | HANDLE h1 = win32_open(ospath1); | ||
386 | if (h1 == INVALID_HANDLE_VALUE) | ||
387 | return -2; | ||
388 | |||
389 | BY_HANDLE_FILE_INFORMATION info1; | ||
390 | if (win32_fstat(-1, h1, &info1)) | ||
391 | { | ||
392 | CloseHandle(h1); | ||
393 | return -3; | ||
394 | } | ||
395 | |||
396 | char path2buf[strlen(ospath2) + 1]; | ||
397 | *path2buf = 0; | ||
398 | |||
399 | ssize_t len = 0; | ||
400 | const char *p = ospath2; | ||
401 | size_t rootlen = win32_path_strip_root(ospath2); | ||
402 | const char *sepmo = PA_SEP_SOFT; | ||
403 | |||
404 | if (rootlen) | ||
405 | { | ||
406 | strmemcpy(path2buf, ospath2, rootlen); | ||
407 | ospath2 += rootlen; | ||
408 | sepmo = PA_SEP_HARD; | ||
409 | } | ||
410 | |||
411 | int rc = RELATE_DIFFERENT; | ||
412 | |||
413 | while (1) | ||
414 | { | ||
415 | if (sepmo != PA_SEP_HARD && | ||
416 | !(len = parse_path_component(&ospath2, &p))) | ||
417 | { | ||
418 | break; | ||
419 | } | ||
420 | |||
421 | char compname[len + 1]; | ||
422 | strmemcpy(compname, p, len); | ||
423 | |||
424 | path_append(path2buf, sepmo, compname, sizeof (path2buf)); | ||
425 | sepmo = PA_SEP_SOFT; | ||
426 | |||
427 | int errnum = errno; /* save and restore if not actually failing */ | ||
428 | BY_HANDLE_FILE_INFORMATION info2; | ||
429 | |||
430 | if (!win32_stat(path2buf, &info2)) | ||
431 | { | ||
432 | if (SAME_FILE_INFO(&info1, &info2)) | ||
433 | { | ||
434 | rc = RELATE_SAME; | ||
435 | } | ||
436 | else if (rc == RELATE_SAME) | ||
437 | { | ||
438 | if (name_is_dot_dot(compname)) | ||
439 | rc = RELATE_DIFFERENT; | ||
440 | else if (!name_is_dot(compname)) | ||
441 | rc = RELATE_PREFIX; | ||
442 | } | ||
443 | } | ||
444 | else if (errno == ENOENT && !*GOBBLE_PATH_SEPCH(ospath2) && | ||
445 | !name_is_dot_dot(compname)) | ||
446 | { | ||
447 | if (rc == RELATE_SAME) | ||
448 | rc = RELATE_PREFIX; | ||
449 | |||
450 | errno = errnum; | ||
451 | break; | ||
452 | } | ||
453 | else | ||
454 | { | ||
455 | rc = -4; | ||
456 | break; | ||
457 | } | ||
458 | } | ||
459 | |||
460 | CloseHandle(h1); | ||
461 | |||
462 | return rc; | ||
463 | } | ||
464 | |||
465 | void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep) | ||
466 | { | ||
467 | ULARGE_INTEGER free = { .QuadPart = 0 }, | ||
468 | size = { .QuadPart = 0 }; | ||
469 | |||
470 | char volpath[MAX_PATH]; | ||
471 | if (os_volume_path(IF_MV(volume, ) volpath, sizeof (volpath)) >= 0) | ||
472 | GetDiskFreeSpaceExW(_toucs2(volpath), &free, &size, NULL); | ||
473 | |||
474 | if (sizep) | ||
475 | *sizep = size.QuadPart / 1024; | ||
476 | |||
477 | if (freep) | ||
478 | *freep = free.QuadPart / 1024; | ||
479 | } | ||
diff --git a/firmware/target/hosted/filesystem-win32.h b/firmware/target/hosted/filesystem-win32.h new file mode 100644 index 0000000000..1d8f2749f9 --- /dev/null +++ b/firmware/target/hosted/filesystem-win32.h | |||
@@ -0,0 +1,111 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILESYSTEM_WIN32_H_ | ||
22 | #define _FILESYSTEM_WIN32_H_ | ||
23 | |||
24 | #ifndef OSFUNCTIONS_DECLARED | ||
25 | |||
26 | #ifdef __MINGW32__ | ||
27 | /* filesystem-win32.c contains some string functions that could be useful | ||
28 | * elsewhere; just move them away to unicode.c or something if they prove | ||
29 | * so. */ | ||
30 | size_t strlcpy_ucs2utf8(char *buffer, const unsigned short *ucs, | ||
31 | size_t bufsize); | ||
32 | |||
33 | #define strlcpy_from_os strlcpy_ucs2utf8 | ||
34 | #endif /* __MINGW32__ */ | ||
35 | |||
36 | #endif /* !OSFUNCTIONS_DECLARED */ | ||
37 | |||
38 | #endif /* _FILESYSTEM_WIN32_H_ */ | ||
39 | |||
40 | #ifdef __MINGW32__ | ||
41 | |||
42 | #ifdef _FILE_H_ | ||
43 | #ifndef _FILESYSTEM_WIN32__FILE_H_ | ||
44 | #define _FILESYSTEM_WIN32__FILE_H_ | ||
45 | |||
46 | #include <unistd.h> | ||
47 | #include <sys/stat.h> | ||
48 | |||
49 | #define OS_STAT_T struct _stat | ||
50 | |||
51 | #ifndef OSFUNCTIONS_DECLARED | ||
52 | /* Wrap for off_t <=> long conversions */ | ||
53 | static inline off_t os_filesize_(int osfd) | ||
54 | { return _filelength(osfd); } | ||
55 | static inline int os_ftruncate_(int osfd, off_t length) | ||
56 | { return _chsize(osfd, length); } | ||
57 | |||
58 | #define os_filesize os_filesize_ | ||
59 | #define os_ftruncate os_ftruncate_ | ||
60 | #define os_fsync _commit | ||
61 | #define os_fstat _fstat | ||
62 | #define os_close close | ||
63 | #define os_lseek lseek | ||
64 | #ifndef os_read | ||
65 | #define os_read read | ||
66 | #endif | ||
67 | #ifndef os_write | ||
68 | #define os_write write | ||
69 | #endif | ||
70 | |||
71 | /* These need string type conversion from utf8 to ucs2; that's done inside */ | ||
72 | int os_open(const char *ospath, int oflag, ...); | ||
73 | int os_creat(const char *ospath, mode_t mode); | ||
74 | int os_stat(const char *ospath, struct _stat *s); | ||
75 | int os_remove(const char *ospath); | ||
76 | int os_rename(const char *osold, const char *osnew); | ||
77 | |||
78 | #endif /* !OSFUNCTIONS_DECLARED */ | ||
79 | |||
80 | #endif /* _FILESYSTEM_WIN32__FILE_H_ */ | ||
81 | #endif /* _FILE_H_ */ | ||
82 | |||
83 | #ifdef _DIR_H_ | ||
84 | #ifndef _FILESYSTEM_WIN32__DIR_H_ | ||
85 | #define _FILESYSTEM_WIN32__DIR_H_ | ||
86 | |||
87 | #include <dirent.h> | ||
88 | |||
89 | #define OS_DIRENT_CONVERT /* needs string conversion */ | ||
90 | #define OS_DIR_T _WDIR | ||
91 | #define OS_DIRENT_T struct _wdirent | ||
92 | |||
93 | #ifndef OSFUNCTIONS_DECLARED | ||
94 | |||
95 | _WDIR * os_opendir(const char *osdirname); | ||
96 | int os_opendirfd(const char *osdirname); | ||
97 | #define os_readdir _wreaddir | ||
98 | #define os_closedir _wclosedir | ||
99 | int os_mkdir(const char *ospath, mode_t mode); | ||
100 | int os_rmdir(const char *ospath); | ||
101 | |||
102 | #endif /* OSFUNCTIONS_DECLARED */ | ||
103 | |||
104 | #endif /* _FILESYSTEM_WIN32__DIR_H_ */ | ||
105 | #endif /* _DIR_H_ */ | ||
106 | |||
107 | #else /* !__MINGW32__ */ | ||
108 | |||
109 | #include "filesystem-unix.h" | ||
110 | |||
111 | #endif /* __MINGW32__ */ | ||
diff --git a/firmware/target/hosted/lc-unix.c b/firmware/target/hosted/lc-unix.c index 6e5f15ec99..810dc9f92c 100644 --- a/firmware/target/hosted/lc-unix.c +++ b/firmware/target/hosted/lc-unix.c | |||
@@ -50,14 +50,3 @@ void lc_close(void *handle) | |||
50 | { | 50 | { |
51 | dlclose(handle); | 51 | dlclose(handle); |
52 | } | 52 | } |
53 | |||
54 | void *lc_open_from_mem(void *addr, size_t blob_size) | ||
55 | { | ||
56 | (void)addr; | ||
57 | (void)blob_size; | ||
58 | /* we don't support loading code from memory on application builds, | ||
59 | * it doesn't make sense (since it means writing the blob to disk again and | ||
60 | * then falling back to load from disk) and requires the ability to write | ||
61 | * to an executable directory */ | ||
62 | return NULL; | ||
63 | } | ||
diff --git a/firmware/export/filefuncs.h b/firmware/target/hosted/sdl/app/load_code-sdl-app.c index 92e97f65b0..686944343f 100644 --- a/firmware/export/filefuncs.h +++ b/firmware/target/hosted/sdl/app/load_code-sdl-app.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2009 by Maurus Cuelenaere | 10 | * Copyright (C) 2010 Thomas Martitz |
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or | 12 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 13 | * modify it under the terms of the GNU General Public License |
@@ -18,22 +18,19 @@ | |||
18 | * KIND, either express or implied. | 18 | * KIND, either express or implied. |
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | #define RB_FILESYSTEM_OS | |
22 | #ifndef __INCLUDE_FILEFUNCS_H_ | 22 | #include "system.h" |
23 | #define __INCLUDE_FILEFUNCS_H_ | ||
24 | |||
25 | #include <stdbool.h> | ||
26 | #include "config.h" | ||
27 | #include "file.h" | 23 | #include "file.h" |
28 | #include "dir.h" | 24 | #include "load_code.h" |
29 | |||
30 | #ifdef HAVE_MULTIVOLUME | ||
31 | int strip_volume(const char* name, char* namecopy); | ||
32 | #endif | ||
33 | |||
34 | bool file_exists(const char *file); | ||
35 | bool dir_exists(const char *path); | ||
36 | 25 | ||
37 | extern struct dirinfo dir_get_info(DIR* parent, struct dirent *entry); | 26 | void *lc_open(const char *filename, unsigned char *buf, size_t buf_size) |
27 | { | ||
28 | char realpath[MAX_PATH]; | ||
29 | const char *fpath = handle_special_dirs(filename, 0, realpath, | ||
30 | sizeof (realpath)); | ||
31 | if (!fpath) | ||
32 | return NULL; | ||
38 | 33 | ||
39 | #endif /* __INCLUDE_FILEFUNCS_H_ */ | 34 | return os_lc_open(fpath); |
35 | (void)buf; (void)buf_size; | ||
36 | } | ||
diff --git a/firmware/target/hosted/sdl/filesystem-sdl.c b/firmware/target/hosted/sdl/filesystem-sdl.c new file mode 100644 index 0000000000..5a8e2c417a --- /dev/null +++ b/firmware/target/hosted/sdl/filesystem-sdl.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #define RB_FILESYSTEM_OS | ||
22 | #include "config.h" | ||
23 | #include "system.h" | ||
24 | #include "thread-sdl.h" | ||
25 | #include "mutex.h" | ||
26 | #include "file.h" | ||
27 | |||
28 | #ifdef HAVE_SDL_THREADS | ||
29 | #define YIELD_THRESHOLD 512 | ||
30 | static bool initialized = false; | ||
31 | static struct mutex readwrite_mtx; | ||
32 | |||
33 | /* Allow other threads to run while performing I/O */ | ||
34 | ssize_t os_sdl_readwrite(int osfd, void *buf, size_t nbyte, bool dowrite) | ||
35 | { | ||
36 | if (!initialized) | ||
37 | { | ||
38 | mutex_init(&readwrite_mtx); | ||
39 | initialized = true; | ||
40 | } | ||
41 | |||
42 | mutex_lock(&readwrite_mtx); | ||
43 | |||
44 | void *mythread = nbyte > YIELD_THRESHOLD ? sim_thread_unlock() : NULL; | ||
45 | |||
46 | ssize_t rc = dowrite ? write(osfd, buf, nbyte) : read(osfd, buf, nbyte); | ||
47 | |||
48 | if (mythread) | ||
49 | sim_thread_lock(mythread); | ||
50 | |||
51 | mutex_unlock(&readwrite_mtx); | ||
52 | return rc; | ||
53 | } | ||
54 | |||
55 | #endif /* HAVE_SDL_THREADS */ | ||
diff --git a/firmware/target/hosted/sdl/filesystem-sdl.h b/firmware/target/hosted/sdl/filesystem-sdl.h new file mode 100644 index 0000000000..934b43b34b --- /dev/null +++ b/firmware/target/hosted/sdl/filesystem-sdl.h | |||
@@ -0,0 +1,37 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILESYSTEM_SDL_H_ | ||
22 | #define _FILESYSTEM_SDL_H_ | ||
23 | |||
24 | #ifdef HAVE_SDL_THREADS | ||
25 | #undef os_read | ||
26 | #undef os_write | ||
27 | |||
28 | ssize_t os_sdl_readwrite(int osfd, void *buf, size_t nbyte, bool dowrite); | ||
29 | |||
30 | #define os_read(osfd, buf, nbyte) \ | ||
31 | os_sdl_readwrite((osfd), (buf), (nbyte), false) | ||
32 | #define os_write(osfd, buf, nbyte) \ | ||
33 | os_sdl_readwrite((osfd), (void *)(buf), (nbyte), true) | ||
34 | |||
35 | #endif /* HAVE_SDL_THREADS */ | ||
36 | |||
37 | #endif /* _FILESYSTEM_SDL_H_ */ | ||
diff --git a/firmware/target/hosted/sdl/load_code-sdl.c b/firmware/target/hosted/sdl/load_code-sdl.c new file mode 100644 index 0000000000..ee29853ab5 --- /dev/null +++ b/firmware/target/hosted/sdl/load_code-sdl.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #define RB_FILESYSTEM_OS | ||
22 | #include <SDL_loadso.h> | ||
23 | #include "system.h" | ||
24 | #include "load_code.h" | ||
25 | #include "filesystem-sdl.h" | ||
26 | #include "debug.h" | ||
27 | |||
28 | void * os_lc_open(const char *ospath) | ||
29 | { | ||
30 | void *handle = SDL_LoadObject(ospath); | ||
31 | if (handle == NULL) | ||
32 | { | ||
33 | DEBUGF("%s(\"%s\") failed\n", __func__, ospath); | ||
34 | DEBUGF(" SDL error '%s'\n", SDL_GetError()); | ||
35 | } | ||
36 | |||
37 | return handle; | ||
38 | } | ||
39 | |||
40 | void * lc_get_header(void *handle) | ||
41 | { | ||
42 | char *ret = SDL_LoadFunction(handle, "__header"); | ||
43 | if (ret == NULL) | ||
44 | ret = SDL_LoadFunction(handle, "___header"); | ||
45 | |||
46 | return ret; | ||
47 | } | ||
48 | |||
49 | void lc_close(void *handle) | ||
50 | { | ||
51 | SDL_UnloadObject(handle); | ||
52 | } | ||
diff --git a/firmware/target/hosted/sdl/system-sdl.c b/firmware/target/hosted/sdl/system-sdl.c index fdf79d9333..aa322ddf3a 100644 --- a/firmware/target/hosted/sdl/system-sdl.c +++ b/firmware/target/hosted/sdl/system-sdl.c | |||
@@ -51,6 +51,8 @@ | |||
51 | 51 | ||
52 | #endif | 52 | #endif |
53 | 53 | ||
54 | #define SIMULATOR_DEFAULT_ROOT "simdisk" | ||
55 | |||
54 | SDL_Surface *gui_surface; | 56 | SDL_Surface *gui_surface; |
55 | 57 | ||
56 | bool background = true; /* use backgrounds by default */ | 58 | bool background = true; /* use backgrounds by default */ |
@@ -63,7 +65,7 @@ bool debug_buttons = false; | |||
63 | bool lcd_display_redraw = true; /* Used for player simulator */ | 65 | bool lcd_display_redraw = true; /* Used for player simulator */ |
64 | char having_new_lcd = true; /* Used for player simulator */ | 66 | char having_new_lcd = true; /* Used for player simulator */ |
65 | bool sim_alarm_wakeup = false; | 67 | bool sim_alarm_wakeup = false; |
66 | const char *sim_root_dir = NULL; | 68 | const char *sim_root_dir = SIMULATOR_DEFAULT_ROOT; |
67 | 69 | ||
68 | static SDL_Thread *evt_thread = NULL; | 70 | static SDL_Thread *evt_thread = NULL; |
69 | 71 | ||
diff --git a/firmware/target/hosted/sdl/system-sim.h b/firmware/target/hosted/sdl/system-sim.h new file mode 100644 index 0000000000..16c0cdde52 --- /dev/null +++ b/firmware/target/hosted/sdl/system-sim.h | |||
@@ -0,0 +1,32 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _SYSTEM_SIM_H_ | ||
22 | #define _SYSTEM_SIM_H_ | ||
23 | |||
24 | #ifdef WIN32 | ||
25 | #include <time.h> | ||
26 | struct tm * localtime_r(const time_t *restrict timer, | ||
27 | struct tm *restrict result); | ||
28 | struct tm * gmtime_r(const time_t *restrict timer, | ||
29 | struct tm *restrict result); | ||
30 | #endif /* WIN32 */ | ||
31 | |||
32 | #endif /* _SYSTEM_SIM_H_ */ | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c b/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c index 9c0d1982ad..0e74444cf3 100644 --- a/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c | |||
@@ -26,7 +26,6 @@ | |||
26 | #include "ata_idle_notify.h" | 26 | #include "ata_idle_notify.h" |
27 | #include "ata-sd-target.h" | 27 | #include "ata-sd-target.h" |
28 | #include "disk.h" | 28 | #include "disk.h" |
29 | #include "fat.h" | ||
30 | #include "led.h" | 29 | #include "led.h" |
31 | #include "sdmmc.h" | 30 | #include "sdmmc.h" |
32 | #include "logf.h" | 31 | #include "logf.h" |
@@ -1467,34 +1466,28 @@ static void sd_thread(void) | |||
1467 | { | 1466 | { |
1468 | #ifdef HAVE_HOTSWAP | 1467 | #ifdef HAVE_HOTSWAP |
1469 | case SYS_HOTSWAP_INSERTED: | 1468 | case SYS_HOTSWAP_INSERTED: |
1470 | case SYS_HOTSWAP_EXTRACTED: | 1469 | case SYS_HOTSWAP_EXTRACTED:; |
1471 | fat_lock(); /* lock-out FAT activity first - | 1470 | int success = 1; |
1472 | prevent deadlocking via disk_mount that | ||
1473 | would cause a reverse-order attempt with | ||
1474 | another thread */ | ||
1475 | mutex_lock(&sd_mtx); /* lock-out card activity - direct calls | ||
1476 | into driver that bypass the fat cache */ | ||
1477 | 1471 | ||
1478 | /* We now have exclusive control of fat cache and ata */ | 1472 | disk_unmount(sd_drive_nr); /* release "by force" */ |
1479 | 1473 | ||
1480 | disk_unmount(sd_drive_nr); /* release "by force", ensure file | 1474 | mutex_lock(&sd_mtx); /* lock-out card activity */ |
1481 | descriptors aren't leaked and any busy | ||
1482 | ones are invalid if mounting */ | ||
1483 | 1475 | ||
1484 | /* Force card init for new card, re-init for re-inserted one or | 1476 | /* Force card init for new card, re-init for re-inserted one or |
1485 | * clear if the last attempt to init failed with an error. */ | 1477 | * clear if the last attempt to init failed with an error. */ |
1486 | card.initialized = 0; | 1478 | card.initialized = 0; |
1487 | 1479 | ||
1480 | mutex_unlock(&sd_mtx); | ||
1481 | |||
1488 | if(ev.id == SYS_HOTSWAP_INSERTED) | 1482 | if(ev.id == SYS_HOTSWAP_INSERTED) |
1489 | disk_mount(sd_drive_nr); | 1483 | success = disk_mount(sd_drive_nr); /* 0 if fail */ |
1490 | 1484 | ||
1491 | queue_broadcast(SYS_FS_CHANGED, 0); | 1485 | if(success) |
1486 | queue_broadcast(SYS_FS_CHANGED, 0); | ||
1492 | 1487 | ||
1493 | /* Access is now safe */ | ||
1494 | mutex_unlock(&sd_mtx); | ||
1495 | fat_unlock(); | ||
1496 | break; | 1488 | break; |
1497 | #endif | 1489 | #endif /* HAVE_HOTSWAP */ |
1490 | |||
1498 | case SYS_TIMEOUT: | 1491 | case SYS_TIMEOUT: |
1499 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | 1492 | if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) |
1500 | idle_notified = false; | 1493 | idle_notified = false; |
diff --git a/firmware/usbstack/usb_storage.c b/firmware/usbstack/usb_storage.c index 59ada3bdd8..48e9442420 100644 --- a/firmware/usbstack/usb_storage.c +++ b/firmware/usbstack/usb_storage.c | |||
@@ -355,10 +355,7 @@ static bool check_disk_present(IF_MD_NONVOID(int volume)) | |||
355 | #ifdef USB_USE_RAMDISK | 355 | #ifdef USB_USE_RAMDISK |
356 | return true; | 356 | return true; |
357 | #else | 357 | #else |
358 | unsigned char* sector = fat_get_sector_buffer(); | 358 | return disk_present(IF_MD(volume)); |
359 | bool success = storage_read_sectors(IF_MD(volume,)0,1,sector) == 0; | ||
360 | fat_release_sector_buffer(); | ||
361 | return success; | ||
362 | #endif | 359 | #endif |
363 | } | 360 | } |
364 | 361 | ||
diff --git a/tools/checkwps/SOURCES b/tools/checkwps/SOURCES index 828b7965ea..425e8de7b9 100644 --- a/tools/checkwps/SOURCES +++ b/tools/checkwps/SOURCES | |||
@@ -1,13 +1,21 @@ | |||
1 | #undef unix /* messes up filesystem-unix.c below */ | ||
1 | ../../apps/gui/skin_engine/skin_parser.c | 2 | ../../apps/gui/skin_engine/skin_parser.c |
2 | ../../apps/gui/skin_engine/skin_backdrops.c | 3 | ../../apps/gui/skin_engine/skin_backdrops.c |
3 | ../../apps/gui/viewport.c | 4 | ../../apps/gui/viewport.c |
4 | ../../apps/misc.c | 5 | ../../apps/misc.c |
5 | ../../firmware/common/strlcpy.c | 6 | ../../firmware/common/strlcpy.c |
6 | checkwps.c | 7 | ../../firmware/common/pathfuncs.c |
7 | 8 | ../../firmware/asm/mempcpy.c | |
9 | ../../firmware/target/hosted/filesystem-unix.c | ||
8 | #ifdef APPLICATION | 10 | #ifdef APPLICATION |
9 | ../../firmware/common/rbpaths.c | 11 | ../../firmware/target/hosted/filesystem-app.c |
12 | #else | ||
13 | ../../uisimulator/common/filesystem-sim.c | ||
10 | #endif | 14 | #endif |
15 | #ifdef DEBUG | ||
16 | ../../firmware/debug.c | ||
17 | #endif | ||
18 | checkwps.c | ||
11 | 19 | ||
12 | #ifdef HAVE_LCD_BITMAP | 20 | #ifdef HAVE_LCD_BITMAP |
13 | ../../apps/recorder/bmp.c | 21 | ../../apps/recorder/bmp.c |
diff --git a/tools/checkwps/checkwps.c b/tools/checkwps/checkwps.c index 10e505f006..c2cadc7444 100644 --- a/tools/checkwps/checkwps.c +++ b/tools/checkwps/checkwps.c | |||
@@ -39,11 +39,14 @@ bool debug_wps = true; | |||
39 | int wps_verbose_level = 0; | 39 | int wps_verbose_level = 0; |
40 | char *skin_buffer; | 40 | char *skin_buffer; |
41 | 41 | ||
42 | int errno; | 42 | const char *sim_root_dir = "."; |
43 | |||
44 | const struct settings_list *settings; | 43 | const struct settings_list *settings; |
45 | const int nb_settings = 0; | 44 | const int nb_settings = 0; |
46 | 45 | ||
46 | #ifdef SIMULATOR | ||
47 | #error beep beep | ||
48 | #endif | ||
49 | |||
47 | /* static endianness conversion */ | 50 | /* static endianness conversion */ |
48 | #define SWAP_16(x) ((typeof(x))(unsigned short)(((unsigned short)(x) >> 8) | \ | 51 | #define SWAP_16(x) ((typeof(x))(unsigned short)(((unsigned short)(x) >> 8) | \ |
49 | ((unsigned short)(x) << 8))) | 52 | ((unsigned short)(x) << 8))) |
diff --git a/tools/checkwps/file.h b/tools/checkwps/file.h deleted file mode 100644 index a14d7f5b91..0000000000 --- a/tools/checkwps/file.h +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | #ifndef MAX_PATH | ||
2 | #define MAX_PATH 260 | ||
3 | #endif | ||
4 | |||
5 | /* Wrapper - required for O_RDONLY */ | ||
6 | |||
7 | #include <fcntl.h> | ||
8 | |||
9 | extern ssize_t read(int fd, void *buf, size_t count); | ||
10 | extern ssize_t write(int fd, const void *buf, size_t count); | ||
11 | extern off_t lseek(int fildes, off_t offset, int whence); | ||
12 | extern int close(int fd); | ||
13 | |||
14 | /* strlcpy doesn't belong here (it's in string.h in the rockbox sources), | ||
15 | * but this avoids complicated magic to override the system string.h */ | ||
16 | size_t strlcpy(char *dst, const char *src, size_t siz); | ||
diff --git a/tools/database/SOURCES b/tools/database/SOURCES index 5c9b971583..71593bba11 100644 --- a/tools/database/SOURCES +++ b/tools/database/SOURCES | |||
@@ -1,15 +1,21 @@ | |||
1 | #undef unix /* messes up filesystem-unix.c below */ | ||
1 | database.c | 2 | database.c |
2 | ../../apps/misc.c | 3 | ../../apps/misc.c |
3 | ../../apps/tagcache.c | 4 | ../../apps/tagcache.c |
4 | ../../firmware/common/crc32.c | 5 | ../../firmware/common/crc32.c |
5 | ../../firmware/common/filefuncs.c | 6 | ../../firmware/common/pathfuncs.c |
6 | ../../firmware/common/strlcpy.c | 7 | ../../firmware/common/strlcpy.c |
7 | ../../firmware/common/strcasestr.c | 8 | ../../firmware/common/strcasestr.c |
8 | ../../firmware/common/structec.c | 9 | ../../firmware/common/structec.c |
9 | ../../firmware/common/unicode.c | 10 | ../../firmware/common/unicode.c |
10 | ../../firmware/target/hosted/debug-hosted.c | 11 | ../../firmware/target/hosted/debug-hosted.c |
11 | ../../firmware/logf.c | 12 | ../../firmware/logf.c |
12 | ../../uisimulator/common/io.c | 13 | ../../firmware/target/hosted/filesystem-unix.c |
14 | #ifdef APPLICATION | ||
15 | ../../firmware/target/hosted/filesystem-app.c | ||
16 | #else /* !APPLICATION */ | ||
17 | ../../uisimulator/common/filesystem-sim.c | ||
18 | #endif /* APPLICATION */ | ||
13 | #if CONFIG_CODEC != SWCODEC | 19 | #if CONFIG_CODEC != SWCODEC |
14 | ../../lib/rbcodec/metadata/id3tags.c | 20 | ../../lib/rbcodec/metadata/id3tags.c |
15 | ../../lib/rbcodec/metadata/metadata.c | 21 | ../../lib/rbcodec/metadata/metadata.c |
diff --git a/tools/root.make b/tools/root.make index 4d58e26e8e..4e0ca7c4f0 100644 --- a/tools/root.make +++ b/tools/root.make | |||
@@ -356,22 +356,22 @@ endif | |||
356 | 356 | ||
357 | ifeq (,$(findstring android, $(APP_TYPE))) | 357 | ifeq (,$(findstring android, $(APP_TYPE))) |
358 | 358 | ||
359 | simext: | 359 | simext1: |
360 | $(SILENT)mkdir -p $@ | 360 | $(SILENT)mkdir -p $@ |
361 | 361 | ||
362 | bininstall: $(BUILDDIR)/$(BINARY) | 362 | bininstall: $(BUILDDIR)/$(BINARY) |
363 | @echo "Installing your rockbox binary in your '$(RBPREFIX)' dir" | 363 | @echo "Installing your rockbox binary in your '$(RBPREFIX)' dir" |
364 | $(SILENT)cp $(BINARY) "$(RBPREFIX)/.rockbox/" | 364 | $(SILENT)cp $(BINARY) "$(RBPREFIX)/.rockbox/" |
365 | 365 | ||
366 | install: simext | 366 | install: simext1 |
367 | @echo "Installing your build in your '$(RBPREFIX)' dir" | 367 | @echo "Installing your build in your '$(RBPREFIX)' dir" |
368 | $(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) --app=$(APPLICATION) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 0 $(TARGET) $(BINARY) | 368 | $(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) --app=$(APPLICATION) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 0 $(TARGET) $(BINARY) |
369 | 369 | ||
370 | fullinstall: simext | 370 | fullinstall: simext1 |
371 | @echo "Installing a full setup in your '$(RBPREFIX)' dir" | 371 | @echo "Installing a full setup in your '$(RBPREFIX)' dir" |
372 | $(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) --app=$(APPLICATION) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 2 $(TARGET) $(BINARY) | 372 | $(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) --app=$(APPLICATION) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 2 $(TARGET) $(BINARY) |
373 | 373 | ||
374 | symlinkinstall: simext | 374 | symlinkinstall: simext1 |
375 | @echo "Installing a full setup with links in your '$(RBPREFIX)' dir" | 375 | @echo "Installing a full setup with links in your '$(RBPREFIX)' dir" |
376 | $(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) --app=$(APPLICATION) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 2 $(TARGET) $(BINARY) -l | 376 | $(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) --app=$(APPLICATION) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 2 $(TARGET) $(BINARY) -l |
377 | endif | 377 | endif |
diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES index 939f1638c3..9833753236 100644 --- a/uisimulator/common/SOURCES +++ b/uisimulator/common/SOURCES | |||
@@ -1,4 +1,5 @@ | |||
1 | #ifdef SIMULATOR | 1 | #ifdef SIMULATOR |
2 | |||
2 | lcd-common.c | 3 | lcd-common.c |
3 | #ifdef HAVE_LCD_CHARCELLS | 4 | #ifdef HAVE_LCD_CHARCELLS |
4 | font-player.c | 5 | font-player.c |
@@ -8,8 +9,20 @@ sim_icons.c | |||
8 | sim_tasks.c | 9 | sim_tasks.c |
9 | fmradio.c | 10 | fmradio.c |
10 | backlight-sim.c | 11 | backlight-sim.c |
11 | stubs.c | ||
12 | powermgmt-sim.c | 12 | powermgmt-sim.c |
13 | filesystem-sim.c | ||
14 | |||
15 | #ifdef WIN32 | ||
16 | time-win32.c | ||
17 | #endif | ||
18 | |||
19 | #ifndef __PCTOOL__ | ||
20 | load_code-sim.c | ||
13 | #endif | 21 | #endif |
14 | 22 | ||
15 | io.c | 23 | stubs.c |
24 | |||
25 | #else | ||
26 | dummylib.c /* for now, so the lib actually builds */ | ||
27 | #endif /* SIMULATOR */ | ||
28 | |||
diff --git a/uisimulator/common/dummylib.c b/uisimulator/common/dummylib.c new file mode 100644 index 0000000000..9747edf3a1 --- /dev/null +++ b/uisimulator/common/dummylib.c | |||
@@ -0,0 +1 @@ | |||
/* this exists to get libuisimulator to make so that the sdl app may link */ | |||
diff --git a/uisimulator/common/filesystem-sim.c b/uisimulator/common/filesystem-sim.c new file mode 100644 index 0000000000..766beb3fda --- /dev/null +++ b/uisimulator/common/filesystem-sim.c | |||
@@ -0,0 +1,833 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Daniel Stenberg | ||
11 | * Copyright (C) 2014 Michael Sevakis | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #define RB_FILESYSTEM_OS | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <time.h> | ||
27 | #include <errno.h> | ||
28 | #include <limits.h> | ||
29 | #include "config.h" | ||
30 | #include "system.h" | ||
31 | #include "file.h" | ||
32 | #include "dir.h" | ||
33 | #include "file_internal.h" | ||
34 | #include "pathfuncs.h" | ||
35 | #include "string-extra.h" | ||
36 | #include "debug.h" | ||
37 | |||
38 | #ifndef os_fstatat | ||
39 | #define USE_OSDIRNAME /* we need to remember the open directory path */ | ||
40 | #endif | ||
41 | |||
42 | extern const char *sim_root_dir; | ||
43 | |||
44 | /* Windows (and potentially other OSes) distinguish binary and text files. | ||
45 | * Define a dummy for the others. */ | ||
46 | #ifndef O_BINARY | ||
47 | #define O_BINARY 0 | ||
48 | #endif | ||
49 | |||
50 | struct filestr_desc | ||
51 | { | ||
52 | int osfd; /* The host OS file descriptor */ | ||
53 | bool mounted; /* Is host volume still mounted? */ | ||
54 | #ifdef HAVE_MULTIVOLUME | ||
55 | int volume; /* The virtual volume number */ | ||
56 | #endif | ||
57 | } openfiles[MAX_OPEN_FILES] = | ||
58 | { | ||
59 | [0 ... MAX_OPEN_FILES-1] = { .osfd = -1 } | ||
60 | }; | ||
61 | |||
62 | static struct filestr_desc * alloc_filestr(int *fildesp) | ||
63 | { | ||
64 | for (unsigned int i = 0; i < MAX_OPEN_FILES; i++) | ||
65 | { | ||
66 | struct filestr_desc *filestr = &openfiles[i]; | ||
67 | if (filestr->osfd < 0) | ||
68 | { | ||
69 | *fildesp = i; | ||
70 | return filestr; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | static struct filestr_desc * get_filestr(int fildes) | ||
78 | { | ||
79 | struct filestr_desc *filestr = &openfiles[fildes]; | ||
80 | |||
81 | if ((unsigned int)fildes >= MAX_OPEN_FILES || filestr->osfd < 0) | ||
82 | filestr = NULL; | ||
83 | else if (filestr->mounted) | ||
84 | return filestr; | ||
85 | |||
86 | errno = filestr ? ENXIO : EBADF; | ||
87 | DEBUGF("fildes %d: %s\n", fildes, strerror(errno)); | ||
88 | return NULL; | ||
89 | } | ||
90 | |||
91 | struct dirstr_desc | ||
92 | { | ||
93 | int osfd; /* Host OS directory file descriptor */ | ||
94 | bool osfd_opened; /* Host fd is another open file */ | ||
95 | OS_DIR_T *osdirp; /* Host OS directory stream */ | ||
96 | #ifdef USE_OSDIRNAME | ||
97 | char *osdirname; /* Host OS directory path */ | ||
98 | #endif | ||
99 | struct sim_dirent entry; /* Rockbox directory entry */ | ||
100 | #ifdef HAVE_MULTIVOLUME | ||
101 | int volume; /* Virtual volume number */ | ||
102 | int volumecounter; /* Counter for root volume entries */ | ||
103 | #endif | ||
104 | bool mounted; /* Is the virtual volume still mounted? */ | ||
105 | } opendirs[MAX_OPEN_DIRS]; | ||
106 | |||
107 | static struct dirstr_desc * alloc_dirstr(void) | ||
108 | { | ||
109 | for (unsigned int i = 0; i < MAX_OPEN_DIRS; i++) | ||
110 | { | ||
111 | struct dirstr_desc *dirstr = &opendirs[i]; | ||
112 | if (dirstr->osdirp == NULL) | ||
113 | return dirstr; | ||
114 | } | ||
115 | |||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | static struct dirstr_desc * get_dirstr(DIR *dirp) | ||
120 | { | ||
121 | struct dirstr_desc *dirstr = (struct dirstr_desc *)dirp; | ||
122 | |||
123 | if (!PTR_IN_ARRAY(opendirs, dirstr, MAX_OPEN_DIRS) || !dirstr->osdirp) | ||
124 | dirstr = NULL; | ||
125 | else if (dirstr->mounted) | ||
126 | return dirstr; | ||
127 | |||
128 | errno = dirstr ? ENXIO : EBADF; | ||
129 | DEBUGF("dir #%d: %s\n", (int)(dirstr - opendirs), strerror(errno)); | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static int close_dirstr(struct dirstr_desc *dirstr) | ||
134 | { | ||
135 | OS_DIR_T *osdirp = dirstr->osdirp; | ||
136 | |||
137 | dirstr->mounted = false; | ||
138 | |||
139 | #ifdef USE_OSDIRNAME | ||
140 | free(dirstr->osdirname); | ||
141 | #endif | ||
142 | if (dirstr->osfd_opened) | ||
143 | { | ||
144 | os_close(dirstr->osfd); | ||
145 | dirstr->osfd_opened = false; | ||
146 | } | ||
147 | |||
148 | int rc = os_closedir(osdirp); | ||
149 | dirstr->osdirp = NULL; | ||
150 | |||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | #ifdef HAVE_MULTIVOLUME | ||
155 | static int readdir_volume_inner(struct dirstr_desc *dirstr, | ||
156 | struct sim_dirent *entry) | ||
157 | { | ||
158 | /* Volumes (secondary file systems) get inserted into the system root | ||
159 | * directory. If the path specified volume 0, enumeration will not | ||
160 | * include other volumes, but just its own files and directories. | ||
161 | * | ||
162 | * Fake special directories, which don't really exist, that will get | ||
163 | * redirected upon opendir() | ||
164 | */ | ||
165 | while (++dirstr->volumecounter < NUM_VOLUMES) | ||
166 | { | ||
167 | /* on the system root */ | ||
168 | if (!volume_present(dirstr->volumecounter)) | ||
169 | continue; | ||
170 | |||
171 | get_volume_name(dirstr->volumecounter, entry->d_name); | ||
172 | return 1; | ||
173 | } | ||
174 | |||
175 | /* do normal directory entry fetching */ | ||
176 | return 0; | ||
177 | } | ||
178 | #endif /* HAVE_MULTIVOLUME */ | ||
179 | |||
180 | static inline int readdir_volume(struct dirstr_desc *dirstr, | ||
181 | struct sim_dirent *entry) | ||
182 | { | ||
183 | #ifdef HAVE_MULTIVOLUME | ||
184 | if (dirstr->volumecounter < NUM_VOLUMES) | ||
185 | return readdir_volume_inner(dirstr, entry); | ||
186 | #endif /* HAVE_MULTIVOLUME */ | ||
187 | |||
188 | /* do normal directory entry fetching */ | ||
189 | return 0; | ||
190 | (void)dirstr; (void)entry; | ||
191 | } | ||
192 | |||
193 | |||
194 | /** Internal functions **/ | ||
195 | |||
196 | #ifdef HAVE_MULTIDRIVE | ||
197 | /** | ||
198 | * Handle drive extraction by pretending the files' volumes no longer exist | ||
199 | * and invalidating their I/O for the remainder of their lifetimes as would | ||
200 | * happen on a native target | ||
201 | */ | ||
202 | void sim_ext_extracted(int drive) | ||
203 | { | ||
204 | for (unsigned int i = 0; i < MAX_OPEN_FILES; i++) | ||
205 | { | ||
206 | struct filestr_desc *filestr = &openfiles[i]; | ||
207 | if (filestr->osfd >= 0 && volume_drive(filestr->volume) == drive) | ||
208 | filestr->mounted = false; | ||
209 | } | ||
210 | |||
211 | for (unsigned int i = 0; i < MAX_OPEN_DIRS; i++) | ||
212 | { | ||
213 | struct dirstr_desc *dirstr = &opendirs[i]; | ||
214 | if (dirstr->osdirp && volume_drive(dirstr->volume) == drive) | ||
215 | dirstr->mounted = false; | ||
216 | } | ||
217 | |||
218 | (void)drive; | ||
219 | } | ||
220 | #endif /* HAVE_MULTIDRIVE */ | ||
221 | |||
222 | /** | ||
223 | * Provides target-like path parsing behavior with single and multiple volumes | ||
224 | * while performing minimal transforming of the input. | ||
225 | * | ||
226 | * Paths are sandboxed to the simulated namespace: | ||
227 | * e.g. "{simdir}/foo/../../../../bar" becomes "{simdir}/foo/../bar" | ||
228 | */ | ||
229 | int sim_get_os_path(char *buffer, const char *path, size_t bufsize) | ||
230 | { | ||
231 | #define ADVBUF(amt) \ | ||
232 | ({ buffer += (amt); bufsize -= (amt); }) | ||
233 | |||
234 | #define PPP_SHOWPATHS 0 | ||
235 | |||
236 | if (!path_is_absolute(path)) | ||
237 | { | ||
238 | DEBUGF("ERROR: path is not absolute: \"%s\"\n", path); | ||
239 | errno = ENOENT; | ||
240 | return -1; | ||
241 | } | ||
242 | |||
243 | #if PPP_SHOWPATHS | ||
244 | const char *const buffer0 = buffer; | ||
245 | DEBUGF("PPP (pre): \"%s\"\n", path); | ||
246 | #endif | ||
247 | |||
248 | /* Prepend sim root */ | ||
249 | size_t size = strlcpy(buffer, sim_root_dir, bufsize); | ||
250 | if (size >= bufsize) | ||
251 | { | ||
252 | errno = ENAMETOOLONG; | ||
253 | return -1; | ||
254 | } | ||
255 | ADVBUF(size); | ||
256 | |||
257 | #ifdef HAVE_MULTIVOLUME | ||
258 | /* Track the last valid volume spec encountered */ | ||
259 | int volume = -1; | ||
260 | bool sysroot = true; | ||
261 | |||
262 | /* Basename of sim dir to switch back to simdisk from ext */ | ||
263 | #define DIRBASE_FMT ".." PATH_SEPSTR "%s" | ||
264 | ssize_t dirbase_len = 0; | ||
265 | char dirbase[size + 3 + 1]; | ||
266 | |||
267 | /* Basename of ext directory to switch to alternate volume */ | ||
268 | #define SIMEXT_FMT ".." PATH_SEPSTR "simext%d" | ||
269 | char extbuf[sizeof (SIMEXT_FMT) + 20 + 1]; | ||
270 | #endif /* HAVE_MULTIVOLUME */ | ||
271 | |||
272 | int level = 0; | ||
273 | bool done = false; | ||
274 | while (!done) | ||
275 | { | ||
276 | const char *p; | ||
277 | ssize_t len = parse_path_component(&path, &p); | ||
278 | |||
279 | |||
280 | switch (len) | ||
281 | { | ||
282 | case 0: | ||
283 | done = true; | ||
284 | if (path[-1] != PATH_SEPCH) | ||
285 | continue; | ||
286 | |||
287 | /* Path ends with a separator; preserve that */ | ||
288 | p = &path[-1]; | ||
289 | len = 1; | ||
290 | break; | ||
291 | |||
292 | case 1: | ||
293 | case 2: | ||
294 | if (p[0] == '.') | ||
295 | { | ||
296 | if (len == 1) | ||
297 | break; | ||
298 | |||
299 | if (p[1] == '.') | ||
300 | goto is_dot_dot; | ||
301 | } | ||
302 | |||
303 | default: | ||
304 | level++; | ||
305 | |||
306 | #ifdef HAVE_MULTIVOLUME | ||
307 | if (level != 1) | ||
308 | break; /* Volume spec only valid @ root level */ | ||
309 | |||
310 | const char *next; | ||
311 | volume = path_strip_volume(p, &next, true); | ||
312 | |||
313 | if (next > p) | ||
314 | { | ||
315 | #ifdef HAVE_MULTIDRIVE | ||
316 | /* Feign failure if the volume isn't "mounted" */ | ||
317 | if (!volume_present(volume)) | ||
318 | { | ||
319 | errno = ENXIO; | ||
320 | return -1; | ||
321 | } | ||
322 | #endif /* HAVE_MULTIDRIVE */ | ||
323 | |||
324 | sysroot = false; | ||
325 | |||
326 | if (volume == 0) | ||
327 | continue; | ||
328 | |||
329 | p = extbuf; | ||
330 | len = snprintf(extbuf, sizeof (extbuf), SIMEXT_FMT, volume); | ||
331 | } | ||
332 | #endif /* HAVE_MULTIVOLUME */ | ||
333 | break; | ||
334 | |||
335 | is_dot_dot: | ||
336 | if (level <= 0) | ||
337 | continue; /* Can't go above root; erase */ | ||
338 | |||
339 | level--; | ||
340 | |||
341 | #ifdef HAVE_MULTIVOLUME | ||
342 | if (level == 0) | ||
343 | { | ||
344 | int v = volume; | ||
345 | bool sr = sysroot; | ||
346 | volume = -1; | ||
347 | sysroot = true; | ||
348 | |||
349 | if (v <= 0) | ||
350 | { | ||
351 | if (sr) | ||
352 | break; | ||
353 | |||
354 | continue; | ||
355 | } | ||
356 | |||
357 | /* Going up from a volume root and back down to the sys root */ | ||
358 | if (dirbase_len == 0) | ||
359 | { | ||
360 | /* Get the main simdisk directory so it can be reentered */ | ||
361 | char tmpbuf[sizeof (dirbase)]; | ||
362 | #ifdef WIN32 | ||
363 | path_correct_separators(tmpbuf, sim_root_dir); | ||
364 | path_strip_drive(tmpbuf, &p, false); | ||
365 | #else | ||
366 | p = tmpbuf; | ||
367 | strcpy(tmpbuf, sim_root_dir); | ||
368 | #endif | ||
369 | size = path_basename(p, &p); | ||
370 | ((char *)p)[size] = '\0'; | ||
371 | |||
372 | if (size == 0 || is_dotdir_name(p)) | ||
373 | { | ||
374 | /* This is nonsense and won't work */ | ||
375 | DEBUGF("ERROR: sim root dir basname is dotdir or" | ||
376 | " empty: \"%s\"\n", sim_root_dir); | ||
377 | errno = ENOENT; | ||
378 | return -1; | ||
379 | } | ||
380 | |||
381 | dirbase_len = snprintf(dirbase, sizeof (dirbase), | ||
382 | DIRBASE_FMT, p); | ||
383 | } | ||
384 | |||
385 | p = dirbase; | ||
386 | len = dirbase_len; | ||
387 | break; | ||
388 | } | ||
389 | #endif /* HAVE_MULTIVOLUME */ | ||
390 | break; | ||
391 | } /* end switch */ | ||
392 | |||
393 | char compname[len + 1]; | ||
394 | strmemcpy(compname, p, len); | ||
395 | |||
396 | size = path_append(buffer, PA_SEP_HARD, compname, bufsize); | ||
397 | if (size >= bufsize) | ||
398 | { | ||
399 | errno = ENAMETOOLONG; | ||
400 | return -1; | ||
401 | } | ||
402 | ADVBUF(size); | ||
403 | } | ||
404 | |||
405 | #if PPP_SHOWPATHS | ||
406 | DEBUGF("PPP (post): \"%s\"" IF_MV(" vol:%d") "\n", | ||
407 | buffer0 IF_MV(, volume)); | ||
408 | #endif | ||
409 | |||
410 | return IF_MV(volume) +1; | ||
411 | } | ||
412 | |||
413 | |||
414 | /** File functions **/ | ||
415 | |||
416 | int sim_open(const char *path, int oflag, ...) | ||
417 | { | ||
418 | int fildes; | ||
419 | struct filestr_desc *filestr = alloc_filestr(&fildes); | ||
420 | if (!filestr) | ||
421 | { | ||
422 | errno = EMFILE; | ||
423 | return -1; | ||
424 | } | ||
425 | |||
426 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
427 | int pprc = sim_get_os_path(ospath, path, sizeof (ospath)); | ||
428 | if (pprc < 0) | ||
429 | return -2; | ||
430 | |||
431 | filestr->osfd = os_open(ospath, oflag | O_BINARY __OPEN_MODE_ARG); | ||
432 | if (filestr->osfd < 0) | ||
433 | return -3; | ||
434 | |||
435 | #ifdef HAVE_MULTIVOLUME | ||
436 | filestr->volume = MAX(pprc - 1, 0); | ||
437 | #endif | ||
438 | filestr->mounted = true; | ||
439 | return fildes; | ||
440 | } | ||
441 | |||
442 | int sim_creat(const char *path, mode_t mode) | ||
443 | { | ||
444 | return sim_open(path, O_WRONLY|O_CREAT|O_TRUNC, mode); | ||
445 | } | ||
446 | |||
447 | int sim_close(int fildes) | ||
448 | { | ||
449 | struct filestr_desc *filestr = &openfiles[fildes]; | ||
450 | if ((unsigned int)fildes >= MAX_OPEN_FILES || filestr->osfd < 0) | ||
451 | { | ||
452 | errno = EBADF; | ||
453 | return -1; | ||
454 | } | ||
455 | |||
456 | int osfd = filestr->osfd; | ||
457 | filestr->osfd = -1; | ||
458 | return os_close(osfd); | ||
459 | } | ||
460 | |||
461 | int sim_ftruncate(int fildes, off_t length) | ||
462 | { | ||
463 | struct filestr_desc *filestr = get_filestr(fildes); | ||
464 | if (!filestr) | ||
465 | return -1; | ||
466 | |||
467 | off_t size = os_filesize(filestr->osfd); | ||
468 | if (size < 0) | ||
469 | return -1; | ||
470 | |||
471 | if (length >= size) | ||
472 | return 0; | ||
473 | |||
474 | int rc = os_ftruncate(filestr->osfd, length); | ||
475 | |||
476 | #ifdef HAVE_DIRCACHE | ||
477 | if (rc >= 0) | ||
478 | dircache_ftruncate(xxxxxx); | ||
479 | #endif | ||
480 | |||
481 | return rc; | ||
482 | } | ||
483 | |||
484 | int sim_fsync(int fildes) | ||
485 | { | ||
486 | struct filestr_desc *filestr = get_filestr(fildes); | ||
487 | if (!filestr) | ||
488 | return -1; | ||
489 | |||
490 | int rc = os_fsync(filestr->osfd); | ||
491 | |||
492 | #ifdef HAVE_DIRCACHE | ||
493 | if (rc >= 0) | ||
494 | dircache_fsync(xxxxxx); | ||
495 | #endif | ||
496 | |||
497 | return rc; | ||
498 | } | ||
499 | |||
500 | off_t sim_lseek(int fildes, off_t offset, int whence) | ||
501 | { | ||
502 | struct filestr_desc *filestr = get_filestr(fildes); | ||
503 | if (!filestr) | ||
504 | return -1; | ||
505 | |||
506 | return os_lseek(filestr->osfd, offset, whence); | ||
507 | } | ||
508 | |||
509 | ssize_t sim_read(int fildes, void *buf, size_t nbyte) | ||
510 | { | ||
511 | struct filestr_desc *filestr = get_filestr(fildes); | ||
512 | if (!filestr) | ||
513 | return -1; | ||
514 | |||
515 | return os_read(filestr->osfd, buf, nbyte); | ||
516 | } | ||
517 | |||
518 | ssize_t sim_write(int fildes, const void *buf, size_t nbyte) | ||
519 | { | ||
520 | struct filestr_desc *filestr = get_filestr(fildes); | ||
521 | if (!filestr) | ||
522 | return -1; | ||
523 | |||
524 | return os_write(filestr->osfd, buf, nbyte); | ||
525 | } | ||
526 | |||
527 | int sim_remove(const char *path) | ||
528 | { | ||
529 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
530 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
531 | return -1; | ||
532 | |||
533 | int rc = os_remove(ospath); | ||
534 | |||
535 | #ifdef HAVE_DIRCACHE | ||
536 | if (rc >= 0) | ||
537 | dircache_remove(xxxxxx); | ||
538 | #endif | ||
539 | |||
540 | return rc; | ||
541 | } | ||
542 | |||
543 | int sim_rename(const char *old, const char *new) | ||
544 | { | ||
545 | char osold[SIM_TMPBUF_MAX_PATH]; | ||
546 | int pprc1 = sim_get_os_path(osold, old, sizeof (osold)); | ||
547 | if (pprc1 < 0) | ||
548 | return -1; | ||
549 | |||
550 | char osnew[SIM_TMPBUF_MAX_PATH]; | ||
551 | int pprc2 = sim_get_os_path(osnew, new, sizeof (osnew)); | ||
552 | if (pprc2 < 0) | ||
553 | return -1; | ||
554 | |||
555 | if (MAX(pprc1 - 1, 0) != MAX(pprc2 - 1, 0)) | ||
556 | { | ||
557 | /* Pretend they're different devices */ | ||
558 | errno = EXDEV; | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | int rc = os_rename(osold, osnew); | ||
563 | |||
564 | #ifdef HAVE_DIRCACHE | ||
565 | if (rc >= 0) | ||
566 | dircache_rename(xxxxxx); | ||
567 | #endif | ||
568 | |||
569 | return rc; | ||
570 | } | ||
571 | |||
572 | off_t sim_filesize(int fildes) | ||
573 | { | ||
574 | struct filestr_desc *filestr = get_filestr(fildes); | ||
575 | if (!filestr) | ||
576 | return -1; | ||
577 | |||
578 | return os_filesize(filestr->osfd); | ||
579 | } | ||
580 | |||
581 | int sim_fsamefile(int fildes1, int fildes2) | ||
582 | { | ||
583 | struct filestr_desc *filestr1 = get_filestr(fildes1); | ||
584 | if (!filestr1) | ||
585 | return -1; | ||
586 | |||
587 | struct filestr_desc *filestr2 = get_filestr(fildes2); | ||
588 | if (!filestr2) | ||
589 | return -1; | ||
590 | |||
591 | if (filestr1 == filestr2) | ||
592 | return 1; | ||
593 | |||
594 | return os_fsamefile(filestr1->osfd, filestr2->osfd); | ||
595 | } | ||
596 | |||
597 | int sim_relate(const char *path1, const char *path2) | ||
598 | { | ||
599 | char ospath1[SIM_TMPBUF_MAX_PATH]; | ||
600 | if (sim_get_os_path(ospath1, path1, sizeof (ospath1)) < 0) | ||
601 | return -1; | ||
602 | |||
603 | char ospath2[SIM_TMPBUF_MAX_PATH]; | ||
604 | if (sim_get_os_path(ospath2, path2, sizeof (ospath2)) < 0) | ||
605 | return -1; | ||
606 | |||
607 | return os_relate(ospath1, ospath2); | ||
608 | } | ||
609 | |||
610 | bool sim_file_exists(const char *path) | ||
611 | { | ||
612 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
613 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
614 | return false; | ||
615 | |||
616 | return os_file_exists(ospath); | ||
617 | } | ||
618 | |||
619 | |||
620 | /** Directory functions **/ | ||
621 | DIR * sim_opendir(const char *dirname) | ||
622 | { | ||
623 | struct dirstr_desc *dirstr = alloc_dirstr(); | ||
624 | if (!dirstr) | ||
625 | { | ||
626 | errno = EMFILE; | ||
627 | return NULL; | ||
628 | } | ||
629 | |||
630 | char osdirname[SIM_TMPBUF_MAX_PATH]; | ||
631 | int pprc = sim_get_os_path(osdirname, dirname, sizeof (osdirname)); | ||
632 | if (pprc < 0) | ||
633 | return NULL; | ||
634 | |||
635 | int rc = os_opendir_and_fd(osdirname, &dirstr->osdirp, &dirstr->osfd); | ||
636 | if (rc < 0) | ||
637 | return NULL; | ||
638 | |||
639 | dirstr->osfd_opened = rc > 0; | ||
640 | |||
641 | #ifdef USE_OSDIRNAME | ||
642 | dirstr->osdirname = strdup(osdirname); | ||
643 | if (!dirstr->osdirname) | ||
644 | { | ||
645 | close_dirstr(dirstr); | ||
646 | return NULL; | ||
647 | } | ||
648 | #endif | ||
649 | |||
650 | dirstr->entry.d_name[0] = 0; /* Mark as invalid */ | ||
651 | #ifdef HAVE_MULTIVOLUME | ||
652 | dirstr->volume = MAX(pprc - 1, 0); | ||
653 | dirstr->volumecounter = pprc == 0 ? 0 : INT_MAX; | ||
654 | #endif | ||
655 | dirstr->mounted = true; | ||
656 | return (DIR *)dirstr; /* A-Okay */ | ||
657 | } | ||
658 | |||
659 | int sim_closedir(DIR *dirp) | ||
660 | { | ||
661 | struct dirstr_desc *dirstr = (struct dirstr_desc *)dirp; | ||
662 | if (!PTR_IN_ARRAY(opendirs, dirstr, MAX_OPEN_DIRS) || !dirstr->osdirp) | ||
663 | { | ||
664 | errno = EBADF; | ||
665 | return -1; | ||
666 | } | ||
667 | |||
668 | return close_dirstr(dirstr); | ||
669 | } | ||
670 | |||
671 | struct sim_dirent * sim_readdir(DIR *dirp) | ||
672 | { | ||
673 | struct dirstr_desc *dirstr = get_dirstr(dirp); | ||
674 | if (!dirstr) | ||
675 | return NULL; | ||
676 | |||
677 | struct sim_dirent *entry = &dirstr->entry; | ||
678 | entry->info.osdirent = NULL; | ||
679 | |||
680 | if (readdir_volume(dirstr, entry)) | ||
681 | return entry; | ||
682 | |||
683 | OS_DIRENT_T *osdirent = os_readdir(dirstr->osdirp); | ||
684 | if (!osdirent) | ||
685 | return NULL; | ||
686 | |||
687 | size_t size = sizeof (entry->d_name); | ||
688 | if (strlcpy_from_os(entry->d_name, osdirent->d_name, size) >= size) | ||
689 | FILE_ERROR_RETURN(ENAMETOOLONG, NULL); | ||
690 | |||
691 | entry->info.osdirent = osdirent; | ||
692 | return entry; | ||
693 | } | ||
694 | |||
695 | int sim_mkdir(const char *path) | ||
696 | { | ||
697 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
698 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
699 | return -1; | ||
700 | |||
701 | int rc = os_mkdir(ospath __MKDIR_MODE_ARG); | ||
702 | |||
703 | #ifdef HAVE_DIRCACHE | ||
704 | if (rc >= 0) | ||
705 | dircache_mkdir(xxxxxx); | ||
706 | #endif | ||
707 | |||
708 | return rc; | ||
709 | } | ||
710 | |||
711 | int sim_rmdir(const char *path) | ||
712 | { | ||
713 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
714 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
715 | return -1; | ||
716 | |||
717 | int rc = os_rmdir(ospath); | ||
718 | |||
719 | #ifdef HAVE_DIRCACHE | ||
720 | if (rc >= 0) | ||
721 | dircache_rmdir(xxxxxx); | ||
722 | #endif | ||
723 | |||
724 | return rc; | ||
725 | } | ||
726 | |||
727 | int sim_samedir(DIR *dirp1, DIR *dirp2) | ||
728 | { | ||
729 | struct dirstr_desc *dirstr1 = get_dirstr(dirp1); | ||
730 | if (!dirstr1) | ||
731 | return -1; | ||
732 | |||
733 | struct dirstr_desc *dirstr2 = get_dirstr(dirp2); | ||
734 | if (!dirstr2) | ||
735 | return -1; | ||
736 | |||
737 | return os_fsamefile(dirstr1->osfd, dirstr2->osfd); | ||
738 | } | ||
739 | |||
740 | bool sim_dir_exists(const char *dirname) | ||
741 | { | ||
742 | char osdirname[SIM_TMPBUF_MAX_PATH]; | ||
743 | if (sim_get_os_path(osdirname, dirname, sizeof (osdirname)) < 0) | ||
744 | return false; | ||
745 | |||
746 | OS_DIR_T *dirp = os_opendir(osdirname); | ||
747 | if (!dirp) | ||
748 | return false; | ||
749 | |||
750 | os_closedir(dirp); | ||
751 | return true; | ||
752 | } | ||
753 | |||
754 | struct dirinfo dir_get_info(DIR *dirp, struct sim_dirent *entry) | ||
755 | { | ||
756 | int rc; | ||
757 | struct dirstr_desc *dirstr = get_dirstr(dirp); | ||
758 | if (!dirstr) | ||
759 | FILE_ERROR(ERRNO, RC); | ||
760 | |||
761 | if (entry->d_name[0] == 0) | ||
762 | FILE_ERROR(ENOENT, RC); | ||
763 | |||
764 | OS_DIRENT_T *osdirent = entry->info.osdirent; | ||
765 | if (osdirent == NULL) | ||
766 | return (struct dirinfo){ .attribute = ATTR_MOUNT_POINT }; | ||
767 | |||
768 | struct dirinfo info; | ||
769 | info.attribute = 0; | ||
770 | |||
771 | OS_STAT_T s; | ||
772 | |||
773 | #ifdef os_fstatat | ||
774 | if (os_fstatat(dirstr->osfd, entry->d_name, &s, 0)) | ||
775 | #else /* no fstatat; build file name for stat() */ | ||
776 | char statpath[SIM_TMPBUF_MAX_PATH]; | ||
777 | if (path_append(statpath, dirstr->osdirname, entry->d_name, | ||
778 | sizeof (statpath)) >= sizeof (statpath)) | ||
779 | { | ||
780 | FILE_ERROR(ENAMETOOLONG, RC); | ||
781 | } | ||
782 | |||
783 | if (os_stat(statpath, &s)) /* get info */ | ||
784 | #endif /* os_fstatat */ | ||
785 | { | ||
786 | /* File size larger than 2 GB? */ | ||
787 | #ifdef EOVERFLOW | ||
788 | if (errno == EOVERFLOW) | ||
789 | DEBUGF("stat overflow for \"%s\"\n", entry->d_name); | ||
790 | #endif | ||
791 | FILE_ERROR(ERRNO, RC); | ||
792 | } | ||
793 | |||
794 | if (S_ISDIR(s.st_mode)) | ||
795 | info.attribute |= ATTR_DIRECTORY; | ||
796 | |||
797 | info.size = s.st_size; | ||
798 | |||
799 | struct tm tm; | ||
800 | if (localtime_r(&s.st_mtime, &tm) == NULL) | ||
801 | FILE_ERROR(ERRNO, RC); | ||
802 | |||
803 | info.mtime = mktime(&tm); | ||
804 | |||
805 | return info; | ||
806 | |||
807 | file_error: | ||
808 | return (struct dirinfo){ .size = 0 }; | ||
809 | } | ||
810 | |||
811 | int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize) | ||
812 | { | ||
813 | if (!buffer || !bufsize IF_MV( || !volume_present(volume) )) | ||
814 | return -1; | ||
815 | |||
816 | char tmpbuf[SIM_TMPBUF_MAX_PATH]; | ||
817 | tmpbuf[0] = '\0'; | ||
818 | |||
819 | #ifdef HAVE_MULTIVOLUME | ||
820 | char volname[VOL_MAX_LEN + 1]; | ||
821 | get_volume_name(volume, volname); | ||
822 | |||
823 | if (path_append(tmpbuf, PA_SEP_HARD, volname, sizeof (volname)) | ||
824 | >= sizeof (volname)) | ||
825 | return -1; | ||
826 | #endif /* HAVE_MULTIVOLUME */ | ||
827 | |||
828 | if (path_append(tmpbuf, PA_SEP_HARD, ".", sizeof (tmpbuf)) | ||
829 | >= sizeof (tmpbuf)) | ||
830 | return -1; | ||
831 | |||
832 | return sim_get_os_path(buffer, tmpbuf, bufsize); | ||
833 | } | ||
diff --git a/uisimulator/common/filesystem-sim.h b/uisimulator/common/filesystem-sim.h new file mode 100644 index 0000000000..7c46c449d8 --- /dev/null +++ b/uisimulator/common/filesystem-sim.h | |||
@@ -0,0 +1,108 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #ifndef _FILESYSTEM_SIM_H_ | ||
22 | #define _FILESYSTEM_SIM_H_ | ||
23 | |||
24 | #if defined(PLUGIN) || defined(CODEC) | ||
25 | /* Prevent often-problematic plugin namespace pollution */ | ||
26 | #define FILEFUNCTIONS_DECLARED | ||
27 | #define FILEFUNCTIONS_DEFINED | ||
28 | #define DIRFUNCTIONS_DECLARED | ||
29 | #define DIRFUNCTIONS_DEFINED | ||
30 | #define OSFUNCTIONS_DECLARED | ||
31 | #endif /* PLUGIN || CODEC */ | ||
32 | |||
33 | #ifndef OSFUNCTIONS_DECLARED | ||
34 | #define FS_PREFIX(_x_) sim_ ## _x_ | ||
35 | |||
36 | /* path sandboxing and volume redirector */ | ||
37 | int sim_get_os_path(char *buffer, const char *path, size_t bufsize); | ||
38 | #define SIM_TMPBUF_MAX_PATH (MAX_PATH+1) | ||
39 | |||
40 | #endif /* !OSFUNCTIONS_DECLARED */ | ||
41 | |||
42 | #endif /* _FILESYSTEM_SIM_H_ */ | ||
43 | |||
44 | #include "filesystem-sdl.h" | ||
45 | #ifdef WIN32 | ||
46 | #include "filesystem-win32.h" | ||
47 | #else /* !WIN32 */ | ||
48 | #include "filesystem-unix.h" | ||
49 | #endif /* WIN32 */ | ||
50 | #include "filesystem-hosted.h" | ||
51 | |||
52 | #ifdef _FILE_H_ | ||
53 | #ifndef _FILESYSTEM_SIM_H__FILE_H_ | ||
54 | #define _FILESYSTEM_SIM_H__FILE_H_ | ||
55 | |||
56 | #ifdef RB_FILESYSTEM_OS | ||
57 | #define FILEFUNCTIONS_DEFINED | ||
58 | #endif | ||
59 | |||
60 | #ifndef FILEFUNCTIONS_DECLARED | ||
61 | int sim_open(const char *name, int oflag, ...); | ||
62 | int sim_creat(const char *name, mode_t mode); | ||
63 | int sim_close(int fildes); | ||
64 | int sim_ftruncate(int fildes, off_t length); | ||
65 | int sim_fsync(int fildes); | ||
66 | off_t sim_lseek(int fildes, off_t offset, int whence); | ||
67 | ssize_t sim_read(int fildes, void *buf, size_t nbyte); | ||
68 | ssize_t sim_write(int fildes, const void *buf, size_t nbyte); | ||
69 | int sim_remove(const char *path); | ||
70 | int sim_rename(const char *old, const char *new); | ||
71 | off_t sim_filesize(int fildes); | ||
72 | int sim_fsamefile(int fildes1, int fildes2); | ||
73 | int sim_relate(const char *path1, const char *path2); | ||
74 | bool sim_file_exists(const char *path); | ||
75 | #endif /* !FILEFUNCTIONS_DECLARED */ | ||
76 | |||
77 | #endif /* _FILESYSTEM_SIM_H__FILE_H_ */ | ||
78 | #endif /* _FILE_H_ */ | ||
79 | |||
80 | #ifdef _DIR_H_ | ||
81 | #ifndef _FILESYSTEM_SIM_H__DIR_H_ | ||
82 | #define _FILESYSTEM_SIM_H__DIR_H_ | ||
83 | |||
84 | #ifdef RB_FILESYSTEM_OS | ||
85 | #define DIRFUNCTIONS_DEFINED | ||
86 | #else | ||
87 | #define dirent DIRENT | ||
88 | #endif | ||
89 | |||
90 | #define DIRENT sim_dirent | ||
91 | |||
92 | struct dirinfo_native | ||
93 | { | ||
94 | void *osdirent; | ||
95 | }; | ||
96 | |||
97 | #ifndef DIRFUNCTIONS_DECLARED | ||
98 | DIR * sim_opendir(const char *dirname); | ||
99 | struct sim_dirent * sim_readdir(DIR *dirp); | ||
100 | int sim_closedir(DIR *dirp); | ||
101 | int sim_mkdir(const char *path); | ||
102 | int sim_rmdir(const char *path); | ||
103 | int sim_samedir(DIR *dirp1, DIR *dirp2); | ||
104 | bool sim_dir_exists(const char *dirname); | ||
105 | #endif /* !DIRFUNCTIONS_DECLARED */ | ||
106 | |||
107 | #endif /* _FILESYSTEM_SIM_H__DIR_H_ */ | ||
108 | #endif /* _DIR_H_ */ | ||
diff --git a/uisimulator/common/io.c b/uisimulator/common/io.c deleted file mode 100644 index 6662e9ffda..0000000000 --- a/uisimulator/common/io.c +++ /dev/null | |||
@@ -1,729 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <time.h> | ||
28 | #include <errno.h> | ||
29 | #include "config.h" | ||
30 | #include "system.h" | ||
31 | #include "ata_idle_notify.h" | ||
32 | #include "mv.h" | ||
33 | |||
34 | #define HAVE_STATVFS (!defined(WIN32)) | ||
35 | #define HAVE_LSTAT (!defined(WIN32)) | ||
36 | |||
37 | #if HAVE_STATVFS | ||
38 | #include <sys/statvfs.h> | ||
39 | #endif | ||
40 | |||
41 | #ifdef WIN32 | ||
42 | #include <windows.h> | ||
43 | #endif | ||
44 | |||
45 | #ifndef _MSC_VER | ||
46 | #include <dirent.h> | ||
47 | #include <unistd.h> | ||
48 | #else | ||
49 | #include "dir-win32.h" | ||
50 | #endif | ||
51 | |||
52 | #include <fcntl.h> | ||
53 | #ifdef HAVE_SDL_THREADS | ||
54 | #include "thread-sdl.h" | ||
55 | #else | ||
56 | #define sim_thread_unlock() NULL | ||
57 | #define sim_thread_lock(a) | ||
58 | #endif | ||
59 | #include "thread.h" | ||
60 | #include "kernel.h" | ||
61 | #include "debug.h" | ||
62 | #include "ata.h" /* for IF_MV et al. */ | ||
63 | #include "rbpaths.h" | ||
64 | #include "load_code.h" | ||
65 | |||
66 | /* keep this in sync with file.h! */ | ||
67 | #undef MAX_PATH /* this avoids problems when building simulator */ | ||
68 | #define MAX_PATH 260 | ||
69 | #define MAX_OPEN_FILES 11 | ||
70 | |||
71 | /* Windows (and potentially other OSes) distinguish binary and text files. | ||
72 | * Define a dummy for the others. */ | ||
73 | #ifndef O_BINARY | ||
74 | #define O_BINARY 0 | ||
75 | #endif | ||
76 | |||
77 | /* Unicode compatibility for win32 */ | ||
78 | #if defined __MINGW32__ | ||
79 | /* Rockbox unicode functions */ | ||
80 | extern const unsigned char* utf8decode(const unsigned char *utf8, | ||
81 | unsigned short *ucs); | ||
82 | extern unsigned char* utf8encode(unsigned long ucs, unsigned char *utf8); | ||
83 | |||
84 | /* Static buffers for the conversion results. This isn't thread safe, | ||
85 | * but it's sufficient for rockbox. */ | ||
86 | static unsigned char convbuf1[3*MAX_PATH]; | ||
87 | static unsigned char convbuf2[3*MAX_PATH]; | ||
88 | |||
89 | static wchar_t* utf8_to_ucs2(const unsigned char *utf8, void *buffer) | ||
90 | { | ||
91 | wchar_t *ucs = buffer; | ||
92 | |||
93 | while (*utf8) | ||
94 | utf8 = utf8decode(utf8, ucs++); | ||
95 | |||
96 | *ucs = 0; | ||
97 | return buffer; | ||
98 | } | ||
99 | static unsigned char *ucs2_to_utf8(const wchar_t *ucs, unsigned char *buffer) | ||
100 | { | ||
101 | unsigned char *utf8 = buffer; | ||
102 | |||
103 | while (*ucs) | ||
104 | utf8 = utf8encode(*ucs++, utf8); | ||
105 | |||
106 | *utf8 = 0; | ||
107 | return buffer; | ||
108 | } | ||
109 | |||
110 | #define UTF8_TO_OS(a) utf8_to_ucs2(a,convbuf1) | ||
111 | #define OS_TO_UTF8(a) ucs2_to_utf8(a,convbuf1) | ||
112 | #define DIR_T _WDIR | ||
113 | #define DIRENT_T struct _wdirent | ||
114 | #define STAT_T struct _stat | ||
115 | extern int _wmkdir(const wchar_t*); | ||
116 | extern int _wrmdir(const wchar_t*); | ||
117 | #define MKDIR(a,b) (_wmkdir)(UTF8_TO_OS(a)) | ||
118 | #define RMDIR(a) (_wrmdir)(UTF8_TO_OS(a)) | ||
119 | #define OPENDIR(a) (_wopendir)(UTF8_TO_OS(a)) | ||
120 | #define READDIR(a) (_wreaddir)(a) | ||
121 | #define CLOSEDIR(a) (_wclosedir)(a) | ||
122 | #define STAT(a,b) (_wstat)(UTF8_TO_OS(a),b) | ||
123 | /* empty variable parameter list doesn't work for variadic macros, | ||
124 | * so pretend the second parameter is variable too */ | ||
125 | #define OPEN(a,...) (_wopen)(UTF8_TO_OS(a), __VA_ARGS__) | ||
126 | #define CLOSE(a) (close)(a) | ||
127 | #define REMOVE(a) (_wremove)(UTF8_TO_OS(a)) | ||
128 | #define RENAME(a,b) (_wrename)(UTF8_TO_OS(a),utf8_to_ucs2(b,convbuf2)) | ||
129 | /* readlink isn't used in the sim yet (FIXME) */ | ||
130 | #define READLINK(a,b,c) ({ fprintf(stderr, "no readlink on windows yet"); abort(); }) | ||
131 | #else /* !__MINGW32__ */ | ||
132 | |||
133 | #define UTF8_TO_OS(a) (a) | ||
134 | #define OS_TO_UTF8(a) (a) | ||
135 | #define DIR_T DIR | ||
136 | #define DIRENT_T struct dirent | ||
137 | #define STAT_T struct stat | ||
138 | #define MKDIR(a,b) (mkdir)(a,b) | ||
139 | #define RMDIR(a) (rmdir)(a) | ||
140 | #define OPENDIR(a) (opendir)(a) | ||
141 | #define READDIR(a) (readdir)(a) | ||
142 | #define CLOSEDIR(a) (closedir)(a) | ||
143 | #define STAT(a,b) (stat)(a,b) | ||
144 | /* empty variable parameter list doesn't work for variadic macros, | ||
145 | * so pretend the second parameter is variable too */ | ||
146 | #define OPEN(a, ...) (open)(a, __VA_ARGS__) | ||
147 | #define CLOSE(x) (close)(x) | ||
148 | #define REMOVE(a) (remove)(a) | ||
149 | #define RENAME(a,b) (rename)(a,b) | ||
150 | #define READLINK(a,b,c) (readlink)(a,b,c) | ||
151 | |||
152 | #endif /* !__MINGW32__ */ | ||
153 | |||
154 | |||
155 | #ifdef HAVE_DIRCACHE | ||
156 | int dircache_get_entry_id(const char *filename); | ||
157 | void dircache_add_file(const char *name, long startcluster); | ||
158 | void dircache_remove(const char *name); | ||
159 | void dircache_rename(const char *oldname, const char *newname); | ||
160 | #endif | ||
161 | |||
162 | #ifndef APPLICATION | ||
163 | |||
164 | #define SIMULATOR_DEFAULT_ROOT "simdisk" | ||
165 | extern const char *sim_root_dir; | ||
166 | |||
167 | static int num_openfiles = 0; | ||
168 | |||
169 | /* from dir.h */ | ||
170 | struct dirinfo { | ||
171 | int attribute; | ||
172 | long size; | ||
173 | unsigned short wrtdate; | ||
174 | unsigned short wrttime; | ||
175 | }; | ||
176 | |||
177 | struct sim_dirent { | ||
178 | unsigned char d_name[MAX_PATH]; | ||
179 | struct dirinfo info; | ||
180 | long startcluster; | ||
181 | }; | ||
182 | |||
183 | struct dirstruct { | ||
184 | void *dir; /* actually a DIR* dir */ | ||
185 | char *name; | ||
186 | } SIM_DIR; | ||
187 | |||
188 | struct mydir { | ||
189 | DIR_T *dir; | ||
190 | IF_MV(int volumes_returned); | ||
191 | char *name; | ||
192 | }; | ||
193 | |||
194 | typedef struct mydir MYDIR; | ||
195 | |||
196 | static unsigned int rockbox2sim(int opt) | ||
197 | { | ||
198 | #if 0 | ||
199 | /* this shouldn't be needed since we use the host's versions */ | ||
200 | int newopt = O_BINARY; | ||
201 | |||
202 | if(opt & 1) | ||
203 | newopt |= O_WRONLY; | ||
204 | if(opt & 2) | ||
205 | newopt |= O_RDWR; | ||
206 | if(opt & 4) | ||
207 | newopt |= O_CREAT; | ||
208 | if(opt & 8) | ||
209 | newopt |= O_APPEND; | ||
210 | if(opt & 0x10) | ||
211 | newopt |= O_TRUNC; | ||
212 | |||
213 | return newopt; | ||
214 | #else | ||
215 | return opt|O_BINARY; | ||
216 | #endif | ||
217 | } | ||
218 | |||
219 | #endif /* APPLICATION */ | ||
220 | |||
221 | /** Simulator I/O engine routines **/ | ||
222 | #define IO_YIELD_THRESHOLD 512 | ||
223 | |||
224 | enum io_dir | ||
225 | { | ||
226 | IO_READ, | ||
227 | IO_WRITE, | ||
228 | }; | ||
229 | |||
230 | struct sim_io | ||
231 | { | ||
232 | struct mutex sim_mutex; /* Rockbox mutex */ | ||
233 | int cmd; /* The command to perform */ | ||
234 | int ready; /* I/O ready flag - 1= ready */ | ||
235 | int fd; /* The file to read/write */ | ||
236 | void *buf; /* The buffer to read/write */ | ||
237 | size_t count; /* Number of bytes to read/write */ | ||
238 | size_t accum; /* Acculated bytes transferred */ | ||
239 | }; | ||
240 | |||
241 | static struct sim_io io; | ||
242 | |||
243 | int ata_init(void) | ||
244 | { | ||
245 | /* Initialize the rockbox kernel objects on a rockbox thread */ | ||
246 | mutex_init(&io.sim_mutex); | ||
247 | io.accum = 0; | ||
248 | return 1; | ||
249 | } | ||
250 | |||
251 | int ata_spinup_time(void) | ||
252 | { | ||
253 | return HZ; | ||
254 | } | ||
255 | |||
256 | static ssize_t io_trigger_and_wait(enum io_dir cmd) | ||
257 | { | ||
258 | void *mythread = NULL; | ||
259 | ssize_t result; | ||
260 | |||
261 | if (io.count > IO_YIELD_THRESHOLD || | ||
262 | (io.accum += io.count) >= IO_YIELD_THRESHOLD) | ||
263 | { | ||
264 | /* Allow other rockbox threads to run */ | ||
265 | io.accum = 0; | ||
266 | mythread = sim_thread_unlock(); | ||
267 | } | ||
268 | |||
269 | switch (cmd) | ||
270 | { | ||
271 | case IO_READ: | ||
272 | result = read(io.fd, io.buf, io.count); | ||
273 | break; | ||
274 | case IO_WRITE: | ||
275 | result = write(io.fd, io.buf, io.count); | ||
276 | break; | ||
277 | /* shut up gcc */ | ||
278 | default: | ||
279 | result = -1; | ||
280 | } | ||
281 | |||
282 | call_storage_idle_notifys(false); | ||
283 | |||
284 | /* Regain our status as current */ | ||
285 | if (mythread != NULL) | ||
286 | { | ||
287 | sim_thread_lock(mythread); | ||
288 | } | ||
289 | |||
290 | return result; | ||
291 | } | ||
292 | |||
293 | |||
294 | ssize_t sim_read(int fd, void *buf, size_t count) | ||
295 | { | ||
296 | ssize_t result; | ||
297 | |||
298 | mutex_lock(&io.sim_mutex); | ||
299 | |||
300 | /* Setup parameters */ | ||
301 | io.fd = fd; | ||
302 | io.buf = buf; | ||
303 | io.count = count; | ||
304 | |||
305 | result = io_trigger_and_wait(IO_READ); | ||
306 | |||
307 | mutex_unlock(&io.sim_mutex); | ||
308 | |||
309 | return result; | ||
310 | } | ||
311 | |||
312 | |||
313 | ssize_t sim_write(int fd, const void *buf, size_t count) | ||
314 | { | ||
315 | ssize_t result; | ||
316 | |||
317 | mutex_lock(&io.sim_mutex); | ||
318 | |||
319 | io.fd = fd; | ||
320 | io.buf = (void*)buf; | ||
321 | io.count = count; | ||
322 | |||
323 | result = io_trigger_and_wait(IO_WRITE); | ||
324 | |||
325 | mutex_unlock(&io.sim_mutex); | ||
326 | |||
327 | return result; | ||
328 | } | ||
329 | |||
330 | #if !defined(APPLICATION) | ||
331 | |||
332 | static const char *handle_special_links(const char* link) | ||
333 | { | ||
334 | #ifdef HAVE_MULTIDRIVE | ||
335 | static char buffer[MAX_PATH]; /* sufficiently big */ | ||
336 | char vol_string[VOL_ENUM_POS + 8]; | ||
337 | int len = sprintf(vol_string, VOL_NAMES, 1); | ||
338 | |||
339 | /* link might be passed with or without HOME_DIR expanded. To handle | ||
340 | * both perform substring matching (VOL_NAMES is unique enough) */ | ||
341 | const char *begin = strstr(link, vol_string); | ||
342 | if (begin) | ||
343 | { | ||
344 | /* begin now points to the start of vol_string within link, | ||
345 | * we want to copy the remainder of the paths, prefixed by | ||
346 | * the actual mount point (the remainder might be "") */ | ||
347 | snprintf(buffer, sizeof(buffer), "%s/../simext/%s", | ||
348 | sim_root_dir ?: SIMULATOR_DEFAULT_ROOT, begin + len); | ||
349 | return buffer; | ||
350 | } | ||
351 | else | ||
352 | #endif | ||
353 | return link; | ||
354 | } | ||
355 | |||
356 | |||
357 | static const char *get_sim_pathname(const char *name) | ||
358 | { | ||
359 | static char buffer[MAX_PATH]; /* sufficiently big */ | ||
360 | |||
361 | if(name[0] == '/') | ||
362 | { | ||
363 | snprintf(buffer, sizeof(buffer), "%s%s", | ||
364 | sim_root_dir ?: SIMULATOR_DEFAULT_ROOT, name); | ||
365 | return handle_special_links(buffer); | ||
366 | } | ||
367 | fprintf(stderr, "WARNING, bad file name lacks slash: %s\n", name); | ||
368 | return name; | ||
369 | } | ||
370 | |||
371 | |||
372 | MYDIR *sim_opendir(const char *name) | ||
373 | { | ||
374 | DIR_T *dir; | ||
375 | dir = (DIR_T *) OPENDIR(get_sim_pathname(name)); | ||
376 | |||
377 | if (dir) | ||
378 | { | ||
379 | MYDIR *my = (MYDIR *)malloc(sizeof(MYDIR)); | ||
380 | my->dir = dir; | ||
381 | my->name = (char *)malloc(strlen(name)+1); | ||
382 | strcpy(my->name, name); | ||
383 | IF_MV(my->volumes_returned = 0); | ||
384 | |||
385 | return my; | ||
386 | } | ||
387 | /* failed open, return NULL */ | ||
388 | return (MYDIR *)0; | ||
389 | } | ||
390 | |||
391 | #if defined(WIN32) | ||
392 | static inline struct tm* localtime_r (const time_t *clock, struct tm *result) { | ||
393 | if (!clock || !result) return NULL; | ||
394 | memcpy(result,localtime(clock),sizeof(*result)); | ||
395 | return result; | ||
396 | } | ||
397 | #endif | ||
398 | |||
399 | struct sim_dirent *sim_readdir(MYDIR *dir) | ||
400 | { | ||
401 | char buffer[MAX_PATH]; /* sufficiently big */ | ||
402 | static struct sim_dirent secret; | ||
403 | STAT_T s; | ||
404 | struct tm tm; | ||
405 | DIRENT_T *x11; | ||
406 | |||
407 | #ifdef EOVERFLOW | ||
408 | read_next: | ||
409 | #endif | ||
410 | |||
411 | #define ATTR_LINK 0x80 /* see dir.h */ | ||
412 | |||
413 | secret.info.attribute = 0; | ||
414 | #ifdef HAVE_MULTIVOLUME | ||
415 | if (dir->name[0] == '/' && dir->name[1] == '\0' | ||
416 | && dir->volumes_returned++ < (NUM_VOLUMES-1) | ||
417 | && volume_present(dir->volumes_returned)) | ||
418 | { | ||
419 | sprintf((char *)secret.d_name, VOL_NAMES, dir->volumes_returned); | ||
420 | secret.info.attribute = ATTR_LINK; | ||
421 | /* build file name for stat() which is the actual mount point */ | ||
422 | snprintf(buffer, sizeof(buffer), "%s/../simext", | ||
423 | sim_root_dir ?: SIMULATOR_DEFAULT_ROOT); | ||
424 | } | ||
425 | else | ||
426 | #endif | ||
427 | { | ||
428 | x11 = READDIR(dir->dir); | ||
429 | |||
430 | if(!x11) | ||
431 | return (struct sim_dirent *)0; | ||
432 | |||
433 | strcpy((char *)secret.d_name, OS_TO_UTF8(x11->d_name)); | ||
434 | /* build file name for stat() */ | ||
435 | snprintf(buffer, sizeof(buffer), "%s/%s", | ||
436 | get_sim_pathname(dir->name), secret.d_name); | ||
437 | } | ||
438 | |||
439 | if (STAT(buffer, &s)) /* get info */ | ||
440 | { | ||
441 | #ifdef EOVERFLOW | ||
442 | /* File size larger than 2 GB? */ | ||
443 | if (errno == EOVERFLOW) | ||
444 | { | ||
445 | DEBUGF("stat() overflow for %s. Skipping\n", buffer); | ||
446 | goto read_next; | ||
447 | } | ||
448 | #endif | ||
449 | |||
450 | return NULL; | ||
451 | } | ||
452 | |||
453 | #define ATTR_DIRECTORY 0x10 | ||
454 | |||
455 | if (S_ISDIR(s.st_mode)) | ||
456 | secret.info.attribute = ATTR_DIRECTORY; | ||
457 | |||
458 | secret.info.size = s.st_size; | ||
459 | |||
460 | if (localtime_r(&(s.st_mtime), &tm) == NULL) | ||
461 | return NULL; | ||
462 | secret.info.wrtdate = ((tm.tm_year - 80) << 9) | | ||
463 | ((tm.tm_mon + 1) << 5) | | ||
464 | tm.tm_mday; | ||
465 | secret.info.wrttime = (tm.tm_hour << 11) | | ||
466 | (tm.tm_min << 5) | | ||
467 | (tm.tm_sec >> 1); | ||
468 | |||
469 | #if HAVE_LSTAT | ||
470 | if (!lstat(buffer, &s) && S_ISLNK(s.st_mode)) | ||
471 | { | ||
472 | secret.info.attribute |= ATTR_LINK; | ||
473 | } | ||
474 | #endif | ||
475 | |||
476 | return &secret; | ||
477 | } | ||
478 | |||
479 | void sim_closedir(MYDIR *dir) | ||
480 | { | ||
481 | free(dir->name); | ||
482 | CLOSEDIR(dir->dir); | ||
483 | |||
484 | free(dir); | ||
485 | } | ||
486 | |||
487 | int sim_open(const char *name, int o, ...) | ||
488 | { | ||
489 | int opts = rockbox2sim(o); | ||
490 | int ret; | ||
491 | if (num_openfiles >= MAX_OPEN_FILES) | ||
492 | return -2; | ||
493 | |||
494 | if (opts & O_CREAT) | ||
495 | { | ||
496 | va_list ap; | ||
497 | va_start(ap, o); | ||
498 | mode_t mode = va_arg(ap, unsigned int); | ||
499 | ret = OPEN(get_sim_pathname(name), opts, mode); | ||
500 | #ifdef HAVE_DIRCACHE | ||
501 | if (ret >= 0 && (dircache_get_entry_id(name) < 0)) | ||
502 | dircache_add_file(name, 0); | ||
503 | #endif | ||
504 | va_end(ap); | ||
505 | } | ||
506 | else | ||
507 | ret = OPEN(get_sim_pathname(name), opts); | ||
508 | |||
509 | if (ret >= 0) | ||
510 | num_openfiles++; | ||
511 | return ret; | ||
512 | } | ||
513 | |||
514 | int sim_close(int fd) | ||
515 | { | ||
516 | int ret; | ||
517 | ret = CLOSE(fd); | ||
518 | if (ret == 0) | ||
519 | num_openfiles--; | ||
520 | return ret; | ||
521 | } | ||
522 | |||
523 | int sim_creat(const char *name, mode_t mode) | ||
524 | { | ||
525 | int ret = OPEN(get_sim_pathname(name), | ||
526 | O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, mode); | ||
527 | #ifdef HAVE_DIRCACHE | ||
528 | if (ret >= 0 && (dircache_get_entry_id(name) < 0)) | ||
529 | dircache_add_file(name, 0); | ||
530 | #endif | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | int sim_mkdir(const char *name) | ||
535 | { | ||
536 | return MKDIR(get_sim_pathname(name), 0777); | ||
537 | } | ||
538 | |||
539 | int sim_rmdir(const char *name) | ||
540 | { | ||
541 | return RMDIR(get_sim_pathname(name)); | ||
542 | } | ||
543 | |||
544 | int sim_remove(const char *name) | ||
545 | { | ||
546 | int ret = REMOVE(get_sim_pathname(name)); | ||
547 | #ifdef HAVE_DIRCACHE | ||
548 | if (ret >= 0) | ||
549 | dircache_remove(name); | ||
550 | #endif | ||
551 | return ret; | ||
552 | } | ||
553 | |||
554 | int sim_rename(const char *oldname, const char *newname) | ||
555 | { | ||
556 | char sim_old[MAX_PATH]; | ||
557 | char sim_new[MAX_PATH]; | ||
558 | #ifdef HAVE_DIRCACHE | ||
559 | dircache_rename(oldname, newname); | ||
560 | #endif | ||
561 | // This is needed as get_sim_pathname() has a static buffer | ||
562 | strncpy(sim_old, get_sim_pathname(oldname), MAX_PATH); | ||
563 | strncpy(sim_new, get_sim_pathname(newname), MAX_PATH); | ||
564 | return RENAME(sim_old, sim_new); | ||
565 | } | ||
566 | |||
567 | /* rockbox off_t may be different from system off_t */ | ||
568 | long sim_lseek(int fildes, long offset, int whence) | ||
569 | { | ||
570 | return lseek(fildes, offset, whence); | ||
571 | } | ||
572 | |||
573 | #else | ||
574 | #define get_sim_pathname(x) x | ||
575 | #endif | ||
576 | |||
577 | long filesize(int fd) | ||
578 | { | ||
579 | #ifdef WIN32 | ||
580 | return _filelength(fd); | ||
581 | #else | ||
582 | struct stat buf; | ||
583 | |||
584 | if (!fstat(fd, &buf)) | ||
585 | return buf.st_size; | ||
586 | else | ||
587 | return -1; | ||
588 | #endif | ||
589 | } | ||
590 | |||
591 | void fat_size(IF_MV(int volume,) unsigned long* size, unsigned long* free) | ||
592 | { | ||
593 | #ifdef HAVE_MULTIVOLUME | ||
594 | if (volume != 0) { | ||
595 | /* debugf("io.c: fat_size(volume=%d); simulator only supports volume 0\n",volume); */ | ||
596 | |||
597 | if (size) *size = 0; | ||
598 | if (free) *free = 0; | ||
599 | return; | ||
600 | } | ||
601 | #endif | ||
602 | |||
603 | #ifdef WIN32 | ||
604 | long secperclus, bytespersec, free_clusters, num_clusters; | ||
605 | |||
606 | if (GetDiskFreeSpace(NULL, &secperclus, &bytespersec, &free_clusters, | ||
607 | &num_clusters)) { | ||
608 | if (size) | ||
609 | *size = num_clusters * secperclus / 2 * (bytespersec / 512); | ||
610 | if (free) | ||
611 | *free = free_clusters * secperclus / 2 * (bytespersec / 512); | ||
612 | } else | ||
613 | #elif HAVE_STATVFS | ||
614 | struct statvfs vfs; | ||
615 | |||
616 | if (!statvfs(".", &vfs)) { | ||
617 | DEBUGF("statvfs: frsize=%d blocks=%ld free=%ld\n", | ||
618 | (int)vfs.f_frsize, (long)vfs.f_blocks, (long)vfs.f_bfree); | ||
619 | if (size) | ||
620 | *size = vfs.f_blocks / 2 * (vfs.f_frsize / 512); | ||
621 | if (free) | ||
622 | *free = vfs.f_bfree / 2 * (vfs.f_frsize / 512); | ||
623 | } else | ||
624 | #endif | ||
625 | { | ||
626 | if (size) | ||
627 | *size = 0; | ||
628 | if (free) | ||
629 | *free = 0; | ||
630 | } | ||
631 | } | ||
632 | |||
633 | int sim_fsync(int fd) | ||
634 | { | ||
635 | #ifdef WIN32 | ||
636 | return _commit(fd); | ||
637 | #else | ||
638 | return fsync(fd); | ||
639 | #endif | ||
640 | } | ||
641 | |||
642 | #ifndef __PCTOOL__ | ||
643 | |||
644 | #include <SDL_loadso.h> | ||
645 | void *lc_open(const char *filename, unsigned char *buf, size_t buf_size) | ||
646 | { | ||
647 | (void)buf; | ||
648 | (void)buf_size; | ||
649 | void *handle = SDL_LoadObject(get_sim_pathname(filename)); | ||
650 | if (handle == NULL) | ||
651 | { | ||
652 | DEBUGF("failed to load %s\n", filename); | ||
653 | DEBUGF("lc_open(%s): %s\n", filename, SDL_GetError()); | ||
654 | } | ||
655 | return handle; | ||
656 | } | ||
657 | |||
658 | void *lc_get_header(void *handle) | ||
659 | { | ||
660 | char *ret = SDL_LoadFunction(handle, "__header"); | ||
661 | if (ret == NULL) | ||
662 | ret = SDL_LoadFunction(handle, "___header"); | ||
663 | |||
664 | return ret; | ||
665 | } | ||
666 | |||
667 | void lc_close(void *handle) | ||
668 | { | ||
669 | SDL_UnloadObject(handle); | ||
670 | } | ||
671 | |||
672 | void *lc_open_from_mem(void *addr, size_t blob_size) | ||
673 | { | ||
674 | #ifndef SIMULATOR | ||
675 | (void)addr; | ||
676 | (void)blob_size; | ||
677 | /* we don't support loading code from memory on application builds, | ||
678 | * it doesn't make sense (since it means writing the blob to disk again and | ||
679 | * then falling back to load from disk) and requires the ability to write | ||
680 | * to an executable directory */ | ||
681 | return NULL; | ||
682 | #else | ||
683 | /* support it in the sim for the sake of simulating */ | ||
684 | int fd, i; | ||
685 | char temp_filename[MAX_PATH]; | ||
686 | |||
687 | /* We have to create the dynamic link library file from ram so we | ||
688 | can simulate the codec loading. With voice and crossfade, | ||
689 | multiple codecs may be loaded at the same time, so we need | ||
690 | to find an unused filename */ | ||
691 | for (i = 0; i < 10; i++) | ||
692 | { | ||
693 | snprintf(temp_filename, sizeof(temp_filename), | ||
694 | ROCKBOX_DIR "/libtemp_binary_%d.dll", i); | ||
695 | fd = open(temp_filename, O_WRONLY|O_CREAT|O_TRUNC, 0700); | ||
696 | if (fd >= 0) | ||
697 | break; /* Created a file ok */ | ||
698 | } | ||
699 | |||
700 | if (fd < 0) | ||
701 | { | ||
702 | DEBUGF("open failed\n"); | ||
703 | return NULL; | ||
704 | } | ||
705 | |||
706 | if (write(fd, addr, blob_size) < (ssize_t)blob_size) | ||
707 | { | ||
708 | DEBUGF("Write failed\n"); | ||
709 | close(fd); | ||
710 | remove(temp_filename); | ||
711 | return NULL; | ||
712 | } | ||
713 | |||
714 | close(fd); | ||
715 | return lc_open(temp_filename, NULL, 0); | ||
716 | #endif | ||
717 | } | ||
718 | |||
719 | #endif /* __PCTOOL__ */ | ||
720 | |||
721 | /* rockbox off_t may be different from system off_t */ | ||
722 | int sim_ftruncate(int fd, long length) | ||
723 | { | ||
724 | #ifdef WIN32 | ||
725 | return _chsize(fd, length); | ||
726 | #else | ||
727 | return ftruncate(fd, length); | ||
728 | #endif | ||
729 | } | ||
diff --git a/uisimulator/common/load_code-sim.c b/uisimulator/common/load_code-sim.c new file mode 100644 index 0000000000..59ca97a259 --- /dev/null +++ b/uisimulator/common/load_code-sim.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #define RB_FILESYSTEM_OS | ||
22 | #include <stdio.h> | ||
23 | #include "config.h" | ||
24 | #include "system.h" | ||
25 | #include "file.h" | ||
26 | #include "load_code.h" | ||
27 | #include "rbpaths.h" | ||
28 | #include "debug.h" | ||
29 | |||
30 | void * lc_open(const char *filename, unsigned char *buf, size_t buf_size) | ||
31 | { | ||
32 | char osfilename[SIM_TMPBUF_MAX_PATH]; | ||
33 | if (sim_get_os_path(osfilename, filename, sizeof (osfilename)) < 0) | ||
34 | return NULL; | ||
35 | |||
36 | return os_lc_open(osfilename); | ||
37 | (void)buf; (void)buf_size; | ||
38 | } | ||
39 | |||
40 | void * lc_open_from_mem(void *addr, size_t blob_size) | ||
41 | { | ||
42 | /* We have to create the dynamic link library file from ram so we can | ||
43 | simulate code loading. Multiple binaries may be loaded at the same | ||
44 | time, so we need to find an unused filename. */ | ||
45 | int fd; | ||
46 | char tempfile[SIM_TMPBUF_MAX_PATH]; | ||
47 | for (unsigned int i = 0; i < 10; i++) | ||
48 | { | ||
49 | snprintf(tempfile, sizeof(tempfile), | ||
50 | ROCKBOX_DIR "/libtemp_binary_%d.dll", i); | ||
51 | fd = sim_open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0700); | ||
52 | if (fd >= 0) | ||
53 | break; /* Created a file ok */ | ||
54 | } | ||
55 | |||
56 | if (fd < 0) | ||
57 | { | ||
58 | DEBUGF("open failed\n"); | ||
59 | return NULL; | ||
60 | } | ||
61 | |||
62 | if (sim_write(fd, addr, blob_size) != (ssize_t)blob_size) | ||
63 | { | ||
64 | DEBUGF("Write failed\n"); | ||
65 | sim_close(fd); | ||
66 | sim_remove(tempfile); | ||
67 | return NULL; | ||
68 | } | ||
69 | |||
70 | sim_close(fd); | ||
71 | return lc_open(tempfile, NULL, 0); | ||
72 | } | ||
diff --git a/uisimulator/common/sim_tasks.c b/uisimulator/common/sim_tasks.c index 1299a69302..003b993740 100644 --- a/uisimulator/common/sim_tasks.c +++ b/uisimulator/common/sim_tasks.c | |||
@@ -28,6 +28,11 @@ | |||
28 | #include "thread.h" | 28 | #include "thread.h" |
29 | #include "debug.h" | 29 | #include "debug.h" |
30 | #include "usb.h" | 30 | #include "usb.h" |
31 | #include "mv.h" | ||
32 | #include "ata_idle_notify.h" | ||
33 | #ifdef WIN32 | ||
34 | #include <windows.h> | ||
35 | #endif | ||
31 | 36 | ||
32 | static void sim_thread(void); | 37 | static void sim_thread(void); |
33 | static long sim_thread_stack[DEFAULT_STACK_SIZE/sizeof(long)]; | 38 | static long sim_thread_stack[DEFAULT_STACK_SIZE/sizeof(long)]; |
@@ -46,6 +51,10 @@ enum { | |||
46 | #endif | 51 | #endif |
47 | }; | 52 | }; |
48 | 53 | ||
54 | #ifdef HAVE_MULTIDRIVE | ||
55 | extern void sim_ext_extracted(int drive); | ||
56 | #endif | ||
57 | |||
49 | void sim_thread(void) | 58 | void sim_thread(void) |
50 | { | 59 | { |
51 | struct queue_event ev; | 60 | struct queue_event ev; |
@@ -54,9 +63,13 @@ void sim_thread(void) | |||
54 | 63 | ||
55 | while (1) | 64 | while (1) |
56 | { | 65 | { |
57 | queue_wait(&sim_queue, &ev); | 66 | queue_wait_w_tmo(&sim_queue, &ev, 5*HZ); |
58 | switch(ev.id) | 67 | switch(ev.id) |
59 | { | 68 | { |
69 | case SYS_TIMEOUT: | ||
70 | call_storage_idle_notifys(false); | ||
71 | break; | ||
72 | |||
60 | case SIM_SCREENDUMP: | 73 | case SIM_SCREENDUMP: |
61 | screen_dump(); | 74 | screen_dump(); |
62 | #ifdef HAVE_REMOTE_LCD | 75 | #ifdef HAVE_REMOTE_LCD |
@@ -102,6 +115,7 @@ void sim_thread(void) | |||
102 | #ifdef HAVE_MULTIDRIVE | 115 | #ifdef HAVE_MULTIDRIVE |
103 | case SIM_EXT_INSERTED: | 116 | case SIM_EXT_INSERTED: |
104 | case SIM_EXT_EXTRACTED: | 117 | case SIM_EXT_EXTRACTED: |
118 | sim_ext_extracted(ev.data); | ||
105 | queue_broadcast(ev.id == SIM_EXT_INSERTED ? | 119 | queue_broadcast(ev.id == SIM_EXT_INSERTED ? |
106 | SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, 0); | 120 | SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, 0); |
107 | sleep(HZ/20); | 121 | sleep(HZ/20); |
@@ -174,11 +188,13 @@ static bool is_ext_inserted; | |||
174 | 188 | ||
175 | void sim_trigger_external(bool inserted) | 189 | void sim_trigger_external(bool inserted) |
176 | { | 190 | { |
191 | is_ext_inserted = inserted; | ||
192 | |||
193 | int drive = 1; /* Can do others! */ | ||
177 | if (inserted) | 194 | if (inserted) |
178 | queue_post(&sim_queue, SIM_EXT_INSERTED, 0); | 195 | queue_post(&sim_queue, SIM_EXT_INSERTED, drive); |
179 | else | 196 | else |
180 | queue_post(&sim_queue, SIM_EXT_EXTRACTED, 0); | 197 | queue_post(&sim_queue, SIM_EXT_EXTRACTED, drive); |
181 | is_ext_inserted = inserted; | ||
182 | } | 198 | } |
183 | 199 | ||
184 | bool hostfs_present(int drive) | 200 | bool hostfs_present(int drive) |
@@ -203,7 +219,13 @@ bool volume_present(int volume) | |||
203 | /* volume == drive for now */ | 219 | /* volume == drive for now */ |
204 | return hostfs_present(volume); | 220 | return hostfs_present(volume); |
205 | } | 221 | } |
206 | #endif | 222 | |
223 | int volume_drive(int volume) | ||
224 | { | ||
225 | /* volume == drive for now */ | ||
226 | return volume; | ||
227 | } | ||
228 | #endif /* HAVE_MULTIVOLUME */ | ||
207 | 229 | ||
208 | #if (CONFIG_STORAGE & STORAGE_MMC) | 230 | #if (CONFIG_STORAGE & STORAGE_MMC) |
209 | bool mmc_touched(void) | 231 | bool mmc_touched(void) |
@@ -212,4 +234,4 @@ bool mmc_touched(void) | |||
212 | } | 234 | } |
213 | #endif | 235 | #endif |
214 | 236 | ||
215 | #endif | 237 | #endif /* CONFIG_STORAGE & STORAGE_MMC */ |
diff --git a/uisimulator/common/stubs.c b/uisimulator/common/stubs.c index 18f60ce6b3..922b9e8da0 100644 --- a/uisimulator/common/stubs.c +++ b/uisimulator/common/stubs.c | |||
@@ -153,10 +153,9 @@ int fat_startsector(void) | |||
153 | return 63; | 153 | return 63; |
154 | } | 154 | } |
155 | 155 | ||
156 | bool fat_ismounted(int volume) | 156 | int ata_spinup_time(void) |
157 | { | 157 | { |
158 | (void)volume; | 158 | return 100; |
159 | return true; | ||
160 | } | 159 | } |
161 | 160 | ||
162 | int storage_spinup_time(void) | 161 | int storage_spinup_time(void) |
diff --git a/uisimulator/common/time-win32.c b/uisimulator/common/time-win32.c new file mode 100644 index 0000000000..1de69c35e6 --- /dev/null +++ b/uisimulator/common/time-win32.c | |||
@@ -0,0 +1,61 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 by Michael Sevakis | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include <time.h> | ||
22 | #include <errno.h> | ||
23 | #include "system.h" | ||
24 | #include "debug.h" | ||
25 | |||
26 | /* These functions we like but Windows doesn't because it implements the | ||
27 | non-"_r" versions with thread-local storage in its multithreaded libs */ | ||
28 | |||
29 | struct tm * localtime_r(const time_t *restrict timer, | ||
30 | struct tm *restrict result) | ||
31 | { | ||
32 | if (!timer || !result) | ||
33 | { | ||
34 | errno = EINVAL; | ||
35 | return NULL; | ||
36 | } | ||
37 | |||
38 | struct tm *tm = localtime(timer); | ||
39 | if (!tm) | ||
40 | return NULL; | ||
41 | |||
42 | *result = *tm; | ||
43 | return result; | ||
44 | } | ||
45 | |||
46 | struct tm * gmtime_r(const time_t *restrict timer, | ||
47 | struct tm *restrict result) | ||
48 | { | ||
49 | if (!timer || !result) | ||
50 | { | ||
51 | errno = EINVAL; | ||
52 | return NULL; | ||
53 | } | ||
54 | |||
55 | struct tm *tm = gmtime(timer); | ||
56 | if (!tm) | ||
57 | return NULL; | ||
58 | |||
59 | *result = *tm; | ||
60 | return result; | ||
61 | } | ||