FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / firmware / common / dircache.c
blobe846d554522bffaea78c58d3e28eac6768f96030
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Miika Pekkarinen
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* TODO:
23 - Allow cache live updating while transparent rebuild is running.
26 #include "config.h"
28 #include <stdio.h>
29 #include <errno.h>
30 #include <string.h>
31 #include <stdbool.h>
32 #include <stdlib.h>
33 #include "debug.h"
34 #include "system.h"
35 #include "logf.h"
36 #include "dircache.h"
37 #include "thread.h"
38 #include "kernel.h"
39 #include "usb.h"
40 #include "file.h"
41 #include "buffer.h"
42 #include "dir.h"
43 #if CONFIG_RTC
44 #include "time.h"
45 #include "timefuncs.h"
46 #endif
48 /* Queue commands. */
49 #define DIRCACHE_BUILD 1
50 #define DIRCACHE_STOP 2
52 #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8)
53 #define MAX_OPEN_DIRS 12
54 #else
55 #define MAX_OPEN_DIRS 8
56 #endif
57 static DIR_CACHED opendirs[MAX_OPEN_DIRS];
59 static struct dircache_entry *fd_bindings[MAX_OPEN_FILES];
60 static struct dircache_entry *dircache_root;
61 #ifdef HAVE_MULTIVOLUME
62 static struct dircache_entry *append_position;
63 #endif
65 static bool dircache_initialized = false;
66 static bool dircache_initializing = false;
67 static bool thread_enabled = false;
68 static unsigned long allocated_size = DIRCACHE_LIMIT;
69 static unsigned long dircache_size = 0;
70 static unsigned long entry_count = 0;
71 static unsigned long reserve_used = 0;
72 static unsigned int cache_build_ticks = 0;
73 static unsigned long appflags = 0;
74 static char dircache_cur_path[MAX_PATH*2];
76 static struct event_queue dircache_queue;
77 static long dircache_stack[(DEFAULT_STACK_SIZE + 0x900)/sizeof(long)];
78 static const char dircache_thread_name[] = "dircache";
80 static struct fdbind_queue fdbind_cache[MAX_PENDING_BINDINGS];
81 static int fdbind_idx = 0;
83 /* --- Internal cache structure control functions --- */
85 /**
86 * Internal function to allocate a new dircache_entry from memory.
88 static struct dircache_entry* allocate_entry(void)
90 struct dircache_entry *next_entry;
92 if (dircache_size > allocated_size - MAX_PATH*2)
94 logf("size limit reached");
95 return NULL;
98 next_entry = (struct dircache_entry *)((char *)dircache_root+dircache_size);
99 #ifdef ROCKBOX_STRICT_ALIGN
100 /* Make sure the entry is long aligned. */
101 if ((long)next_entry & 0x03)
103 dircache_size += 4 - ((long)next_entry & 0x03);
104 next_entry = (struct dircache_entry *)(((long)next_entry & ~0x03) + 0x04);
106 #endif
107 next_entry->name_len = 0;
108 next_entry->d_name = NULL;
109 next_entry->up = NULL;
110 next_entry->down = NULL;
111 next_entry->next = NULL;
113 dircache_size += sizeof(struct dircache_entry);
115 return next_entry;
119 * Internal function to allocate a dircache_entry and set
120 * ->next entry pointers.
122 static struct dircache_entry* dircache_gen_next(struct dircache_entry *ce)
124 struct dircache_entry *next_entry;
126 if ( (next_entry = allocate_entry()) == NULL)
127 return NULL;
128 next_entry->up = ce->up;
129 ce->next = next_entry;
131 return next_entry;
135 * Internal function to allocate a dircache_entry and set
136 * ->down entry pointers.
138 static struct dircache_entry* dircache_gen_down(struct dircache_entry *ce)
140 struct dircache_entry *next_entry;
142 if ( (next_entry = allocate_entry()) == NULL)
143 return NULL;
144 next_entry->up = ce;
145 ce->down = next_entry;
147 return next_entry;
150 /* This will eat ~30 KiB of memory!
151 * We should probably use that as additional reserve buffer in future. */
152 #define MAX_SCAN_DEPTH 16
153 static struct travel_data dir_recursion[MAX_SCAN_DEPTH];
156 * Returns true if there is an event waiting in the queue
157 * that requires the current operation to be aborted.
159 static bool check_event_queue(void)
161 struct queue_event ev;
163 queue_wait_w_tmo(&dircache_queue, &ev, 0);
164 switch (ev.id)
166 case DIRCACHE_STOP:
167 case SYS_USB_CONNECTED:
168 #ifdef HAVE_HOTSWAP
169 case SYS_FS_CHANGED:
170 #endif
171 /* Put the event back into the queue. */
172 queue_post(&dircache_queue, ev.id, ev.data);
173 return true;
176 return false;
180 * Internal function to iterate a path.
182 static int dircache_scan(IF_MV2(int volume,) struct travel_data *td)
184 #ifdef SIMULATOR
185 while ( ( td->entry = readdir_uncached(td->dir) ) )
186 #else
187 while ( (fat_getnext(td->dir, &td->entry) >= 0) && (td->entry.name[0]))
188 #endif
190 #ifdef SIMULATOR
191 if (!strcmp(".", td->entry->d_name) ||
192 !strcmp("..", td->entry->d_name))
194 continue;
197 td->ce->attribute = td->entry->attribute;
198 td->ce->name_len = strlen(td->entry->d_name) + 1;
199 td->ce->d_name = ((char *)dircache_root+dircache_size);
200 td->ce->size = td->entry->size;
201 td->ce->wrtdate = td->entry->wrtdate;
202 td->ce->wrttime = td->entry->wrttime;
203 memcpy(td->ce->d_name, td->entry->d_name, td->ce->name_len);
204 #else
205 if (!strcmp(".", td->entry.name) ||
206 !strcmp("..", td->entry.name))
208 continue;
211 td->ce->attribute = td->entry.attr;
212 td->ce->name_len = strlen(td->entry.name) + 1;
213 td->ce->d_name = ((char *)dircache_root+dircache_size);
214 td->ce->startcluster = td->entry.firstcluster;
215 td->ce->size = td->entry.filesize;
216 td->ce->wrtdate = td->entry.wrtdate;
217 td->ce->wrttime = td->entry.wrttime;
218 memcpy(td->ce->d_name, td->entry.name, td->ce->name_len);
219 #endif
220 dircache_size += td->ce->name_len;
221 entry_count++;
223 #ifdef SIMULATOR
224 if (td->entry->attribute & ATTR_DIRECTORY)
225 #else
226 if (td->entry.attr & FAT_ATTR_DIRECTORY)
227 #endif
230 td->down_entry = dircache_gen_down(td->ce);
231 if (td->down_entry == NULL)
232 return -2;
234 td->pathpos = strlen(dircache_cur_path);
235 strlcpy(&dircache_cur_path[td->pathpos], "/",
236 sizeof(dircache_cur_path) - td->pathpos);
237 #ifdef SIMULATOR
238 strlcpy(&dircache_cur_path[td->pathpos+1], td->entry->d_name,
239 sizeof(dircache_cur_path) - td->pathpos - 1);
241 td->newdir = opendir_uncached(dircache_cur_path);
242 if (td->newdir == NULL)
244 logf("Failed to opendir_uncached(): %s", dircache_cur_path);
245 return -3;
247 #else
248 strlcpy(&dircache_cur_path[td->pathpos+1], td->entry.name,
249 sizeof(dircache_cur_path) - td->pathpos - 1);
251 td->newdir = *td->dir;
252 if (fat_opendir(IF_MV2(volume,) &td->newdir,
253 td->entry.firstcluster, td->dir) < 0 )
255 return -3;
257 #endif
259 td->ce = dircache_gen_next(td->ce);
260 if (td->ce == NULL)
261 return -4;
263 return 1;
266 td->ce->down = NULL;
267 td->ce = dircache_gen_next(td->ce);
268 if (td->ce == NULL)
269 return -5;
271 /* When simulator is used, it's only safe to yield here. */
272 if (thread_enabled)
274 /* Stop if we got an external signal. */
275 if (check_event_queue())
276 return -6;
277 yield();
282 return 0;
285 /**
286 * Recursively scan the hard disk and build the cache.
288 #ifdef SIMULATOR
289 static int dircache_travel(IF_MV2(int volume,) DIR_UNCACHED *dir, struct dircache_entry *ce)
290 #else
291 static int dircache_travel(IF_MV2(int volume,) struct fat_dir *dir, struct dircache_entry *ce)
292 #endif
294 int depth = 0;
295 int result;
297 memset(ce, 0, sizeof(struct dircache_entry));
299 #if defined(HAVE_MULTIVOLUME) && !defined(SIMULATOR)
300 if (volume > 0)
302 ce->d_name = ((char *)dircache_root+dircache_size);
303 snprintf(ce->d_name, VOL_ENUM_POS + 3, VOL_NAMES, volume);
304 ce->name_len = VOL_ENUM_POS + 3;
305 dircache_size += ce->name_len;
306 ce->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
307 ce->size = 0;
308 append_position = dircache_gen_next(ce);
309 ce = dircache_gen_down(ce);
311 #endif
313 dir_recursion[0].dir = dir;
314 dir_recursion[0].ce = ce;
315 dir_recursion[0].first = ce;
317 do {
318 //logf("=> %s", dircache_cur_path);
319 result = dircache_scan(IF_MV2(volume,) &dir_recursion[depth]);
320 switch (result) {
321 case 0: /* Leaving the current directory. */
322 /* Add the standard . and .. entries. */
323 ce = dir_recursion[depth].ce;
324 ce->d_name = ".";
325 ce->name_len = 2;
326 #ifdef SIMULATOR
327 closedir_uncached(dir_recursion[depth].dir);
328 ce->attribute = ATTR_DIRECTORY;
329 #else
330 ce->attribute = FAT_ATTR_DIRECTORY;
331 ce->startcluster = dir_recursion[depth].dir->file.firstcluster;
332 #endif
333 ce->size = 0;
334 ce->down = dir_recursion[depth].first;
336 depth--;
337 if (depth < 0)
338 break ;
340 dircache_cur_path[dir_recursion[depth].pathpos] = '\0';
342 ce = dircache_gen_next(ce);
343 if (ce == NULL)
345 logf("memory allocation error");
346 return -3;
348 #ifdef SIMULATOR
349 ce->attribute = ATTR_DIRECTORY;
350 #else
351 ce->attribute = FAT_ATTR_DIRECTORY;
352 ce->startcluster = dir_recursion[depth].dir->file.firstcluster;
353 #endif
354 ce->d_name = "..";
355 ce->name_len = 3;
356 ce->size = 0;
357 ce->down = dir_recursion[depth].first;
359 break ;
361 case 1: /* Going down in the directory tree. */
362 depth++;
363 if (depth >= MAX_SCAN_DEPTH)
365 logf("Too deep directory structure");
366 return -2;
369 #ifdef SIMULATOR
370 dir_recursion[depth].dir = dir_recursion[depth-1].newdir;
371 #else
372 dir_recursion[depth].dir = &dir_recursion[depth-1].newdir;
373 #endif
374 dir_recursion[depth].first = dir_recursion[depth-1].down_entry;
375 dir_recursion[depth].ce = dir_recursion[depth-1].down_entry;
376 break ;
378 default:
379 logf("Scan failed");
380 logf("-> %s", dircache_cur_path);
381 return -1;
383 } while (depth >= 0) ;
385 return 0;
389 * Internal function to get a pointer to dircache_entry for a given filename.
390 * path: Absolute path to a file or directory.
391 * get_before: Returns the cache pointer before the last valid entry found.
392 * only_directories: Match only filenames which are a directory type.
394 static struct dircache_entry* dircache_get_entry(const char *path,
395 bool get_before, bool only_directories)
397 struct dircache_entry *cache_entry, *before;
398 char namecopy[MAX_PATH*2];
399 char* part;
400 char* end;
402 strlcpy(namecopy, path, sizeof(namecopy));
403 cache_entry = dircache_root;
404 before = NULL;
406 for ( part = strtok_r(namecopy, "/", &end); part;
407 part = strtok_r(NULL, "/", &end)) {
409 /* scan dir for name */
410 while (1)
412 if (cache_entry == NULL)
414 return NULL;
416 else if (cache_entry->name_len == 0)
418 cache_entry = cache_entry->next;
419 continue ;
422 if (!strcasecmp(part, cache_entry->d_name))
424 before = cache_entry;
425 if (cache_entry->down || only_directories)
426 cache_entry = cache_entry->down;
427 break ;
430 cache_entry = cache_entry->next;
434 if (get_before)
435 cache_entry = before;
437 return cache_entry;
440 #ifdef HAVE_EEPROM_SETTINGS
442 * Function to load the internal cache structure from disk to initialize
443 * the dircache really fast and little disk access.
445 int dircache_load(void)
447 struct dircache_maindata maindata;
448 int bytes_read;
449 int fd;
451 if (dircache_initialized)
452 return -1;
454 logf("Loading directory cache");
455 dircache_size = 0;
457 fd = open(DIRCACHE_FILE, O_RDONLY);
458 if (fd < 0)
459 return -2;
461 bytes_read = read(fd, &maindata, sizeof(struct dircache_maindata));
462 if (bytes_read != sizeof(struct dircache_maindata)
463 || maindata.size <= 0)
465 logf("Dircache file header error");
466 close(fd);
467 remove(DIRCACHE_FILE);
468 return -3;
471 dircache_root = buffer_alloc(0);
472 if ((long)maindata.root_entry != (long)dircache_root)
474 logf("Position missmatch");
475 close(fd);
476 remove(DIRCACHE_FILE);
477 return -4;
480 dircache_root = buffer_alloc(maindata.size + DIRCACHE_RESERVE);
481 entry_count = maindata.entry_count;
482 appflags = maindata.appflags;
483 bytes_read = read(fd, dircache_root, MIN(DIRCACHE_LIMIT, maindata.size));
484 close(fd);
485 remove(DIRCACHE_FILE);
487 if (bytes_read != maindata.size)
489 logf("Dircache read failed");
490 return -6;
493 /* Cache successfully loaded. */
494 dircache_size = maindata.size;
495 allocated_size = dircache_size + DIRCACHE_RESERVE;
496 reserve_used = 0;
497 logf("Done, %ld KiB used", dircache_size / 1024);
498 dircache_initialized = true;
499 memset(fd_bindings, 0, sizeof(fd_bindings));
501 return 0;
505 * Function to save the internal cache stucture to disk for fast loading
506 * on boot.
508 int dircache_save(void)
510 struct dircache_maindata maindata;
511 int fd;
512 unsigned long bytes_written;
514 remove(DIRCACHE_FILE);
516 if (!dircache_initialized)
517 return -1;
519 logf("Saving directory cache");
520 fd = open(DIRCACHE_FILE, O_WRONLY | O_CREAT | O_TRUNC);
522 maindata.magic = DIRCACHE_MAGIC;
523 maindata.size = dircache_size;
524 maindata.root_entry = dircache_root;
525 maindata.entry_count = entry_count;
526 maindata.appflags = appflags;
528 /* Save the info structure */
529 bytes_written = write(fd, &maindata, sizeof(struct dircache_maindata));
530 if (bytes_written != sizeof(struct dircache_maindata))
532 close(fd);
533 logf("dircache: write failed #1");
534 return -2;
537 /* Dump whole directory cache to disk */
538 bytes_written = write(fd, dircache_root, dircache_size);
539 close(fd);
540 if (bytes_written != dircache_size)
542 logf("dircache: write failed #2");
543 return -3;
546 return 0;
548 #endif /* #if 0 */
551 * Internal function which scans the disk and creates the dircache structure.
553 static int dircache_do_rebuild(void)
555 #ifdef SIMULATOR
556 DIR_UNCACHED *pdir;
557 #else
558 struct fat_dir dir, *pdir;
559 #endif
560 unsigned int start_tick;
561 int i;
563 /* Measure how long it takes build the cache. */
564 start_tick = current_tick;
565 dircache_initializing = true;
566 appflags = 0;
567 entry_count = 0;
569 memset(dircache_cur_path, 0, sizeof(dircache_cur_path));
570 dircache_size = sizeof(struct dircache_entry);
572 #ifdef HAVE_MULTIVOLUME
573 append_position = dircache_root;
575 for (i = NUM_VOLUMES; i >= 0; i--)
577 if (fat_ismounted(i))
579 #endif
580 #ifdef SIMULATOR
581 pdir = opendir_uncached("/");
582 if (pdir == NULL)
584 logf("Failed to open rootdir");
585 dircache_initializing = false;
586 return -3;
588 #else
589 #ifdef HAVE_MULTIVOLUME
590 if ( fat_opendir(IF_MV2(i,) &dir, 0, NULL) < 0 ) {
591 #else
592 if ( fat_opendir(IF_MV2(0,) &dir, 0, NULL) < 0 ) {
593 #endif /* HAVE_MULTIVOLUME */
594 logf("Failed opening root dir");
595 dircache_initializing = false;
596 return -3;
598 pdir = &dir;
599 #endif
600 cpu_boost(true);
601 #ifdef HAVE_MULTIVOLUME
602 if (dircache_travel(IF_MV2(i,) pdir, append_position) < 0)
603 #else
604 if (dircache_travel(IF_MV2(0,) pdir, dircache_root) < 0)
605 #endif /* HAVE_MULTIVOLUME */
607 logf("dircache_travel failed");
608 cpu_boost(false);
609 dircache_size = 0;
610 dircache_initializing = false;
611 return -2;
613 cpu_boost(false);
614 #ifdef HAVE_MULTIVOLUME
617 #endif
619 logf("Done, %ld KiB used", dircache_size / 1024);
621 dircache_initialized = true;
622 dircache_initializing = false;
623 cache_build_ticks = current_tick - start_tick;
625 /* Initialized fd bindings. */
626 memset(fd_bindings, 0, sizeof(fd_bindings));
627 for (i = 0; i < fdbind_idx; i++)
628 dircache_bind(fdbind_cache[i].fd, fdbind_cache[i].path);
629 fdbind_idx = 0;
631 if (thread_enabled)
633 if (allocated_size - dircache_size < DIRCACHE_RESERVE)
634 reserve_used = DIRCACHE_RESERVE - (allocated_size - dircache_size);
636 else
638 /* We have to long align the audiobuf to keep the buffer access fast. */
639 audiobuf += (long)((dircache_size & ~0x03) + 0x04);
640 audiobuf += DIRCACHE_RESERVE;
641 allocated_size = dircache_size + DIRCACHE_RESERVE;
642 reserve_used = 0;
645 return 1;
649 * Internal thread that controls transparent cache building.
651 static void dircache_thread(void)
653 struct queue_event ev;
655 while (1)
657 queue_wait(&dircache_queue, &ev);
659 switch (ev.id)
661 #ifdef HAVE_HOTSWAP
662 case SYS_FS_CHANGED:
663 if (!dircache_initialized)
664 break;
665 dircache_initialized = false;
666 #endif
667 case DIRCACHE_BUILD:
668 thread_enabled = true;
669 dircache_do_rebuild();
670 thread_enabled = false;
671 break ;
673 case DIRCACHE_STOP:
674 logf("Stopped the rebuilding.");
675 dircache_initialized = false;
676 break ;
678 #ifndef SIMULATOR
679 case SYS_USB_CONNECTED:
680 usb_acknowledge(SYS_USB_CONNECTED_ACK);
681 usb_wait_for_disconnect(&dircache_queue);
682 break ;
683 #endif
689 * Start scanning the disk to build the dircache.
690 * Either transparent or non-transparent build method is used.
692 int dircache_build(int last_size)
694 if (dircache_initialized || thread_enabled)
695 return -3;
697 logf("Building directory cache");
698 remove(DIRCACHE_FILE);
700 /* Background build, dircache has been previously allocated */
701 if (dircache_size > 0)
703 thread_enabled = true;
704 dircache_initializing = true;
705 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
706 return 2;
709 if (last_size > DIRCACHE_RESERVE && last_size < DIRCACHE_LIMIT )
711 allocated_size = last_size + DIRCACHE_RESERVE;
712 dircache_root = buffer_alloc(allocated_size);
713 thread_enabled = true;
715 /* Start a transparent rebuild. */
716 queue_post(&dircache_queue, DIRCACHE_BUILD, 0);
717 return 3;
720 dircache_root = (struct dircache_entry *)(((long)audiobuf & ~0x03) + 0x04);
722 /* Start a non-transparent rebuild. */
723 return dircache_do_rebuild();
727 * Steal the allocated dircache buffer and disable dircache.
729 void* dircache_steal_buffer(long *size)
731 dircache_disable();
732 if (dircache_size == 0)
734 *size = 0;
735 return NULL;
738 *size = dircache_size + (DIRCACHE_RESERVE-reserve_used);
740 return dircache_root;
744 * Main initialization function that must be called before any other
745 * operations within the dircache.
747 void dircache_init(void)
749 int i;
751 dircache_initialized = false;
752 dircache_initializing = false;
754 memset(opendirs, 0, sizeof(opendirs));
755 for (i = 0; i < MAX_OPEN_DIRS; i++)
757 opendirs[i].secondary_entry.d_name = buffer_alloc(MAX_PATH);
760 queue_init(&dircache_queue, true);
761 create_thread(dircache_thread, dircache_stack,
762 sizeof(dircache_stack), 0, dircache_thread_name
763 IF_PRIO(, PRIORITY_BACKGROUND)
764 IF_COP(, CPU));
768 * Returns true if dircache has been initialized and is ready to be used.
770 bool dircache_is_enabled(void)
772 return dircache_initialized;
776 * Returns true if dircache is being initialized.
778 bool dircache_is_initializing(void)
780 return dircache_initializing || thread_enabled;
784 * Set application flags used to determine if dircache is still intact.
786 void dircache_set_appflag(long mask)
788 appflags |= mask;
792 * Get application flags used to determine if dircache is still intact.
794 bool dircache_get_appflag(long mask)
796 return dircache_is_enabled() && (appflags & mask);
800 * Returns the current number of entries (directories and files) in the cache.
802 int dircache_get_entry_count(void)
804 return entry_count;
808 * Returns the allocated space for dircache (without reserve space).
810 int dircache_get_cache_size(void)
812 return dircache_is_enabled() ? dircache_size : 0;
816 * Returns how many bytes of the reserve allocation for live cache
817 * updates have been used.
819 int dircache_get_reserve_used(void)
821 return dircache_is_enabled() ? reserve_used : 0;
825 * Returns the time in kernel ticks that took to build the cache.
827 int dircache_get_build_ticks(void)
829 return dircache_is_enabled() ? cache_build_ticks : 0;
833 * Disables the dircache. Usually called on shutdown or when
834 * accepting a usb connection.
836 void dircache_disable(void)
838 int i;
839 bool cache_in_use;
841 if (thread_enabled)
842 queue_post(&dircache_queue, DIRCACHE_STOP, 0);
844 while (thread_enabled)
845 sleep(1);
846 dircache_initialized = false;
848 logf("Waiting for cached dirs to release");
849 do {
850 cache_in_use = false;
851 for (i = 0; i < MAX_OPEN_DIRS; i++) {
852 if (!opendirs[i].regulardir && opendirs[i].busy)
854 cache_in_use = true;
855 sleep(1);
856 break ;
859 } while (cache_in_use) ;
861 logf("Cache released");
862 entry_count = 0;
866 * Usermode function to return dircache_entry pointer to the given path.
868 const struct dircache_entry *dircache_get_entry_ptr(const char *filename)
870 if (!dircache_initialized || filename == NULL)
871 return NULL;
873 return dircache_get_entry(filename, false, false);
877 * Function to copy the full absolute path from dircache to the given buffer
878 * using the given dircache_entry pointer.
880 void dircache_copy_path(const struct dircache_entry *entry, char *buf, int size)
882 const struct dircache_entry *down[MAX_SCAN_DEPTH];
883 int depth = 0;
885 if (size <= 0)
886 return ;
888 buf[0] = '\0';
890 if (entry == NULL)
891 return ;
893 do {
894 down[depth] = entry;
895 entry = entry->up;
896 depth++;
897 } while (entry != NULL && depth < MAX_SCAN_DEPTH);
899 while (--depth >= 0)
901 snprintf(buf, size, "/%s", down[depth]->d_name);
902 buf += down[depth]->name_len; /* '/' + d_name */
903 size -= down[depth]->name_len;
904 if (size <= 0)
905 break ;
909 /* --- Directory cache live updating functions --- */
910 static int block_until_ready(void)
912 /* Block until dircache has been built. */
913 while (!dircache_initialized && dircache_is_initializing())
914 sleep(1);
916 if (!dircache_initialized)
917 return -1;
919 return 0;
922 static struct dircache_entry* dircache_new_entry(const char *path, int attribute)
924 struct dircache_entry *entry;
925 char basedir[MAX_PATH*2];
926 char *new;
927 long last_cache_size = dircache_size;
929 strlcpy(basedir, path, sizeof(basedir));
930 new = strrchr(basedir, '/');
931 if (new == NULL)
933 logf("error occurred");
934 dircache_initialized = false;
935 return NULL;
938 *new = '\0';
939 new++;
941 entry = dircache_get_entry(basedir, false, true);
942 if (entry == NULL)
944 logf("basedir not found!");
945 logf("%s", basedir);
946 dircache_initialized = false;
947 return NULL;
950 if (reserve_used + 2*sizeof(struct dircache_entry) + strlen(new)+1
951 >= DIRCACHE_RESERVE)
953 logf("not enough space");
954 dircache_initialized = false;
955 return NULL;
958 while (entry->next != NULL)
959 entry = entry->next;
961 if (entry->name_len > 0)
962 entry = dircache_gen_next(entry);
964 if (entry == NULL)
966 dircache_initialized = false;
967 return NULL;
970 entry->attribute = attribute;
971 entry->name_len = MIN(254, strlen(new)) + 1;
972 entry->d_name = ((char *)dircache_root+dircache_size);
973 entry->startcluster = 0;
974 entry->wrtdate = 0;
975 entry->wrttime = 0;
976 entry->size = 0;
977 memcpy(entry->d_name, new, entry->name_len);
978 dircache_size += entry->name_len;
980 if (attribute & ATTR_DIRECTORY)
982 logf("gen_down");
983 dircache_gen_down(entry);
986 reserve_used += dircache_size - last_cache_size;
988 return entry;
991 void dircache_bind(int fd, const char *path)
993 struct dircache_entry *entry;
995 /* Queue requests until dircache has been built. */
996 if (!dircache_initialized && dircache_is_initializing())
998 if (fdbind_idx >= MAX_PENDING_BINDINGS)
999 return ;
1000 strlcpy(fdbind_cache[fdbind_idx].path, path,
1001 sizeof(fdbind_cache[fdbind_idx].path));
1002 fdbind_cache[fdbind_idx].fd = fd;
1003 fdbind_idx++;
1004 return ;
1007 if (!dircache_initialized)
1008 return ;
1010 logf("bind: %d/%s", fd, path);
1011 entry = dircache_get_entry(path, false, false);
1012 if (entry == NULL)
1014 logf("not found!");
1015 dircache_initialized = false;
1016 return ;
1019 fd_bindings[fd] = entry;
1022 void dircache_update_filesize(int fd, long newsize, long startcluster)
1024 if (!dircache_initialized || fd < 0)
1025 return ;
1027 if (fd_bindings[fd] == NULL)
1029 logf("dircache fd access error");
1030 dircache_initialized = false;
1031 return ;
1034 fd_bindings[fd]->size = newsize;
1035 fd_bindings[fd]->startcluster = startcluster;
1037 void dircache_update_filetime(int fd)
1039 #if CONFIG_RTC == 0
1040 (void)fd;
1041 #else
1042 short year;
1043 struct tm *now = get_time();
1044 if (!dircache_initialized || fd < 0)
1045 return ;
1047 if (fd_bindings[fd] == NULL)
1049 logf("dircache fd access error");
1050 dircache_initialized = false;
1051 return ;
1053 year = now->tm_year+1900-1980;
1054 fd_bindings[fd]->wrtdate = (((year)&0x7f)<<9) |
1055 (((now->tm_mon+1)&0xf)<<5) |
1056 (((now->tm_mday)&0x1f));
1057 fd_bindings[fd]->wrttime = (((now->tm_hour)&0x1f)<<11) |
1058 (((now->tm_min)&0x3f)<<5) |
1059 (((now->tm_sec/2)&0x1f));
1060 #endif
1063 void dircache_mkdir(const char *path)
1064 { /* Test ok. */
1065 if (block_until_ready())
1066 return ;
1068 logf("mkdir: %s", path);
1069 dircache_new_entry(path, ATTR_DIRECTORY);
1072 void dircache_rmdir(const char *path)
1073 { /* Test ok. */
1074 struct dircache_entry *entry;
1076 if (block_until_ready())
1077 return ;
1079 logf("rmdir: %s", path);
1080 entry = dircache_get_entry(path, true, true);
1081 if (entry == NULL)
1083 logf("not found!");
1084 dircache_initialized = false;
1085 return ;
1088 entry->down = NULL;
1089 entry->name_len = 0;
1092 /* Remove a file from cache */
1093 void dircache_remove(const char *name)
1094 { /* Test ok. */
1095 struct dircache_entry *entry;
1097 if (block_until_ready())
1098 return ;
1100 logf("remove: %s", name);
1102 entry = dircache_get_entry(name, false, false);
1104 if (entry == NULL)
1106 logf("not found!");
1107 dircache_initialized = false;
1108 return ;
1111 entry->name_len = 0;
1114 void dircache_rename(const char *oldpath, const char *newpath)
1115 { /* Test ok. */
1116 struct dircache_entry *entry, *newentry;
1117 struct dircache_entry oldentry;
1118 char absolute_path[MAX_PATH*2];
1119 char *p;
1121 if (block_until_ready())
1122 return ;
1124 logf("rename: %s->%s", oldpath, newpath);
1126 entry = dircache_get_entry(oldpath, true, false);
1127 if (entry == NULL)
1129 logf("not found!");
1130 dircache_initialized = false;
1131 return ;
1134 /* Delete the old entry. */
1135 entry->name_len = 0;
1137 /** If we rename the same filename twice in a row, we need to
1138 * save the data, because the entry will be re-used. */
1139 oldentry = *entry;
1141 /* Generate the absolute path for destination if necessary. */
1142 if (newpath[0] != '/')
1144 strlcpy(absolute_path, oldpath, sizeof(absolute_path));
1145 p = strrchr(absolute_path, '/');
1146 if (!p)
1148 logf("Invalid path");
1149 dircache_initialized = false;
1150 return ;
1153 *p = '\0';
1154 strlcpy(p, absolute_path, sizeof(absolute_path)-strlen(p));
1155 newpath = absolute_path;
1158 newentry = dircache_new_entry(newpath, entry->attribute);
1159 if (newentry == NULL)
1161 dircache_initialized = false;
1162 return ;
1165 newentry->down = oldentry.down;
1166 newentry->size = oldentry.size;
1167 newentry->startcluster = oldentry.startcluster;
1168 newentry->wrttime = oldentry.wrttime;
1169 newentry->wrtdate = oldentry.wrtdate;
1172 void dircache_add_file(const char *path, long startcluster)
1174 struct dircache_entry *entry;
1176 if (block_until_ready())
1177 return ;
1179 logf("add file: %s", path);
1180 entry = dircache_new_entry(path, 0);
1181 if (entry == NULL)
1182 return ;
1184 entry->startcluster = startcluster;
1187 DIR_CACHED* opendir_cached(const char* name)
1189 struct dircache_entry *cache_entry;
1190 int dd;
1191 DIR_CACHED* pdir = opendirs;
1193 if ( name[0] != '/' )
1195 DEBUGF("Only absolute paths supported right now\n");
1196 return NULL;
1199 /* find a free dir descriptor */
1200 for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
1201 if ( !pdir->busy )
1202 break;
1204 if ( dd == MAX_OPEN_DIRS )
1206 DEBUGF("Too many dirs open\n");
1207 errno = EMFILE;
1208 return NULL;
1211 if (!dircache_initialized)
1213 pdir->regulardir = opendir_uncached(name);
1214 if (!pdir->regulardir)
1215 return NULL;
1217 pdir->busy = true;
1218 return pdir;
1221 pdir->busy = true;
1222 pdir->regulardir = NULL;
1223 cache_entry = dircache_get_entry(name, false, true);
1224 pdir->entry = cache_entry;
1226 if (cache_entry == NULL)
1228 pdir->busy = false;
1229 return NULL;
1232 return pdir;
1235 struct dircache_entry* readdir_cached(DIR_CACHED* dir)
1237 struct dirent_uncached *regentry;
1238 struct dircache_entry *ce;
1240 if (!dir->busy)
1241 return NULL;
1243 if (dir->regulardir != NULL)
1245 regentry = readdir_uncached(dir->regulardir);
1246 if (regentry == NULL)
1247 return NULL;
1249 strlcpy(dir->secondary_entry.d_name, regentry->d_name, MAX_PATH);
1250 dir->secondary_entry.size = regentry->size;
1251 dir->secondary_entry.startcluster = regentry->startcluster;
1252 dir->secondary_entry.attribute = regentry->attribute;
1253 dir->secondary_entry.wrttime = regentry->wrttime;
1254 dir->secondary_entry.wrtdate = regentry->wrtdate;
1255 dir->secondary_entry.next = NULL;
1257 return &dir->secondary_entry;
1260 do {
1261 if (dir->entry == NULL)
1262 return NULL;
1264 ce = dir->entry;
1265 if (ce->name_len == 0)
1266 dir->entry = ce->next;
1267 } while (ce->name_len == 0) ;
1269 dir->entry = ce->next;
1271 strlcpy(dir->secondary_entry.d_name, ce->d_name, MAX_PATH);
1272 /* Can't do `dir->secondary_entry = *ce`
1273 because that modifies the d_name pointer. */
1274 dir->secondary_entry.size = ce->size;
1275 dir->secondary_entry.startcluster = ce->startcluster;
1276 dir->secondary_entry.attribute = ce->attribute;
1277 dir->secondary_entry.wrttime = ce->wrttime;
1278 dir->secondary_entry.wrtdate = ce->wrtdate;
1279 dir->secondary_entry.next = NULL;
1280 dir->internal_entry = ce;
1282 //logf("-> %s", ce->name);
1283 return &dir->secondary_entry;
1286 int closedir_cached(DIR_CACHED* dir)
1288 if (!dir->busy)
1289 return -1;
1291 dir->busy=false;
1292 if (dir->regulardir != NULL)
1293 return closedir_uncached(dir->regulardir);
1295 return 0;
1298 int mkdir_cached(const char *name)
1300 int rc=mkdir_uncached(name);
1301 if (rc >= 0)
1302 dircache_mkdir(name);
1303 return(rc);
1306 int rmdir_cached(const char* name)
1308 int rc=rmdir_uncached(name);
1309 if(rc>=0)
1310 dircache_rmdir(name);
1311 return(rc);