if a proxy is set, pre-fill the proxy setting dialog with the old value.
[Rockbox.git] / apps / tagcache.c
blobd82149ad3bdef00cac00efc0e42da0a239890a8e
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 by Miika Pekkarinen
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 * TagCache API
23 * ----------x---------x------------------x-----
24 * | | | External
25 * +---------------x-------+ | TagCache | Libraries
26 * | Modification routines | | Core |
27 * +-x---------x-----------+ | |
28 * | (R/W) | | | |
29 * | +------x-------------x-+ +-------------x-----+ |
30 * | | x==x Filters & clauses | |
31 * | | Search routines | +-------------------+ |
32 * | | x============================x DirCache
33 * | +-x--------------------+ | (optional)
34 * | | (R) |
35 * | | +-------------------------------+ +---------+ |
36 * | | | DB Commit (sort,unique,index) | | | |
37 * | | +-x--------------------------x--+ | Control | |
38 * | | | (R/W) | (R) | Thread | |
39 * | | | +----------------------+ | | | |
40 * | | | | TagCache DB Builder | | +---------+ |
41 * | | | +-x-------------x------+ | |
42 * | | | | (R) | (W) | |
43 * | | | | +--x--------x---------+ |
44 * | | | | | Temporary Commit DB | |
45 * | | | | +---------------------+ |
46 * +-x----x-------x--+ |
47 * | TagCache RAM DB x==\(W) +-----------------+ |
48 * +-----------------+ \===x | |
49 * | | | | (R) | Ram DB Loader x============x DirCache
50 * +-x----x---x---x---+ /==x | | (optional)
51 * | Tagcache Disk DB x==/ +-----------------+ |
52 * +------------------+ |
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <ctype.h>
59 #include "config.h"
60 #include "thread.h"
61 #include "kernel.h"
62 #include "system.h"
63 #include "logf.h"
64 #include "string.h"
65 #include "usb.h"
66 #include "metadata.h"
67 #include "id3.h"
68 #include "tagcache.h"
69 #include "buffer.h"
70 #include "crc32.h"
71 #include "misc.h"
72 #include "settings.h"
73 #include "dircache.h"
74 #include "structec.h"
75 #ifndef __PCTOOL__
76 #include "atoi.h"
77 #include "splash.h"
78 #include "lang.h"
79 #include "eeprom_settings.h"
80 #endif
82 #ifdef __PCTOOL__
83 #define yield() do { } while(0)
84 #define sim_sleep(timeout) do { } while(0)
85 #define do_timed_yield() do { } while(0)
86 #endif
89 #ifndef __PCTOOL__
90 /* Tag Cache thread. */
91 static struct event_queue tagcache_queue;
92 static long tagcache_stack[(DEFAULT_STACK_SIZE + 0x4000)/sizeof(long)];
93 static const char tagcache_thread_name[] = "tagcache";
94 #endif
96 #define UNTAGGED "<Untagged>"
98 /* Previous path when scanning directory tree recursively. */
99 static char curpath[TAG_MAXLEN+32];
100 static long curpath_size = sizeof(curpath);
102 /* Used when removing duplicates. */
103 static char *tempbuf; /* Allocated when needed. */
104 static long tempbufidx; /* Current location in buffer. */
105 static long tempbuf_size; /* Buffer size (TEMPBUF_SIZE). */
106 static long tempbuf_left; /* Buffer space left. */
107 static long tempbuf_pos;
109 /* Tags we want to get sorted (loaded to the tempbuf). */
110 static const int sorted_tags[] = { tag_artist, tag_album, tag_genre,
111 tag_composer, tag_comment, tag_albumartist, tag_title };
113 /* Uniqued tags (we can use these tags with filters and conditional clauses). */
114 static const int unique_tags[] = { tag_artist, tag_album, tag_genre,
115 tag_composer, tag_comment, tag_albumartist };
117 /* Numeric tags (we can use these tags with conditional clauses). */
118 static const int numeric_tags[] = { tag_year, tag_tracknumber, tag_length,
119 tag_bitrate, tag_playcount, tag_rating, tag_playtime, tag_lastplayed, tag_commitid,
120 tag_virt_length_min, tag_virt_length_sec,
121 tag_virt_playtime_min, tag_virt_playtime_sec,
122 tag_virt_entryage, tag_virt_autoscore };
124 /* String presentation of the tags defined in tagcache.h. Must be in correct order! */
125 static const char *tags_str[] = { "artist", "album", "genre", "title",
126 "filename", "composer", "comment", "albumartist", "year", "tracknumber",
127 "bitrate", "length", "playcount", "rating", "playtime", "lastplayed", "commitid" };
129 /* Status information of the tagcache. */
130 static struct tagcache_stat tc_stat;
132 /* Queue commands. */
133 enum tagcache_queue {
134 Q_STOP_SCAN = 0,
135 Q_START_SCAN,
136 Q_IMPORT_CHANGELOG,
137 Q_UPDATE,
138 Q_REBUILD,
142 /* Tag database structures. */
144 /* Variable-length tag entry in tag files. */
145 struct tagfile_entry {
146 short tag_length; /* Length of the data in bytes including '\0' */
147 short idx_id; /* Corresponding entry location in index file of not unique tags */
148 char tag_data[0]; /* Begin of the tag data */
151 /* Fixed-size tag entry in master db index. */
152 struct index_entry {
153 long tag_seek[TAG_COUNT]; /* Location of tag data or numeric tag data */
154 long flag; /* Status flags */
157 /* Header is the same in every file. */
158 struct tagcache_header {
159 long magic; /* Header version number */
160 long datasize; /* Data size in bytes */
161 long entry_count; /* Number of entries in this file */
164 struct master_header {
165 struct tagcache_header tch;
166 long serial; /* Increasing counting number */
167 long commitid; /* Number of commits so far */
168 long dirty;
171 /* For the endianess correction */
172 static const char *tagfile_entry_ec = "ss";
173 static const char *index_entry_ec = "llllllllllllllllll"; /* (1 + TAG_COUNT) * l */
174 static const char *tagcache_header_ec = "lll";
175 static const char *master_header_ec = "llllll";
177 static struct master_header current_tcmh;
179 #ifdef HAVE_TC_RAMCACHE
180 /* Header is created when loading database to ram. */
181 struct ramcache_header {
182 struct master_header h; /* Header from the master index */
183 struct index_entry *indices; /* Master index file content */
184 char *tags[TAG_COUNT]; /* Tag file content (not including filename tag) */
185 int entry_count[TAG_COUNT]; /* Number of entries in the indices. */
188 # ifdef HAVE_EEPROM_SETTINGS
189 struct statefile_header {
190 struct ramcache_header *hdr;
191 struct tagcache_stat tc_stat;
193 # endif
195 /* Pointer to allocated ramcache_header */
196 static struct ramcache_header *hdr;
197 #endif
199 /**
200 * Full tag entries stored in a temporary file waiting
201 * for commit to the cache. */
202 struct temp_file_entry {
203 long tag_offset[TAG_COUNT];
204 short tag_length[TAG_COUNT];
205 long flag;
207 long data_length;
210 struct tempbuf_id_list {
211 long id;
212 struct tempbuf_id_list *next;
215 struct tempbuf_searchidx {
216 long idx_id;
217 char *str;
218 int seek;
219 struct tempbuf_id_list idlist;
222 /* Lookup buffer for fixing messed up index while after sorting. */
223 static long commit_entry_count;
224 static long lookup_buffer_depth;
225 static struct tempbuf_searchidx **lookup;
227 /* Used when building the temporary file. */
228 static int cachefd = -1, filenametag_fd;
229 static int total_entry_count = 0;
230 static int data_size = 0;
231 static int processed_dir_count;
233 /* Thread safe locking */
234 static volatile int write_lock;
235 static volatile int read_lock;
237 const char* tagcache_tag_to_str(int tag)
239 return tags_str[tag];
242 bool tagcache_is_numeric_tag(int type)
244 int i;
246 for (i = 0; i < (int)(sizeof(numeric_tags)/sizeof(numeric_tags[0])); i++)
248 if (type == numeric_tags[i])
249 return true;
252 return false;
255 bool tagcache_is_unique_tag(int type)
257 int i;
259 for (i = 0; i < (int)(sizeof(unique_tags)/sizeof(unique_tags[0])); i++)
261 if (type == unique_tags[i])
262 return true;
265 return false;
268 bool tagcache_is_sorted_tag(int type)
270 int i;
272 for (i = 0; i < (int)(sizeof(sorted_tags)/sizeof(sorted_tags[0])); i++)
274 if (type == sorted_tags[i])
275 return true;
278 return false;
281 static int open_tag_fd(struct tagcache_header *hdr, int tag, bool write)
283 int fd;
284 char buf[MAX_PATH];
285 int rc;
287 if (tagcache_is_numeric_tag(tag) || tag < 0 || tag >= TAG_COUNT)
288 return -1;
290 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag);
292 fd = open(buf, write ? O_RDWR : O_RDONLY);
293 if (fd < 0)
295 logf("tag file open failed: tag=%d write=%d file=%s", tag, write, buf);
296 tc_stat.ready = false;
297 return fd;
300 /* Check the header. */
301 rc = ecread(fd, hdr, 1, tagcache_header_ec, tc_stat.econ);
302 if (hdr->magic != TAGCACHE_MAGIC || rc != sizeof(struct tagcache_header))
304 logf("header error");
305 tc_stat.ready = false;
306 close(fd);
307 return -2;
310 return fd;
313 #ifndef __PCTOOL__
314 static bool do_timed_yield(void)
316 /* Sorting can lock up for quite a while, so yield occasionally */
317 static long wakeup_tick = 0;
318 if (current_tick >= wakeup_tick)
320 wakeup_tick = current_tick + (HZ/4);
321 yield();
322 return true;
324 return false;
326 #endif
328 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
329 static long find_entry_ram(const char *filename,
330 const struct dircache_entry *dc)
332 static long last_pos = 0;
333 int i;
335 /* Check if we tagcache is loaded into ram. */
336 if (!tc_stat.ramcache)
337 return -1;
339 if (dc == NULL)
340 dc = dircache_get_entry_ptr(filename);
342 if (dc == NULL)
344 logf("tagcache: file not found.");
345 return -1;
348 try_again:
350 if (last_pos > 0)
351 i = last_pos;
352 else
353 i = 0;
355 for (; i < hdr->h.tch.entry_count; i++)
357 if (hdr->indices[i].tag_seek[tag_filename] == (long)dc)
359 last_pos = MAX(0, i - 3);
360 return i;
363 do_timed_yield();
366 if (last_pos > 0)
368 last_pos = 0;
369 goto try_again;
372 return -1;
374 #endif
376 static long find_entry_disk(const char *filename)
378 struct tagcache_header tch;
379 static long last_pos = -1;
380 long pos_history[POS_HISTORY_COUNT];
381 long pos_history_idx = 0;
382 bool found = false;
383 struct tagfile_entry tfe;
384 int fd;
385 char buf[TAG_MAXLEN+32];
386 int i;
387 int pos = -1;
389 if (!tc_stat.ready)
390 return -2;
392 fd = filenametag_fd;
393 if (fd < 0)
395 last_pos = -1;
396 if ( (fd = open_tag_fd(&tch, tag_filename, false)) < 0)
397 return -1;
400 check_again:
402 if (last_pos > 0)
403 lseek(fd, last_pos, SEEK_SET);
404 else
405 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
407 while (true)
409 pos = lseek(fd, 0, SEEK_CUR);
410 for (i = pos_history_idx-1; i >= 0; i--)
411 pos_history[i+1] = pos_history[i];
412 pos_history[0] = pos;
414 if (ecread(fd, &tfe, 1, tagfile_entry_ec, tc_stat.econ)
415 != sizeof(struct tagfile_entry))
417 break ;
420 if (tfe.tag_length >= (long)sizeof(buf))
422 logf("too long tag #1");
423 close(fd);
424 last_pos = -1;
425 return -2;
428 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
430 logf("read error #2");
431 close(fd);
432 last_pos = -1;
433 return -3;
436 if (!strcasecmp(filename, buf))
438 last_pos = pos_history[pos_history_idx];
439 found = true;
440 break ;
443 if (pos_history_idx < POS_HISTORY_COUNT - 1)
444 pos_history_idx++;
447 /* Not found? */
448 if (!found)
450 if (last_pos > 0)
452 last_pos = -1;
453 logf("seek again");
454 goto check_again;
457 if (fd != filenametag_fd)
458 close(fd);
459 return -4;
462 if (fd != filenametag_fd)
463 close(fd);
465 return tfe.idx_id;
468 static int find_index(const char *filename)
470 long idx_id = -1;
472 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
473 if (tc_stat.ramcache && dircache_is_enabled())
474 idx_id = find_entry_ram(filename, NULL);
475 #endif
477 if (idx_id < 0)
478 idx_id = find_entry_disk(filename);
480 return idx_id;
483 bool tagcache_find_index(struct tagcache_search *tcs, const char *filename)
485 int idx_id;
487 if (!tc_stat.ready)
488 return false;
490 idx_id = find_index(filename);
491 if (idx_id < 0)
492 return false;
494 if (!tagcache_search(tcs, tag_filename))
495 return false;
497 tcs->entry_count = 0;
498 tcs->idx_id = idx_id;
500 return true;
503 static bool get_index(int masterfd, int idxid,
504 struct index_entry *idx, bool use_ram)
506 if (idxid < 0)
508 logf("Incorrect idxid: %d", idxid);
509 return false;
512 #ifdef HAVE_TC_RAMCACHE
513 if (tc_stat.ramcache && use_ram)
515 if (hdr->indices[idxid].flag & FLAG_DELETED)
516 return false;
518 memcpy(idx, &hdr->indices[idxid], sizeof(struct index_entry));
519 return true;
521 #else
522 (void)use_ram;
523 #endif
525 lseek(masterfd, idxid * sizeof(struct index_entry)
526 + sizeof(struct master_header), SEEK_SET);
527 if (ecread(masterfd, idx, 1, index_entry_ec, tc_stat.econ)
528 != sizeof(struct index_entry))
530 logf("read error #3");
531 return false;
534 if (idx->flag & FLAG_DELETED)
535 return false;
537 return true;
540 static bool write_index(int masterfd, int idxid, struct index_entry *idx)
542 /* We need to exclude all memory only flags & tags when writing to disk. */
543 if (idx->flag & FLAG_DIRCACHE)
545 logf("memory only flags!");
546 return false;
549 #ifdef HAVE_TC_RAMCACHE
550 /* Only update numeric data. Writing the whole index to RAM by memcpy
551 * destroys dircache pointers!
553 if (tc_stat.ramcache)
555 int tag;
556 struct index_entry *idx_ram = &hdr->indices[idxid];
558 for (tag = 0; tag < TAG_COUNT; tag++)
560 if (tagcache_is_numeric_tag(tag))
562 idx_ram->tag_seek[tag] = idx->tag_seek[tag];
566 /* Don't touch the dircache flag. */
567 idx_ram->flag = idx->flag | (idx_ram->flag & FLAG_DIRCACHE);
569 #endif
571 lseek(masterfd, idxid * sizeof(struct index_entry)
572 + sizeof(struct master_header), SEEK_SET);
573 if (ecwrite(masterfd, idx, 1, index_entry_ec, tc_stat.econ)
574 != sizeof(struct index_entry))
576 logf("write error #3");
577 logf("idxid: %d", idxid);
578 return false;
581 return true;
584 static bool open_files(struct tagcache_search *tcs, int tag)
586 if (tcs->idxfd[tag] < 0)
588 char fn[MAX_PATH];
590 snprintf(fn, sizeof fn, TAGCACHE_FILE_INDEX, tag);
591 tcs->idxfd[tag] = open(fn, O_RDONLY);
594 if (tcs->idxfd[tag] < 0)
596 logf("File not open!");
597 return false;
600 return true;
603 static bool retrieve(struct tagcache_search *tcs, struct index_entry *idx,
604 int tag, char *buf, long size)
606 struct tagfile_entry tfe;
607 long seek;
609 *buf = '\0';
611 if (tagcache_is_numeric_tag(tag))
612 return false;
614 seek = idx->tag_seek[tag];
615 if (seek < 0)
617 logf("Retrieve failed");
618 return false;
621 #ifdef HAVE_TC_RAMCACHE
622 if (tcs->ramsearch)
624 struct tagfile_entry *ep;
626 # ifdef HAVE_DIRCACHE
627 if (tag == tag_filename && idx->flag & FLAG_DIRCACHE)
629 dircache_copy_path((struct dircache_entry *)seek,
630 buf, size);
631 return true;
633 else
634 # endif
635 if (tag != tag_filename)
637 ep = (struct tagfile_entry *)&hdr->tags[tag][seek];
638 strncpy(buf, ep->tag_data, size-1);
640 return true;
643 #endif
645 if (!open_files(tcs, tag))
646 return false;
648 lseek(tcs->idxfd[tag], seek, SEEK_SET);
649 if (ecread(tcs->idxfd[tag], &tfe, 1, tagfile_entry_ec, tc_stat.econ)
650 != sizeof(struct tagfile_entry))
652 logf("read error #5");
653 return false;
656 if (tfe.tag_length >= size)
658 logf("too small buffer");
659 return false;
662 if (read(tcs->idxfd[tag], buf, tfe.tag_length) !=
663 tfe.tag_length)
665 logf("read error #6");
666 return false;
669 buf[tfe.tag_length] = '\0';
671 return true;
674 static long check_virtual_tags(int tag, const struct index_entry *idx)
676 long data = 0;
678 switch (tag)
680 case tag_virt_length_sec:
681 data = (idx->tag_seek[tag_length]/1000) % 60;
682 break;
684 case tag_virt_length_min:
685 data = (idx->tag_seek[tag_length]/1000) / 60;
686 break;
688 case tag_virt_playtime_sec:
689 data = (idx->tag_seek[tag_playtime]/1000) % 60;
690 break;
692 case tag_virt_playtime_min:
693 data = (idx->tag_seek[tag_playtime]/1000) / 60;
694 break;
696 case tag_virt_autoscore:
697 if (idx->tag_seek[tag_length] == 0
698 || idx->tag_seek[tag_playcount] == 0)
700 data = 0;
702 else
704 data = 100 * idx->tag_seek[tag_playtime]
705 / idx->tag_seek[tag_length]
706 / idx->tag_seek[tag_playcount];
708 break;
710 /* How many commits before the file has been added to the DB. */
711 case tag_virt_entryage:
712 data = current_tcmh.commitid - idx->tag_seek[tag_commitid] - 1;
713 break;
715 default:
716 data = idx->tag_seek[tag];
719 return data;
722 long tagcache_get_numeric(const struct tagcache_search *tcs, int tag)
724 struct index_entry idx;
726 if (!tc_stat.ready)
727 return false;
729 if (!tagcache_is_numeric_tag(tag))
730 return -1;
732 if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
733 return -2;
735 return check_virtual_tags(tag, &idx);
738 inline static bool str_ends_with(const char *str1, const char *str2)
740 int str_len = strlen(str1);
741 int clause_len = strlen(str2);
743 if (clause_len > str_len)
744 return false;
746 return !strcasecmp(&str1[str_len - clause_len], str2);
749 inline static bool str_oneof(const char *str, const char *list)
751 const char *sep;
752 int l, len = strlen(str);
754 while (*list)
756 sep = strchr(list, '|');
757 l = sep ? (long)sep - (long)list : (int)strlen(list);
758 if ((l==len) && !strncasecmp(str, list, len))
759 return true;
760 list += sep ? l + 1 : l;
763 return false;
766 static bool check_against_clause(long numeric, const char *str,
767 const struct tagcache_search_clause *clause)
769 if (clause->numeric)
771 switch (clause->type)
773 case clause_is:
774 return numeric == clause->numeric_data;
775 case clause_is_not:
776 return numeric != clause->numeric_data;
777 case clause_gt:
778 return numeric > clause->numeric_data;
779 case clause_gteq:
780 return numeric >= clause->numeric_data;
781 case clause_lt:
782 return numeric < clause->numeric_data;
783 case clause_lteq:
784 return numeric <= clause->numeric_data;
785 default:
786 logf("Incorrect numeric tag: %d", clause->type);
789 else
791 switch (clause->type)
793 case clause_is:
794 return !strcasecmp(clause->str, str);
795 case clause_is_not:
796 return strcasecmp(clause->str, str);
797 case clause_gt:
798 return 0>strcasecmp(clause->str, str);
799 case clause_gteq:
800 return 0>=strcasecmp(clause->str, str);
801 case clause_lt:
802 return 0<strcasecmp(clause->str, str);
803 case clause_lteq:
804 return 0<=strcasecmp(clause->str, str);
805 case clause_contains:
806 return (strcasestr(str, clause->str) != NULL);
807 case clause_not_contains:
808 return (strcasestr(str, clause->str) == NULL);
809 case clause_begins_with:
810 return (strcasestr(str, clause->str) == str);
811 case clause_not_begins_with:
812 return (strcasestr(str, clause->str) != str);
813 case clause_ends_with:
814 return str_ends_with(str, clause->str);
815 case clause_not_ends_with:
816 return !str_ends_with(str, clause->str);
817 case clause_oneof:
818 return str_oneof(str, clause->str);
820 default:
821 logf("Incorrect tag: %d", clause->type);
825 return false;
828 static bool check_clauses(struct tagcache_search *tcs,
829 struct index_entry *idx,
830 struct tagcache_search_clause **clause, int count)
832 int i;
834 #ifdef HAVE_TC_RAMCACHE
835 if (tcs->ramsearch)
837 /* Go through all conditional clauses. */
838 for (i = 0; i < count; i++)
840 struct tagfile_entry *tfe;
841 int seek;
842 char buf[256];
843 char *str = NULL;
845 seek = check_virtual_tags(clause[i]->tag, idx);
847 if (!tagcache_is_numeric_tag(clause[i]->tag))
849 if (clause[i]->tag == tag_filename)
851 retrieve(tcs, idx, tag_filename, buf, sizeof buf);
852 str = buf;
854 else
856 tfe = (struct tagfile_entry *)&hdr->tags[clause[i]->tag][seek];
857 str = tfe->tag_data;
861 if (!check_against_clause(seek, str, clause[i]))
862 return false;
865 else
866 #endif
868 /* Check for conditions. */
869 for (i = 0; i < count; i++)
871 struct tagfile_entry tfe;
872 int seek;
873 char str[256];
875 seek = check_virtual_tags(clause[i]->tag, idx);
877 memset(str, 0, sizeof str);
878 if (!tagcache_is_numeric_tag(clause[i]->tag))
880 int fd = tcs->idxfd[clause[i]->tag];
881 lseek(fd, seek, SEEK_SET);
882 ecread(fd, &tfe, 1, tagfile_entry_ec, tc_stat.econ);
883 if (tfe.tag_length >= (int)sizeof(str))
885 logf("Too long tag read!");
886 break ;
889 read(fd, str, tfe.tag_length);
891 /* Check if entry has been deleted. */
892 if (str[0] == '\0')
893 break;
896 if (!check_against_clause(seek, str, clause[i]))
897 return false;
901 return true;
904 bool tagcache_check_clauses(struct tagcache_search *tcs,
905 struct tagcache_search_clause **clause, int count)
907 struct index_entry idx;
909 if (count == 0)
910 return true;
912 if (!get_index(tcs->masterfd, tcs->idx_id, &idx, true))
913 return false;
915 return check_clauses(tcs, &idx, clause, count);
918 static bool add_uniqbuf(struct tagcache_search *tcs, unsigned long id)
920 int i;
922 /* If uniq buffer is not defined we must return true for search to work. */
923 if (tcs->unique_list == NULL
924 || (!tagcache_is_unique_tag(tcs->type)
925 && !tagcache_is_numeric_tag(tcs->type)))
927 return true;
930 for (i = 0; i < tcs->unique_list_count; i++)
932 /* Return false if entry is found. */
933 if (tcs->unique_list[i] == id)
934 return false;
937 if (tcs->unique_list_count < tcs->unique_list_capacity)
939 tcs->unique_list[i] = id;
940 tcs->unique_list_count++;
943 return true;
946 static bool build_lookup_list(struct tagcache_search *tcs)
948 struct index_entry entry;
949 int i;
951 tcs->seek_list_count = 0;
953 #ifdef HAVE_TC_RAMCACHE
954 if (tcs->ramsearch)
956 int j;
958 for (i = tcs->seek_pos; i < hdr->h.tch.entry_count; i++)
960 struct index_entry *idx = &hdr->indices[i];
961 if (tcs->seek_list_count == SEEK_LIST_SIZE)
962 break ;
964 /* Skip deleted files. */
965 if (idx->flag & FLAG_DELETED)
966 continue;
968 /* Go through all filters.. */
969 for (j = 0; j < tcs->filter_count; j++)
971 if (idx->tag_seek[tcs->filter_tag[j]] != tcs->filter_seek[j])
973 break ;
977 if (j < tcs->filter_count)
978 continue ;
980 /* Check for conditions. */
981 if (!check_clauses(tcs, idx, tcs->clause, tcs->clause_count))
982 continue;
984 /* Add to the seek list if not already in uniq buffer. */
985 if (!add_uniqbuf(tcs, idx->tag_seek[tcs->type]))
986 continue;
988 /* Lets add it. */
989 tcs->seek_list[tcs->seek_list_count] = idx->tag_seek[tcs->type];
990 tcs->seek_flags[tcs->seek_list_count] = idx->flag;
991 tcs->seek_list_count++;
994 tcs->seek_pos = i;
996 return tcs->seek_list_count > 0;
998 #endif
1000 lseek(tcs->masterfd, tcs->seek_pos * sizeof(struct index_entry) +
1001 sizeof(struct master_header), SEEK_SET);
1003 while (ecread(tcs->masterfd, &entry, 1, index_entry_ec, tc_stat.econ)
1004 == sizeof(struct index_entry))
1006 if (tcs->seek_list_count == SEEK_LIST_SIZE)
1007 break ;
1009 tcs->seek_pos++;
1011 /* Check if entry has been deleted. */
1012 if (entry.flag & FLAG_DELETED)
1013 continue;
1015 /* Go through all filters.. */
1016 for (i = 0; i < tcs->filter_count; i++)
1018 if (entry.tag_seek[tcs->filter_tag[i]] != tcs->filter_seek[i])
1019 break ;
1022 if (i < tcs->filter_count)
1023 continue ;
1025 /* Check for conditions. */
1026 if (!check_clauses(tcs, &entry, tcs->clause, tcs->clause_count))
1027 continue;
1029 /* Add to the seek list if not already in uniq buffer. */
1030 if (!add_uniqbuf(tcs, entry.tag_seek[tcs->type]))
1031 continue;
1033 /* Lets add it. */
1034 tcs->seek_list[tcs->seek_list_count] = entry.tag_seek[tcs->type];
1035 tcs->seek_flags[tcs->seek_list_count] = entry.flag;
1036 tcs->seek_list_count++;
1038 yield();
1041 return tcs->seek_list_count > 0;
1045 static void remove_files(void)
1047 int i;
1048 char buf[MAX_PATH];
1050 tc_stat.ready = false;
1051 tc_stat.ramcache = false;
1052 tc_stat.econ = false;
1053 remove(TAGCACHE_FILE_MASTER);
1054 for (i = 0; i < TAG_COUNT; i++)
1056 if (tagcache_is_numeric_tag(i))
1057 continue;
1059 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, i);
1060 remove(buf);
1065 static int open_master_fd(struct master_header *hdr, bool write)
1067 int fd;
1068 int rc;
1070 fd = open(TAGCACHE_FILE_MASTER, write ? O_RDWR : O_RDONLY);
1071 if (fd < 0)
1073 logf("master file open failed for R/W");
1074 tc_stat.ready = false;
1075 return fd;
1078 tc_stat.econ = false;
1080 /* Check the header. */
1081 rc = read(fd, hdr, sizeof(struct master_header));
1082 if (hdr->tch.magic == TAGCACHE_MAGIC && rc == sizeof(struct master_header))
1084 /* Success. */
1085 return fd;
1088 /* Trying to read again, this time with endianess correction enabled. */
1089 lseek(fd, 0, SEEK_SET);
1091 rc = ecread(fd, hdr, 1, master_header_ec, true);
1092 if (hdr->tch.magic != TAGCACHE_MAGIC || rc != sizeof(struct master_header))
1094 logf("header error");
1095 tc_stat.ready = false;
1096 close(fd);
1097 return -2;
1100 tc_stat.econ = true;
1102 return fd;
1105 static bool check_all_headers(void)
1107 struct master_header myhdr;
1108 struct tagcache_header tch;
1109 int tag;
1110 int fd;
1112 if ( (fd = open_master_fd(&myhdr, false)) < 0)
1113 return false;
1115 close(fd);
1116 if (myhdr.dirty)
1118 logf("tagcache is dirty!");
1119 return false;
1122 memcpy(&current_tcmh, &myhdr, sizeof(struct master_header));
1124 for (tag = 0; tag < TAG_COUNT; tag++)
1126 if (tagcache_is_numeric_tag(tag))
1127 continue;
1129 if ( (fd = open_tag_fd(&tch, tag, false)) < 0)
1130 return false;
1132 close(fd);
1135 return true;
1138 bool tagcache_search(struct tagcache_search *tcs, int tag)
1140 struct tagcache_header tag_hdr;
1141 struct master_header master_hdr;
1142 int i;
1144 if (tcs->initialized)
1145 tagcache_search_finish(tcs);
1147 while (read_lock)
1148 sleep(1);
1150 memset(tcs, 0, sizeof(struct tagcache_search));
1151 if (tc_stat.commit_step > 0 || !tc_stat.ready)
1152 return false;
1154 tcs->position = sizeof(struct tagcache_header);
1155 tcs->type = tag;
1156 tcs->seek_pos = 0;
1157 tcs->seek_list_count = 0;
1158 tcs->filter_count = 0;
1159 tcs->masterfd = -1;
1161 for (i = 0; i < TAG_COUNT; i++)
1162 tcs->idxfd[i] = -1;
1164 #ifndef HAVE_TC_RAMCACHE
1165 tcs->ramsearch = false;
1166 #else
1167 tcs->ramsearch = tc_stat.ramcache;
1168 if (tcs->ramsearch)
1170 tcs->entry_count = hdr->entry_count[tcs->type];
1172 else
1173 #endif
1175 if (!tagcache_is_numeric_tag(tcs->type))
1177 tcs->idxfd[tcs->type] = open_tag_fd(&tag_hdr, tcs->type, false);
1178 if (tcs->idxfd[tcs->type] < 0)
1179 return false;
1182 /* Always open as R/W so we can pass tcs to functions that modify data also
1183 * without failing. */
1184 tcs->masterfd = open_master_fd(&master_hdr, true);
1186 if (tcs->masterfd < 0)
1187 return false;
1190 tcs->valid = true;
1191 tcs->initialized = true;
1192 write_lock++;
1194 return true;
1197 void tagcache_search_set_uniqbuf(struct tagcache_search *tcs,
1198 void *buffer, long length)
1200 tcs->unique_list = (unsigned long *)buffer;
1201 tcs->unique_list_capacity = length / sizeof(*tcs->unique_list);
1202 tcs->unique_list_count = 0;
1205 bool tagcache_search_add_filter(struct tagcache_search *tcs,
1206 int tag, int seek)
1208 if (tcs->filter_count == TAGCACHE_MAX_FILTERS)
1209 return false;
1211 if (!tagcache_is_unique_tag(tag) || tagcache_is_numeric_tag(tag))
1212 return false;
1214 tcs->filter_tag[tcs->filter_count] = tag;
1215 tcs->filter_seek[tcs->filter_count] = seek;
1216 tcs->filter_count++;
1218 return true;
1221 bool tagcache_search_add_clause(struct tagcache_search *tcs,
1222 struct tagcache_search_clause *clause)
1224 int i;
1226 if (tcs->clause_count >= TAGCACHE_MAX_CLAUSES)
1228 logf("Too many clauses");
1229 return false;
1232 /* Check if there is already a similar filter in present (filters are
1233 * much faster than clauses).
1235 for (i = 0; i < tcs->filter_count; i++)
1237 if (tcs->filter_tag[i] == clause->tag)
1238 return true;
1241 if (!tagcache_is_numeric_tag(clause->tag) && tcs->idxfd[clause->tag] < 0)
1243 char buf[MAX_PATH];
1245 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, clause->tag);
1246 tcs->idxfd[clause->tag] = open(buf, O_RDONLY);
1249 tcs->clause[tcs->clause_count] = clause;
1250 tcs->clause_count++;
1252 return true;
1255 #define TAG_FILENAME_RAM(tcs) ((tcs->type == tag_filename) \
1256 ? (flag & FLAG_DIRCACHE) : 1)
1258 static bool get_next(struct tagcache_search *tcs)
1260 static char buf[TAG_MAXLEN+32];
1261 struct tagfile_entry entry;
1262 long flag = 0;
1264 if (!tcs->valid || !tc_stat.ready)
1265 return false;
1267 if (tcs->idxfd[tcs->type] < 0 && !tagcache_is_numeric_tag(tcs->type)
1268 #ifdef HAVE_TC_RAMCACHE
1269 && !tcs->ramsearch
1270 #endif
1272 return false;
1274 /* Relative fetch. */
1275 if (tcs->filter_count > 0 || tcs->clause_count > 0
1276 || tagcache_is_numeric_tag(tcs->type))
1278 /* Check for end of list. */
1279 if (tcs->seek_list_count == 0)
1281 /* Try to fetch more. */
1282 if (!build_lookup_list(tcs))
1284 tcs->valid = false;
1285 return false;
1289 tcs->seek_list_count--;
1290 flag = tcs->seek_flags[tcs->seek_list_count];
1292 /* Seek stream to the correct position and continue to direct fetch. */
1293 if ((!tcs->ramsearch || !TAG_FILENAME_RAM(tcs))
1294 && !tagcache_is_numeric_tag(tcs->type))
1296 if (!open_files(tcs, tcs->type))
1297 return false;
1299 lseek(tcs->idxfd[tcs->type], tcs->seek_list[tcs->seek_list_count], SEEK_SET);
1301 else
1302 tcs->position = tcs->seek_list[tcs->seek_list_count];
1305 if (tagcache_is_numeric_tag(tcs->type))
1307 snprintf(buf, sizeof(buf), "%d", tcs->position);
1308 tcs->result_seek = tcs->position;
1309 tcs->result = buf;
1310 tcs->result_len = strlen(buf) + 1;
1311 return true;
1314 /* Direct fetch. */
1315 #ifdef HAVE_TC_RAMCACHE
1316 if (tcs->ramsearch && TAG_FILENAME_RAM(tcs))
1318 struct tagfile_entry *ep;
1320 if (tcs->entry_count == 0)
1322 tcs->valid = false;
1323 return false;
1325 tcs->entry_count--;
1327 tcs->result_seek = tcs->position;
1329 # ifdef HAVE_DIRCACHE
1330 if (tcs->type == tag_filename)
1332 dircache_copy_path((struct dircache_entry *)tcs->position,
1333 buf, sizeof buf);
1334 tcs->result = buf;
1335 tcs->result_len = strlen(buf) + 1;
1336 tcs->idx_id = FLAG_GET_ATTR(flag);
1337 tcs->ramresult = false;
1339 return true;
1341 # endif
1343 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->position];
1344 tcs->position += sizeof(struct tagfile_entry) + ep->tag_length;
1345 tcs->result = ep->tag_data;
1346 tcs->result_len = strlen(tcs->result) + 1;
1347 tcs->idx_id = ep->idx_id;
1348 tcs->ramresult = true;
1350 return true;
1352 else
1353 #endif
1355 if (!open_files(tcs, tcs->type))
1356 return false;
1358 tcs->result_seek = lseek(tcs->idxfd[tcs->type], 0, SEEK_CUR);
1359 if (ecread(tcs->idxfd[tcs->type], &entry, 1,
1360 tagfile_entry_ec, tc_stat.econ) != sizeof(struct tagfile_entry))
1362 /* End of data. */
1363 tcs->valid = false;
1364 return false;
1368 if (entry.tag_length > (long)sizeof(buf))
1370 tcs->valid = false;
1371 logf("too long tag #2");
1372 return false;
1375 if (read(tcs->idxfd[tcs->type], buf, entry.tag_length) != entry.tag_length)
1377 tcs->valid = false;
1378 logf("read error #4");
1379 return false;
1382 tcs->result = buf;
1383 tcs->result_len = strlen(tcs->result) + 1;
1384 tcs->idx_id = entry.idx_id;
1385 tcs->ramresult = false;
1387 return true;
1390 bool tagcache_get_next(struct tagcache_search *tcs)
1392 while (get_next(tcs))
1394 if (tcs->result_len > 1)
1395 return true;
1398 return false;
1401 bool tagcache_retrieve(struct tagcache_search *tcs, int idxid,
1402 int tag, char *buf, long size)
1404 struct index_entry idx;
1406 *buf = '\0';
1407 if (!get_index(tcs->masterfd, idxid, &idx, true))
1408 return false;
1410 return retrieve(tcs, &idx, tag, buf, size);
1413 static bool update_master_header(void)
1415 struct master_header myhdr;
1416 int fd;
1418 if (!tc_stat.ready)
1419 return false;
1421 if ( (fd = open_master_fd(&myhdr, true)) < 0)
1422 return false;
1424 myhdr.serial = current_tcmh.serial;
1425 myhdr.commitid = current_tcmh.commitid;
1427 /* Write it back */
1428 lseek(fd, 0, SEEK_SET);
1429 ecwrite(fd, &myhdr, 1, master_header_ec, tc_stat.econ);
1430 close(fd);
1432 #ifdef HAVE_TC_RAMCACHE
1433 if (hdr)
1435 hdr->h.serial = current_tcmh.serial;
1436 hdr->h.commitid = current_tcmh.commitid;
1438 #endif
1440 return true;
1443 #if 0
1445 void tagcache_modify(struct tagcache_search *tcs, int type, const char *text)
1447 struct tagentry *entry;
1449 if (tcs->type != tag_title)
1450 return ;
1452 /* We will need reserve buffer for this. */
1453 if (tcs->ramcache)
1455 struct tagfile_entry *ep;
1457 ep = (struct tagfile_entry *)&hdr->tags[tcs->type][tcs->result_seek];
1458 tcs->seek_list[tcs->seek_list_count];
1461 entry = find_entry_ram();
1464 #endif
1466 void tagcache_search_finish(struct tagcache_search *tcs)
1468 int i;
1470 if (!tcs->initialized)
1471 return;
1473 if (tcs->masterfd >= 0)
1475 close(tcs->masterfd);
1476 tcs->masterfd = -1;
1479 for (i = 0; i < TAG_COUNT; i++)
1481 if (tcs->idxfd[i] >= 0)
1483 close(tcs->idxfd[i]);
1484 tcs->idxfd[i] = -1;
1488 tcs->ramsearch = false;
1489 tcs->valid = false;
1490 tcs->initialized = 0;
1491 if (write_lock > 0)
1492 write_lock--;
1495 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1496 static struct tagfile_entry *get_tag(const struct index_entry *entry, int tag)
1498 return (struct tagfile_entry *)&hdr->tags[tag][entry->tag_seek[tag]];
1501 static long get_tag_numeric(const struct index_entry *entry, int tag)
1503 return entry->tag_seek[tag];
1506 static char* get_tag_string(const struct index_entry *entry, int tag)
1508 char* s = get_tag(entry, tag)->tag_data;
1509 return strcmp(s, UNTAGGED) ? s : NULL;
1512 bool tagcache_fill_tags(struct mp3entry *id3, const char *filename)
1514 struct index_entry *entry;
1515 int idx_id;
1517 if (!tc_stat.ready)
1518 return false;
1520 /* Find the corresponding entry in tagcache. */
1521 idx_id = find_entry_ram(filename, NULL);
1522 if (idx_id < 0 || !tc_stat.ramcache)
1523 return false;
1525 entry = &hdr->indices[idx_id];
1527 id3->title = get_tag_string(entry, tag_title);
1528 id3->artist = get_tag_string(entry, tag_artist);
1529 id3->album = get_tag_string(entry, tag_album);
1530 id3->genre_string = get_tag_string(entry, tag_genre);
1531 id3->composer = get_tag_string(entry, tag_composer);
1532 id3->comment = get_tag_string(entry, tag_comment);
1533 id3->albumartist = get_tag_string(entry, tag_albumartist);
1535 id3->playcount = get_tag_numeric(entry, tag_playcount);
1536 id3->rating = get_tag_numeric(entry, tag_rating);
1537 id3->lastplayed = get_tag_numeric(entry, tag_lastplayed);
1538 id3->score = get_tag_numeric(entry, tag_virt_autoscore) / 10;
1539 id3->year = get_tag_numeric(entry, tag_year);
1541 id3->tracknum = get_tag_numeric(entry, tag_tracknumber);
1542 id3->bitrate = get_tag_numeric(entry, tag_bitrate);
1543 if (id3->bitrate == 0)
1544 id3->bitrate = 1;
1546 return true;
1548 #endif
1550 static inline void write_item(const char *item)
1552 int len = strlen(item) + 1;
1554 data_size += len;
1555 write(cachefd, item, len);
1558 static int check_if_empty(char **tag)
1560 int length;
1562 if (*tag == NULL || *tag[0] == '\0')
1564 *tag = UNTAGGED;
1565 return sizeof(UNTAGGED); /* Tag length */
1568 length = strlen(*tag);
1569 if (length > TAG_MAXLEN)
1571 logf("over length tag: %s", *tag);
1572 length = TAG_MAXLEN;
1573 *tag[length] = '\0';
1576 return length + 1;
1579 #define ADD_TAG(entry,tag,data) \
1580 /* Adding tag */ \
1581 entry.tag_offset[tag] = offset; \
1582 entry.tag_length[tag] = check_if_empty(data); \
1583 offset += entry.tag_length[tag]
1585 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1586 static void add_tagcache(char *path, const struct dircache_entry *dc)
1587 #else
1588 static void add_tagcache(char *path)
1589 #endif
1591 struct track_info track;
1592 struct temp_file_entry entry;
1593 bool ret;
1594 int fd;
1595 char tracknumfix[3];
1596 int offset = 0;
1597 int path_length = strlen(path);
1599 if (cachefd < 0)
1600 return ;
1602 /* Check for overlength file path. */
1603 if (path_length > TAG_MAXLEN)
1605 /* Path can't be shortened. */
1606 logf("Too long path: %s", path);
1607 return ;
1610 /* Check if the file is supported. */
1611 if (probe_file_format(path) == AFMT_UNKNOWN)
1612 return ;
1614 /* Check if the file is already cached. */
1615 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1616 if (tc_stat.ramcache && dircache_is_enabled())
1618 if (find_entry_ram(path, dc) >= 0)
1619 return ;
1621 else
1622 #endif
1624 if (filenametag_fd >= 0)
1626 if (find_entry_disk(path) >= 0)
1627 return ;
1631 fd = open(path, O_RDONLY);
1632 if (fd < 0)
1634 logf("open fail: %s", path);
1635 return ;
1638 memset(&track, 0, sizeof(struct track_info));
1639 memset(&entry, 0, sizeof(struct temp_file_entry));
1640 memset(&tracknumfix, 0, sizeof(tracknumfix));
1641 ret = get_metadata(&track, fd, path, false);
1642 close(fd);
1644 if (!ret)
1645 return ;
1647 logf("-> %s", path);
1649 /* Generate track number if missing. */
1650 if (track.id3.tracknum <= 0)
1652 const char *p = strrchr(path, '.');
1654 if (p == NULL)
1655 p = &path[strlen(path)-1];
1657 while (*p != '/')
1659 if (isdigit(*p) && isdigit(*(p-1)))
1661 tracknumfix[1] = *p--;
1662 tracknumfix[0] = *p;
1663 break;
1665 p--;
1668 if (tracknumfix[0] != '\0')
1670 track.id3.tracknum = atoi(tracknumfix);
1671 /* Set a flag to indicate track number has been generated. */
1672 entry.flag |= FLAG_TRKNUMGEN;
1674 else
1676 /* Unable to generate track number. */
1677 track.id3.tracknum = -1;
1681 /* Numeric tags */
1682 entry.tag_offset[tag_year] = track.id3.year;
1683 entry.tag_offset[tag_tracknumber] = track.id3.tracknum;
1684 entry.tag_offset[tag_length] = track.id3.length;
1685 entry.tag_offset[tag_bitrate] = track.id3.bitrate;
1687 /* String tags. */
1688 ADD_TAG(entry, tag_filename, &path);
1689 ADD_TAG(entry, tag_title, &track.id3.title);
1690 ADD_TAG(entry, tag_artist, &track.id3.artist);
1691 ADD_TAG(entry, tag_album, &track.id3.album);
1692 ADD_TAG(entry, tag_genre, &track.id3.genre_string);
1693 ADD_TAG(entry, tag_composer, &track.id3.composer);
1694 ADD_TAG(entry, tag_comment, &track.id3.comment);
1695 ADD_TAG(entry, tag_albumartist, &track.id3.albumartist);
1696 entry.data_length = offset;
1698 /* Write the header */
1699 write(cachefd, &entry, sizeof(struct temp_file_entry));
1701 /* And tags also... Correct order is critical */
1702 write_item(path);
1703 write_item(track.id3.title);
1704 write_item(track.id3.artist);
1705 write_item(track.id3.album);
1706 write_item(track.id3.genre_string);
1707 write_item(track.id3.composer);
1708 write_item(track.id3.comment);
1709 write_item(track.id3.albumartist);
1710 total_entry_count++;
1713 static bool tempbuf_insert(char *str, int id, int idx_id, bool unique)
1715 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
1716 int len = strlen(str)+1;
1717 int i;
1718 unsigned crc32;
1719 unsigned *crcbuf = (unsigned *)&tempbuf[tempbuf_size-4];
1720 char buf[TAG_MAXLEN+32];
1722 for (i = 0; str[i] != '\0' && i < (int)sizeof(buf)-1; i++)
1723 buf[i] = tolower(str[i]);
1724 buf[i] = '\0';
1726 crc32 = crc_32(buf, i, 0xffffffff);
1728 if (unique)
1730 /* Check if the crc does not exist -> entry does not exist for sure. */
1731 for (i = 0; i < tempbufidx; i++)
1733 if (crcbuf[-i] != crc32)
1734 continue;
1736 if (!strcasecmp(str, index[i].str))
1738 if (id < 0 || id >= lookup_buffer_depth)
1740 logf("lookup buf overf.: %d", id);
1741 return false;
1744 lookup[id] = &index[i];
1745 return true;
1750 /* Insert to CRC buffer. */
1751 crcbuf[-tempbufidx] = crc32;
1752 tempbuf_left -= 4;
1754 /* Insert it to the buffer. */
1755 tempbuf_left -= len;
1756 if (tempbuf_left - 4 < 0 || tempbufidx >= commit_entry_count-1)
1757 return false;
1759 if (id >= lookup_buffer_depth)
1761 logf("lookup buf overf. #2: %d", id);
1762 return false;
1765 if (id >= 0)
1767 lookup[id] = &index[tempbufidx];
1768 index[tempbufidx].idlist.id = id;
1770 else
1771 index[tempbufidx].idlist.id = -1;
1773 index[tempbufidx].idlist.next = NULL;
1774 index[tempbufidx].idx_id = idx_id;
1775 index[tempbufidx].seek = -1;
1776 index[tempbufidx].str = &tempbuf[tempbuf_pos];
1777 memcpy(index[tempbufidx].str, str, len);
1778 tempbuf_pos += len;
1779 tempbufidx++;
1781 return true;
1784 static int compare(const void *p1, const void *p2)
1786 do_timed_yield();
1788 struct tempbuf_searchidx *e1 = (struct tempbuf_searchidx *)p1;
1789 struct tempbuf_searchidx *e2 = (struct tempbuf_searchidx *)p2;
1791 if (strcmp(e1->str, UNTAGGED) == 0)
1793 if (strcmp(e2->str, UNTAGGED) == 0)
1794 return 0;
1795 return -1;
1797 else if (strcmp(e2->str, UNTAGGED) == 0)
1798 return 1;
1800 return strncasecmp(e1->str, e2->str, TAG_MAXLEN);
1803 static int tempbuf_sort(int fd)
1805 struct tempbuf_searchidx *index = (struct tempbuf_searchidx *)tempbuf;
1806 struct tagfile_entry fe;
1807 int i;
1808 int length;
1810 /* Generate reverse lookup entries. */
1811 for (i = 0; i < lookup_buffer_depth; i++)
1813 struct tempbuf_id_list *idlist;
1815 if (!lookup[i])
1816 continue;
1818 if (lookup[i]->idlist.id == i)
1819 continue;
1821 idlist = &lookup[i]->idlist;
1822 while (idlist->next != NULL)
1823 idlist = idlist->next;
1825 tempbuf_left -= sizeof(struct tempbuf_id_list);
1826 if (tempbuf_left - 4 < 0)
1827 return -1;
1829 idlist->next = (struct tempbuf_id_list *)&tempbuf[tempbuf_pos];
1830 if (tempbuf_pos & 0x03)
1832 tempbuf_pos = (tempbuf_pos & ~0x03) + 0x04;
1833 tempbuf_left -= 3;
1834 idlist->next = (struct tempbuf_id_list *)&tempbuf[tempbuf_pos];
1836 tempbuf_pos += sizeof(struct tempbuf_id_list);
1838 idlist = idlist->next;
1839 idlist->id = i;
1840 idlist->next = NULL;
1842 do_timed_yield();
1845 qsort(index, tempbufidx, sizeof(struct tempbuf_searchidx), compare);
1846 memset(lookup, 0, lookup_buffer_depth * sizeof(struct tempbuf_searchidx **));
1848 for (i = 0; i < tempbufidx; i++)
1850 struct tempbuf_id_list *idlist = &index[i].idlist;
1852 /* Fix the lookup list. */
1853 while (idlist != NULL)
1855 if (idlist->id >= 0)
1856 lookup[idlist->id] = &index[i];
1857 idlist = idlist->next;
1860 index[i].seek = lseek(fd, 0, SEEK_CUR);
1861 length = strlen(index[i].str) + 1;
1862 fe.tag_length = length;
1863 fe.idx_id = index[i].idx_id;
1865 /* Check the chunk alignment. */
1866 if ((fe.tag_length + sizeof(struct tagfile_entry))
1867 % TAGFILE_ENTRY_CHUNK_LENGTH)
1869 fe.tag_length += TAGFILE_ENTRY_CHUNK_LENGTH -
1870 ((fe.tag_length + sizeof(struct tagfile_entry))
1871 % TAGFILE_ENTRY_CHUNK_LENGTH);
1874 #ifdef TAGCACHE_STRICT_ALIGN
1875 /* Make sure the entry is long aligned. */
1876 if (index[i].seek & 0x03)
1878 logf("tempbuf_sort: alignment error!");
1879 return -3;
1881 #endif
1883 if (ecwrite(fd, &fe, 1, tagfile_entry_ec, tc_stat.econ) !=
1884 sizeof(struct tagfile_entry))
1886 logf("tempbuf_sort: write error #1");
1887 return -1;
1890 if (write(fd, index[i].str, length) != length)
1892 logf("tempbuf_sort: write error #2");
1893 return -2;
1896 /* Write some padding. */
1897 if (fe.tag_length - length > 0)
1898 write(fd, "XXXXXXXX", fe.tag_length - length);
1901 return i;
1904 inline static struct tempbuf_searchidx* tempbuf_locate(int id)
1906 if (id < 0 || id >= lookup_buffer_depth)
1907 return NULL;
1909 return lookup[id];
1913 inline static int tempbuf_find_location(int id)
1915 struct tempbuf_searchidx *entry;
1917 entry = tempbuf_locate(id);
1918 if (entry == NULL)
1919 return -1;
1921 return entry->seek;
1924 static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
1926 struct master_header tcmh;
1927 struct index_entry idx;
1928 int masterfd;
1929 int masterfd_pos;
1930 struct temp_file_entry *entrybuf = (struct temp_file_entry *)tempbuf;
1931 int max_entries;
1932 int entries_processed = 0;
1933 int i, j;
1935 max_entries = tempbuf_size / sizeof(struct temp_file_entry) - 1;
1937 logf("Building numeric indices...");
1938 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
1940 if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
1941 return false;
1943 masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry),
1944 SEEK_CUR);
1945 if (masterfd_pos == filesize(masterfd))
1947 logf("we can't append!");
1948 close(masterfd);
1949 return false;
1952 while (entries_processed < h->entry_count)
1954 int count = MIN(h->entry_count - entries_processed, max_entries);
1956 /* Read in as many entries as possible. */
1957 for (i = 0; i < count; i++)
1959 /* Read in numeric data. */
1960 if (read(tmpfd, &entrybuf[i], sizeof(struct temp_file_entry)) !=
1961 sizeof(struct temp_file_entry))
1963 logf("read fail #1");
1964 close(masterfd);
1965 return false;
1968 /* Skip string data. */
1969 lseek(tmpfd, entrybuf[i].data_length, SEEK_CUR);
1972 /* Commit the data to the index. */
1973 for (i = 0; i < count; i++)
1975 int loc = lseek(masterfd, 0, SEEK_CUR);
1977 if (ecread(masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
1978 != sizeof(struct index_entry))
1980 logf("read fail #2");
1981 close(masterfd);
1982 return false;
1985 for (j = 0; j < TAG_COUNT; j++)
1987 if (!tagcache_is_numeric_tag(j))
1988 continue;
1990 idx.tag_seek[j] = entrybuf[i].tag_offset[j];
1992 idx.flag = entrybuf[i].flag;
1994 if (tc_stat.ready && current_tcmh.commitid > 0)
1996 idx.tag_seek[tag_commitid] = current_tcmh.commitid;
1997 idx.flag |= FLAG_DIRTYNUM;
2000 /* Write back the updated index. */
2001 lseek(masterfd, loc, SEEK_SET);
2002 if (ecwrite(masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
2003 != sizeof(struct index_entry))
2005 logf("write fail");
2006 close(masterfd);
2007 return false;
2011 entries_processed += count;
2012 logf("%d/%ld entries processed", entries_processed, h->entry_count);
2015 close(masterfd);
2017 return true;
2021 * Return values:
2022 * > 0 success
2023 * == 0 temporary failure
2024 * < 0 fatal error
2026 static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
2028 int i;
2029 struct tagcache_header tch;
2030 struct master_header tcmh;
2031 struct index_entry idxbuf[IDX_BUF_DEPTH];
2032 int idxbuf_pos;
2033 char buf[TAG_MAXLEN+32];
2034 int fd = -1, masterfd;
2035 bool error = false;
2036 int init;
2037 int masterfd_pos;
2039 logf("Building index: %d", index_type);
2041 /* Check the number of entries we need to allocate ram for. */
2042 commit_entry_count = h->entry_count + 1;
2044 masterfd = open_master_fd(&tcmh, false);
2045 if (masterfd >= 0)
2047 commit_entry_count += tcmh.tch.entry_count;
2048 close(masterfd);
2050 else
2051 remove_files(); /* Just to be sure we are clean. */
2053 /* Open the index file, which contains the tag names. */
2054 fd = open_tag_fd(&tch, index_type, true);
2055 if (fd >= 0)
2057 logf("tch.datasize=%ld", tch.datasize);
2058 lookup_buffer_depth = 1 +
2059 /* First part */ commit_entry_count +
2060 /* Second part */ (tch.datasize / TAGFILE_ENTRY_CHUNK_LENGTH);
2062 else
2064 lookup_buffer_depth = 1 +
2065 /* First part */ commit_entry_count +
2066 /* Second part */ 0;
2069 logf("lookup_buffer_depth=%ld", lookup_buffer_depth);
2070 logf("commit_entry_count=%ld", commit_entry_count);
2072 /* Allocate buffer for all index entries from both old and new
2073 * tag files. */
2074 tempbufidx = 0;
2075 tempbuf_pos = commit_entry_count * sizeof(struct tempbuf_searchidx);
2077 /* Allocate lookup buffer. The first portion of commit_entry_count
2078 * contains the new tags in the temporary file and the second
2079 * part for locating entries already in the db.
2081 * New tags Old tags
2082 * +---------+---------------------------+
2083 * | index | position/ENTRY_CHUNK_SIZE | lookup buffer
2084 * +---------+---------------------------+
2086 * Old tags are inserted to a temporary buffer with position:
2087 * tempbuf_insert(position/ENTRY_CHUNK_SIZE, ...);
2088 * And new tags with index:
2089 * tempbuf_insert(idx, ...);
2091 * The buffer is sorted and written into tag file:
2092 * tempbuf_sort(...);
2093 * leaving master index locations messed up.
2095 * That is fixed using the lookup buffer for old tags:
2096 * new_seek = tempbuf_find_location(old_seek, ...);
2097 * and for new tags:
2098 * new_seek = tempbuf_find_location(idx);
2100 lookup = (struct tempbuf_searchidx **)&tempbuf[tempbuf_pos];
2101 tempbuf_pos += lookup_buffer_depth * sizeof(void **);
2102 memset(lookup, 0, lookup_buffer_depth * sizeof(void **));
2104 /* And calculate the remaining data space used mainly for storing
2105 * tag data (strings). */
2106 tempbuf_left = tempbuf_size - tempbuf_pos - 8;
2107 if (tempbuf_left - TAGFILE_ENTRY_AVG_LENGTH * commit_entry_count < 0)
2109 logf("Buffer way too small!");
2110 return 0;
2113 if (fd >= 0)
2116 * If tag file contains unique tags (sorted index), we will load
2117 * it entirely into memory so we can resort it later for use with
2118 * chunked browsing.
2120 if (tagcache_is_sorted_tag(index_type))
2122 logf("loading tags...");
2123 for (i = 0; i < tch.entry_count; i++)
2125 struct tagfile_entry entry;
2126 int loc = lseek(fd, 0, SEEK_CUR);
2127 bool ret;
2129 if (ecread(fd, &entry, 1, tagfile_entry_ec, tc_stat.econ)
2130 != sizeof(struct tagfile_entry))
2132 logf("read error #7");
2133 close(fd);
2134 return -2;
2137 if (entry.tag_length >= (int)sizeof(buf))
2139 logf("too long tag #3");
2140 close(fd);
2141 return -2;
2144 if (read(fd, buf, entry.tag_length) != entry.tag_length)
2146 logf("read error #8");
2147 close(fd);
2148 return -2;
2151 /* Skip deleted entries. */
2152 if (buf[0] == '\0')
2153 continue;
2156 * Save the tag and tag id in the memory buffer. Tag id
2157 * is saved so we can later reindex the master lookup
2158 * table when the index gets resorted.
2160 ret = tempbuf_insert(buf, loc/TAGFILE_ENTRY_CHUNK_LENGTH
2161 + commit_entry_count, entry.idx_id,
2162 tagcache_is_unique_tag(index_type));
2163 if (!ret)
2165 close(fd);
2166 return -3;
2168 do_timed_yield();
2170 logf("done");
2172 else
2173 tempbufidx = tch.entry_count;
2175 else
2178 * Creating new index file to store the tags. No need to preload
2179 * anything whether the index type is sorted or not.
2181 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, index_type);
2182 fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC);
2183 if (fd < 0)
2185 logf("%s open fail", buf);
2186 return -2;
2189 tch.magic = TAGCACHE_MAGIC;
2190 tch.entry_count = 0;
2191 tch.datasize = 0;
2193 if (ecwrite(fd, &tch, 1, tagcache_header_ec, tc_stat.econ)
2194 != sizeof(struct tagcache_header))
2196 logf("header write failed");
2197 close(fd);
2198 return -2;
2202 /* Loading the tag lookup file as "master file". */
2203 logf("Loading index file");
2204 masterfd = open(TAGCACHE_FILE_MASTER, O_RDWR);
2206 if (masterfd < 0)
2208 logf("Creating new DB");
2209 masterfd = open(TAGCACHE_FILE_MASTER, O_WRONLY | O_CREAT | O_TRUNC);
2211 if (masterfd < 0)
2213 logf("Failure to create index file (%s)", TAGCACHE_FILE_MASTER);
2214 close(fd);
2215 return -2;
2218 /* Write the header (write real values later). */
2219 memset(&tcmh, 0, sizeof(struct master_header));
2220 tcmh.tch = *h;
2221 tcmh.tch.entry_count = 0;
2222 tcmh.tch.datasize = 0;
2223 tcmh.dirty = true;
2224 ecwrite(masterfd, &tcmh, 1, master_header_ec, tc_stat.econ);
2225 init = true;
2226 masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
2228 else
2231 * Master file already exists so we need to process the current
2232 * file first.
2234 init = false;
2236 if (ecread(masterfd, &tcmh, 1, master_header_ec, tc_stat.econ) !=
2237 sizeof(struct master_header) || tcmh.tch.magic != TAGCACHE_MAGIC)
2239 logf("header error");
2240 close(fd);
2241 close(masterfd);
2242 return -2;
2246 * If we reach end of the master file, we need to expand it to
2247 * hold new tags. If the current index is not sorted, we can
2248 * simply append new data to end of the file.
2249 * However, if the index is sorted, we need to update all tag
2250 * pointers in the master file for the current index.
2252 masterfd_pos = lseek(masterfd, tcmh.tch.entry_count * sizeof(struct index_entry),
2253 SEEK_CUR);
2254 if (masterfd_pos == filesize(masterfd))
2256 logf("appending...");
2257 init = true;
2262 * Load new unique tags in memory to be sorted later and added
2263 * to the master lookup file.
2265 if (tagcache_is_sorted_tag(index_type))
2267 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
2268 /* h is the header of the temporary file containing new tags. */
2269 logf("inserting new tags...");
2270 for (i = 0; i < h->entry_count; i++)
2272 struct temp_file_entry entry;
2274 if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
2275 sizeof(struct temp_file_entry))
2277 logf("read fail #3");
2278 error = true;
2279 goto error_exit;
2282 /* Read data. */
2283 if (entry.tag_length[index_type] >= (long)sizeof(buf))
2285 logf("too long entry!");
2286 error = true;
2287 goto error_exit;
2290 lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
2291 if (read(tmpfd, buf, entry.tag_length[index_type]) !=
2292 entry.tag_length[index_type])
2294 logf("read fail #4");
2295 error = true;
2296 goto error_exit;
2299 if (tagcache_is_unique_tag(index_type))
2300 error = !tempbuf_insert(buf, i, -1, true);
2301 else
2302 error = !tempbuf_insert(buf, i, tcmh.tch.entry_count + i, false);
2304 if (error)
2306 logf("insert error");
2307 goto error_exit;
2310 /* Skip to next. */
2311 lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
2312 entry.tag_length[index_type], SEEK_CUR);
2313 do_timed_yield();
2315 logf("done");
2317 /* Sort the buffer data and write it to the index file. */
2318 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
2319 i = tempbuf_sort(fd);
2320 if (i < 0)
2321 goto error_exit;
2322 logf("sorted %d tags", i);
2325 * Now update all indexes in the master lookup file.
2327 logf("updating indices...");
2328 lseek(masterfd, sizeof(struct master_header), SEEK_SET);
2329 for (i = 0; i < tcmh.tch.entry_count; i += idxbuf_pos)
2331 int j;
2332 int loc = lseek(masterfd, 0, SEEK_CUR);
2334 idxbuf_pos = MIN(tcmh.tch.entry_count - i, IDX_BUF_DEPTH);
2336 if (ecread(masterfd, idxbuf, idxbuf_pos, index_entry_ec, tc_stat.econ)
2337 != (int)sizeof(struct index_entry)*idxbuf_pos)
2339 logf("read fail #5");
2340 error = true;
2341 goto error_exit ;
2343 lseek(masterfd, loc, SEEK_SET);
2345 for (j = 0; j < idxbuf_pos; j++)
2347 if (idxbuf[j].flag & FLAG_DELETED)
2349 /* We can just ignore deleted entries. */
2350 idxbuf[j].tag_seek[index_type] = 0;
2351 continue;
2354 idxbuf[j].tag_seek[index_type] = tempbuf_find_location(
2355 idxbuf[j].tag_seek[index_type]/TAGFILE_ENTRY_CHUNK_LENGTH
2356 + commit_entry_count);
2358 if (idxbuf[j].tag_seek[index_type] < 0)
2360 logf("update error: %d/%ld", i+j, tcmh.tch.entry_count);
2361 error = true;
2362 goto error_exit;
2365 do_timed_yield();
2368 /* Write back the updated index. */
2369 if (ecwrite(masterfd, idxbuf, idxbuf_pos,
2370 index_entry_ec, tc_stat.econ) !=
2371 (int)sizeof(struct index_entry)*idxbuf_pos)
2373 logf("write fail");
2374 error = true;
2375 goto error_exit;
2378 logf("done");
2382 * Walk through the temporary file containing the new tags.
2384 // build_normal_index(h, tmpfd, masterfd, idx);
2385 logf("updating new indices...");
2386 lseek(masterfd, masterfd_pos, SEEK_SET);
2387 lseek(tmpfd, sizeof(struct tagcache_header), SEEK_SET);
2388 lseek(fd, 0, SEEK_END);
2389 for (i = 0; i < h->entry_count; i += idxbuf_pos)
2391 int j;
2393 idxbuf_pos = MIN(h->entry_count - i, IDX_BUF_DEPTH);
2394 if (init)
2396 memset(idxbuf, 0, sizeof(struct index_entry)*IDX_BUF_DEPTH);
2398 else
2400 int loc = lseek(masterfd, 0, SEEK_CUR);
2402 if (ecread(masterfd, idxbuf, idxbuf_pos, index_entry_ec, tc_stat.econ)
2403 != (int)sizeof(struct index_entry)*idxbuf_pos)
2405 logf("read fail #6");
2406 error = true;
2407 break ;
2409 lseek(masterfd, loc, SEEK_SET);
2412 /* Read entry headers. */
2413 for (j = 0; j < idxbuf_pos; j++)
2415 if (!tagcache_is_sorted_tag(index_type))
2417 struct temp_file_entry entry;
2418 struct tagfile_entry fe;
2420 if (read(tmpfd, &entry, sizeof(struct temp_file_entry)) !=
2421 sizeof(struct temp_file_entry))
2423 logf("read fail #7");
2424 error = true;
2425 break ;
2428 /* Read data. */
2429 if (entry.tag_length[index_type] >= (int)sizeof(buf))
2431 logf("too long entry!");
2432 logf("length=%d", entry.tag_length[index_type]);
2433 logf("pos=0x%02lx", lseek(tmpfd, 0, SEEK_CUR));
2434 error = true;
2435 break ;
2438 lseek(tmpfd, entry.tag_offset[index_type], SEEK_CUR);
2439 if (read(tmpfd, buf, entry.tag_length[index_type]) !=
2440 entry.tag_length[index_type])
2442 logf("read fail #8");
2443 logf("offset=0x%02lx", entry.tag_offset[index_type]);
2444 logf("length=0x%02x", entry.tag_length[index_type]);
2445 error = true;
2446 break ;
2449 /* Write to index file. */
2450 idxbuf[j].tag_seek[index_type] = lseek(fd, 0, SEEK_CUR);
2451 fe.tag_length = entry.tag_length[index_type];
2452 fe.idx_id = tcmh.tch.entry_count + i + j;
2453 ecwrite(fd, &fe, 1, tagfile_entry_ec, tc_stat.econ);
2454 write(fd, buf, fe.tag_length);
2455 tempbufidx++;
2457 /* Skip to next. */
2458 lseek(tmpfd, entry.data_length - entry.tag_offset[index_type] -
2459 entry.tag_length[index_type], SEEK_CUR);
2461 else
2463 /* Locate the correct entry from the sorted array. */
2464 idxbuf[j].tag_seek[index_type] = tempbuf_find_location(i + j);
2465 if (idxbuf[j].tag_seek[index_type] < 0)
2467 logf("entry not found (%d)", j);
2468 error = true;
2469 break ;
2474 /* Write index. */
2475 if (ecwrite(masterfd, idxbuf, idxbuf_pos,
2476 index_entry_ec, tc_stat.econ) !=
2477 (int)sizeof(struct index_entry)*idxbuf_pos)
2479 logf("tagcache: write fail #4");
2480 error = true;
2481 break ;
2484 do_timed_yield();
2486 logf("done");
2488 /* Finally write the header. */
2489 tch.magic = TAGCACHE_MAGIC;
2490 tch.entry_count = tempbufidx;
2491 tch.datasize = lseek(fd, 0, SEEK_END) - sizeof(struct tagcache_header);
2492 lseek(fd, 0, SEEK_SET);
2493 ecwrite(fd, &tch, 1, tagcache_header_ec, tc_stat.econ);
2495 if (index_type != tag_filename)
2496 h->datasize += tch.datasize;
2497 logf("s:%d/%ld/%ld", index_type, tch.datasize, h->datasize);
2498 error_exit:
2500 close(fd);
2501 close(masterfd);
2503 if (error)
2504 return -2;
2506 return 1;
2509 static bool commit(void)
2511 struct tagcache_header tch;
2512 struct master_header tcmh;
2513 int i, len, rc;
2514 int tmpfd;
2515 int masterfd;
2516 #ifdef HAVE_DIRCACHE
2517 bool dircache_buffer_stolen = false;
2518 #endif
2519 bool local_allocation = false;
2521 logf("committing tagcache");
2523 while (write_lock)
2524 sleep(1);
2526 tmpfd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
2527 if (tmpfd < 0)
2529 logf("nothing to commit");
2530 return true;
2534 /* Load the header. */
2535 len = sizeof(struct tagcache_header);
2536 rc = read(tmpfd, &tch, len);
2538 if (tch.magic != TAGCACHE_MAGIC || rc != len)
2540 logf("incorrect header");
2541 close(tmpfd);
2542 remove(TAGCACHE_FILE_TEMP);
2543 return false;
2546 if (tch.entry_count == 0)
2548 logf("nothing to commit");
2549 close(tmpfd);
2550 remove(TAGCACHE_FILE_TEMP);
2551 return true;
2554 #ifdef HAVE_EEPROM_SETTINGS
2555 remove(TAGCACHE_STATEFILE);
2556 #endif
2558 /* At first be sure to unload the ramcache! */
2559 #ifdef HAVE_TC_RAMCACHE
2560 tc_stat.ramcache = false;
2561 #endif
2563 read_lock++;
2565 /* Try to steal every buffer we can :) */
2566 if (tempbuf_size == 0)
2567 local_allocation = true;
2569 #ifdef HAVE_DIRCACHE
2570 if (tempbuf_size == 0)
2572 /* Try to steal the dircache buffer. */
2573 tempbuf = dircache_steal_buffer(&tempbuf_size);
2574 tempbuf_size &= ~0x03;
2576 if (tempbuf_size > 0)
2578 dircache_buffer_stolen = true;
2581 #endif
2583 #ifdef HAVE_TC_RAMCACHE
2584 if (tempbuf_size == 0 && tc_stat.ramcache_allocated > 0)
2586 tempbuf = (char *)(hdr + 1);
2587 tempbuf_size = tc_stat.ramcache_allocated - sizeof(struct ramcache_header) - 128;
2588 tempbuf_size &= ~0x03;
2590 #endif
2592 /* And finally fail if there are no buffers available. */
2593 if (tempbuf_size == 0)
2595 logf("delaying commit until next boot");
2596 tc_stat.commit_delayed = true;
2597 close(tmpfd);
2598 read_lock--;
2599 return false;
2602 logf("commit %ld entries...", tch.entry_count);
2604 /* Mark DB dirty so it will stay disabled if commit fails. */
2605 current_tcmh.dirty = true;
2606 update_master_header();
2608 /* Now create the index files. */
2609 tc_stat.commit_step = 0;
2610 tch.datasize = 0;
2611 tc_stat.commit_delayed = false;
2613 for (i = 0; i < TAG_COUNT; i++)
2615 int ret;
2617 if (tagcache_is_numeric_tag(i))
2618 continue;
2620 tc_stat.commit_step++;
2621 ret = build_index(i, &tch, tmpfd);
2622 if (ret <= 0)
2624 close(tmpfd);
2625 logf("tagcache failed init");
2626 if (ret < 0)
2627 remove_files();
2628 else
2629 tc_stat.commit_delayed = true;
2630 tc_stat.commit_step = 0;
2631 read_lock--;
2632 return false;
2636 if (!build_numeric_indices(&tch, tmpfd))
2638 logf("Failure to commit numeric indices");
2639 close(tmpfd);
2640 remove_files();
2641 tc_stat.commit_step = 0;
2642 read_lock--;
2643 return false;
2646 close(tmpfd);
2647 tc_stat.commit_step = 0;
2649 /* Update the master index headers. */
2650 if ( (masterfd = open_master_fd(&tcmh, true)) < 0)
2652 read_lock--;
2653 return false;
2656 tcmh.tch.entry_count += tch.entry_count;
2657 tcmh.tch.datasize = sizeof(struct master_header)
2658 + sizeof(struct index_entry) * tcmh.tch.entry_count
2659 + tch.datasize;
2660 tcmh.dirty = false;
2661 tcmh.commitid++;
2663 lseek(masterfd, 0, SEEK_SET);
2664 ecwrite(masterfd, &tcmh, 1, master_header_ec, tc_stat.econ);
2665 close(masterfd);
2667 logf("tagcache committed");
2668 remove(TAGCACHE_FILE_TEMP);
2669 tc_stat.ready = check_all_headers();
2670 tc_stat.readyvalid = true;
2672 if (local_allocation)
2674 tempbuf = NULL;
2675 tempbuf_size = 0;
2678 #ifdef HAVE_DIRCACHE
2679 /* Rebuild the dircache, if we stole the buffer. */
2680 if (dircache_buffer_stolen)
2681 dircache_build(0);
2682 #endif
2684 #ifdef HAVE_TC_RAMCACHE
2685 /* Reload tagcache. */
2686 if (tc_stat.ramcache_allocated > 0)
2687 tagcache_start_scan();
2688 #endif
2690 read_lock--;
2692 return true;
2695 static void allocate_tempbuf(void)
2697 /* Yeah, malloc would be really nice now :) */
2698 #ifdef __PCTOOL__
2699 tempbuf_size = 32*1024*1024;
2700 tempbuf = malloc(tempbuf_size);
2701 #else
2702 tempbuf = (char *)(((long)audiobuf & ~0x03) + 0x04);
2703 tempbuf_size = (long)audiobufend - (long)audiobuf - 4;
2704 audiobuf += tempbuf_size;
2705 #endif
2708 static void free_tempbuf(void)
2710 if (tempbuf_size == 0)
2711 return ;
2713 #ifdef __PCTOOL__
2714 free(tempbuf);
2715 #else
2716 audiobuf -= tempbuf_size;
2717 #endif
2718 tempbuf = NULL;
2719 tempbuf_size = 0;
2722 long tagcache_increase_serial(void)
2724 long old;
2726 if (!tc_stat.ready)
2727 return -2;
2729 while (read_lock)
2730 sleep(1);
2732 old = current_tcmh.serial++;
2733 if (!update_master_header())
2734 return -1;
2736 return old;
2739 long tagcache_get_serial(void)
2741 return current_tcmh.serial;
2744 long tagcache_get_commitid(void)
2746 return current_tcmh.commitid;
2749 static bool modify_numeric_entry(int masterfd, int idx_id, int tag, long data)
2751 struct index_entry idx;
2753 if (!tc_stat.ready)
2754 return false;
2756 if (!tagcache_is_numeric_tag(tag))
2757 return false;
2759 if (!get_index(masterfd, idx_id, &idx, false))
2760 return false;
2762 idx.tag_seek[tag] = data;
2763 idx.flag |= FLAG_DIRTYNUM;
2765 return write_index(masterfd, idx_id, &idx);
2768 bool tagcache_modify_numeric_entry(struct tagcache_search *tcs,
2769 int tag, long data)
2771 struct master_header myhdr;
2773 if (tcs->masterfd < 0)
2775 if ( (tcs->masterfd = open_master_fd(&myhdr, true)) < 0)
2776 return false;
2779 return modify_numeric_entry(tcs->masterfd, tcs->idx_id, tag, data);
2782 static bool write_tag(int fd, const char *tagstr, const char *datastr)
2784 char buf[512];
2785 int i;
2787 snprintf(buf, sizeof buf, "%s=\"", tagstr);
2788 for (i = strlen(buf); i < (long)sizeof(buf)-3; i++)
2790 if (*datastr == '\0')
2791 break;
2793 if (*datastr == '"')
2795 buf[i] = '\\';
2796 datastr++;
2797 continue;
2800 buf[i] = *(datastr++);
2803 strcpy(&buf[i], "\" ");
2805 write(fd, buf, i + 2);
2807 return true;
2810 static bool read_tag(char *dest, long size,
2811 const char *src, const char *tagstr)
2813 int pos;
2814 char current_tag[32];
2816 while (*src != '\0')
2818 /* Skip all whitespace */
2819 while (*src == ' ')
2820 src++;
2822 if (*src == '\0')
2823 break;
2825 pos = 0;
2826 /* Read in tag name */
2827 while (*src != '=' && *src != ' ')
2829 current_tag[pos] = *src;
2830 src++;
2831 pos++;
2833 if (*src == '\0' || pos >= (long)sizeof(current_tag))
2834 return false;
2836 current_tag[pos] = '\0';
2838 /* Read in tag data */
2840 /* Find the start. */
2841 while (*src != '"' && *src != '\0')
2842 src++;
2844 if (*src == '\0' || *(++src) == '\0')
2845 return false;
2847 /* Read the data. */
2848 for (pos = 0; pos < size; pos++)
2850 if (*src == '\0')
2851 break;
2853 if (*src == '\\' && *(src+1) == '"')
2855 dest[pos] = '"';
2856 src += 2;
2857 continue;
2860 dest[pos] = *src;
2862 if (*src == '"')
2864 src++;
2865 break;
2868 if (*src == '\0')
2869 break;
2871 src++;
2873 dest[pos] = '\0';
2875 if (!strcasecmp(tagstr, current_tag))
2876 return true;
2879 return false;
2882 static int parse_changelog_line(int line_n, const char *buf, void *parameters)
2884 struct index_entry idx;
2885 char tag_data[TAG_MAXLEN+32];
2886 int idx_id;
2887 long masterfd = (long)parameters;
2888 const int import_tags[] = { tag_playcount, tag_rating, tag_playtime, tag_lastplayed,
2889 tag_commitid };
2890 int i;
2891 (void)line_n;
2893 if (*buf == '#')
2894 return 0;
2896 logf("%d/%s", line_n, buf);
2897 if (!read_tag(tag_data, sizeof tag_data, buf, "filename"))
2899 logf("filename missing");
2900 logf("-> %s", buf);
2901 return 0;
2904 idx_id = find_index(tag_data);
2905 if (idx_id < 0)
2907 logf("entry not found");
2908 return 0;
2911 if (!get_index(masterfd, idx_id, &idx, false))
2913 logf("failed to retrieve index entry");
2914 return 0;
2917 /* Stop if tag has already been modified. */
2918 if (idx.flag & FLAG_DIRTYNUM)
2919 return 0;
2921 logf("import: %s", tag_data);
2923 idx.flag |= FLAG_DIRTYNUM;
2924 for (i = 0; i < (long)(sizeof(import_tags)/sizeof(import_tags[0])); i++)
2926 int data;
2928 if (!read_tag(tag_data, sizeof tag_data, buf,
2929 tagcache_tag_to_str(import_tags[i])))
2931 continue;
2934 data = atoi(tag_data);
2935 if (data < 0)
2936 continue;
2938 idx.tag_seek[import_tags[i]] = data;
2940 if (import_tags[i] == tag_lastplayed && data > current_tcmh.serial)
2941 current_tcmh.serial = data;
2942 else if (import_tags[i] == tag_commitid && data >= current_tcmh.commitid)
2943 current_tcmh.commitid = data + 1;
2946 return write_index(masterfd, idx_id, &idx) ? 0 : -5;
2949 #ifndef __PCTOOL__
2950 bool tagcache_import_changelog(void)
2952 struct master_header myhdr;
2953 struct tagcache_header tch;
2954 int clfd;
2955 long masterfd;
2956 char buf[2048];
2958 if (!tc_stat.ready)
2959 return false;
2961 while (read_lock)
2962 sleep(1);
2964 clfd = open(TAGCACHE_FILE_CHANGELOG, O_RDONLY);
2965 if (clfd < 0)
2967 logf("failure to open changelog");
2968 return false;
2971 if ( (masterfd = open_master_fd(&myhdr, true)) < 0)
2973 close(clfd);
2974 return false;
2977 write_lock++;
2979 filenametag_fd = open_tag_fd(&tch, tag_filename, false);
2981 fast_readline(clfd, buf, sizeof buf, (long *)masterfd,
2982 parse_changelog_line);
2984 close(clfd);
2985 close(masterfd);
2987 if (filenametag_fd >= 0)
2988 close(filenametag_fd);
2990 write_lock--;
2992 update_master_header();
2994 return true;
2996 #endif
2998 bool tagcache_create_changelog(struct tagcache_search *tcs)
3000 struct master_header myhdr;
3001 struct index_entry idx;
3002 char buf[TAG_MAXLEN+32];
3003 char temp[32];
3004 int clfd;
3005 int i, j;
3007 if (!tc_stat.ready)
3008 return false;
3010 if (!tagcache_search(tcs, tag_filename))
3011 return false;
3013 /* Initialize the changelog */
3014 clfd = open(TAGCACHE_FILE_CHANGELOG, O_WRONLY | O_CREAT | O_TRUNC);
3015 if (clfd < 0)
3017 logf("failure to open changelog");
3018 return false;
3021 if (tcs->masterfd < 0)
3023 if ( (tcs->masterfd = open_master_fd(&myhdr, false)) < 0)
3024 return false;
3026 else
3028 lseek(tcs->masterfd, 0, SEEK_SET);
3029 ecread(tcs->masterfd, &myhdr, 1, master_header_ec, tc_stat.econ);
3032 write(clfd, "## Changelog version 1\n", 23);
3034 for (i = 0; i < myhdr.tch.entry_count; i++)
3036 if (ecread(tcs->masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
3037 != sizeof(struct index_entry))
3039 logf("read error #9");
3040 tagcache_search_finish(tcs);
3041 close(clfd);
3042 return false;
3045 /* Skip until the entry found has been modified. */
3046 if (! (idx.flag & FLAG_DIRTYNUM) )
3047 continue;
3049 /* Skip deleted entries too. */
3050 if (idx.flag & FLAG_DELETED)
3051 continue;
3053 /* Now retrieve all tags. */
3054 for (j = 0; j < TAG_COUNT; j++)
3056 if (tagcache_is_numeric_tag(j))
3058 snprintf(temp, sizeof temp, "%d", idx.tag_seek[j]);
3059 write_tag(clfd, tagcache_tag_to_str(j), temp);
3060 continue;
3063 tcs->type = j;
3064 tagcache_retrieve(tcs, i, tcs->type, buf, sizeof buf);
3065 write_tag(clfd, tagcache_tag_to_str(j), buf);
3068 write(clfd, "\n", 1);
3069 do_timed_yield();
3072 close(clfd);
3074 tagcache_search_finish(tcs);
3076 return true;
3079 static bool delete_entry(long idx_id)
3081 int fd;
3082 int tag, i;
3083 struct index_entry idx, myidx;
3084 struct master_header myhdr;
3085 char buf[TAG_MAXLEN+32];
3086 int in_use[TAG_COUNT];
3088 logf("delete_entry(): %ld", idx_id);
3090 #ifdef HAVE_TC_RAMCACHE
3091 /* At first mark the entry removed from ram cache. */
3092 if (tc_stat.ramcache)
3093 hdr->indices[idx_id].flag |= FLAG_DELETED;
3094 #endif
3096 if ( (fd = open_master_fd(&myhdr, true) ) < 0)
3097 return false;
3099 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR);
3100 if (ecread(fd, &myidx, 1, index_entry_ec, tc_stat.econ)
3101 != sizeof(struct index_entry))
3103 logf("delete_entry(): read error");
3104 close(fd);
3105 return false;
3108 myidx.flag |= FLAG_DELETED;
3109 lseek(fd, -sizeof(struct index_entry), SEEK_CUR);
3110 if (ecwrite(fd, &myidx, 1, index_entry_ec, tc_stat.econ)
3111 != sizeof(struct index_entry))
3113 logf("delete_entry(): write_error");
3114 close(fd);
3115 return false;
3118 /* Now check which tags are no longer in use (if any) */
3119 for (tag = 0; tag < TAG_COUNT; tag++)
3120 in_use[tag] = 0;
3122 lseek(fd, sizeof(struct master_header), SEEK_SET);
3123 for (i = 0; i < myhdr.tch.entry_count; i++)
3125 struct index_entry *idxp;
3127 #ifdef HAVE_TC_RAMCACHE
3128 /* Use RAM DB if available for greater speed */
3129 if (tc_stat.ramcache)
3130 idxp = &hdr->indices[i];
3131 else
3132 #endif
3134 if (ecread(fd, &idx, 1, index_entry_ec, tc_stat.econ)
3135 != sizeof(struct index_entry))
3137 logf("delete_entry(): read error #2");
3138 close(fd);
3139 return false;
3141 idxp = &idx;
3144 if (idxp->flag & FLAG_DELETED)
3145 continue;
3147 for (tag = 0; tag < TAG_COUNT; tag++)
3149 if (tagcache_is_numeric_tag(tag))
3150 continue;
3152 if (idxp->tag_seek[tag] == myidx.tag_seek[tag])
3153 in_use[tag]++;
3157 close(fd);
3159 /* Now delete all tags no longer in use. */
3160 for (tag = 0; tag < TAG_COUNT; tag++)
3162 if (tagcache_is_numeric_tag(tag))
3163 continue;
3165 if (in_use[tag])
3167 logf("in use: %d/%d", tag, in_use[tag]);
3168 continue;
3171 #ifdef HAVE_TC_RAMCACHE
3172 /* Delete from ram. */
3173 if (tc_stat.ramcache && tag != tag_filename)
3175 struct tagfile_entry *tagentry = get_tag(&myidx, tag);
3176 tagentry->tag_data[0] = '\0';
3178 #endif
3180 /* Open the index file, which contains the tag names. */
3181 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag);
3182 fd = open(buf, O_RDWR);
3184 if (fd < 0)
3186 logf("open failed");
3187 return false;
3190 /* Skip the header block */
3191 lseek(fd, myidx.tag_seek[tag] + sizeof(struct tagfile_entry), SEEK_SET);
3193 /* Debug, print 10 first characters of the tag
3194 read(fd, buf, 10);
3195 buf[10]='\0';
3196 logf("TAG:%s", buf);
3197 lseek(fd, -10, SEEK_CUR);
3200 /* Write first data byte in tag as \0 */
3201 write(fd, "", 1);
3203 /* Now tag data has been removed */
3204 close(fd);
3207 return true;
3210 #ifndef __PCTOOL__
3212 * Returns true if there is an event waiting in the queue
3213 * that requires the current operation to be aborted.
3215 static bool check_event_queue(void)
3217 struct event ev;
3219 queue_wait_w_tmo(&tagcache_queue, &ev, 0);
3220 switch (ev.id)
3222 case Q_STOP_SCAN:
3223 case SYS_POWEROFF:
3224 case SYS_USB_CONNECTED:
3225 /* Put the event back into the queue. */
3226 queue_post(&tagcache_queue, ev.id, ev.data);
3227 return true;
3230 return false;
3232 #endif
3234 #ifdef HAVE_TC_RAMCACHE
3235 static bool allocate_tagcache(void)
3237 struct master_header tcmh;
3238 int fd;
3240 /* Load the header. */
3241 if ( (fd = open_master_fd(&tcmh, false)) < 0)
3243 hdr = NULL;
3244 return false;
3247 close(fd);
3249 /**
3250 * Now calculate the required cache size plus
3251 * some extra space for alignment fixes.
3253 tc_stat.ramcache_allocated = tcmh.tch.datasize + 128 + TAGCACHE_RESERVE +
3254 sizeof(struct ramcache_header) + TAG_COUNT*sizeof(void *);
3255 hdr = buffer_alloc(tc_stat.ramcache_allocated + 128);
3256 memset(hdr, 0, sizeof(struct ramcache_header));
3257 memcpy(&hdr->h, &tcmh, sizeof(struct master_header));
3258 hdr->indices = (struct index_entry *)(hdr + 1);
3259 logf("tagcache: %d bytes allocated.", tc_stat.ramcache_allocated);
3261 return true;
3264 # ifdef HAVE_EEPROM_SETTINGS
3265 static bool tagcache_dumpload(void)
3267 struct statefile_header shdr;
3268 int fd, rc;
3269 long offpos;
3270 int i;
3272 fd = open(TAGCACHE_STATEFILE, O_RDONLY);
3273 if (fd < 0)
3275 logf("no tagcache statedump");
3276 return false;
3279 /* Check the statefile memory placement */
3280 hdr = buffer_alloc(0);
3281 rc = read(fd, &shdr, sizeof(struct statefile_header));
3282 if (rc != sizeof(struct statefile_header)
3283 /* || (long)hdr != (long)shdr.hdr */)
3285 logf("incorrect statefile");
3286 hdr = NULL;
3287 close(fd);
3288 return false;
3291 offpos = (long)hdr - (long)shdr.hdr;
3293 /* Lets allocate real memory and load it */
3294 hdr = buffer_alloc(shdr.tc_stat.ramcache_allocated);
3295 rc = read(fd, hdr, shdr.tc_stat.ramcache_allocated);
3296 close(fd);
3298 if (rc != shdr.tc_stat.ramcache_allocated)
3300 logf("read failure!");
3301 hdr = NULL;
3302 return false;
3305 memcpy(&tc_stat, &shdr.tc_stat, sizeof(struct tagcache_stat));
3307 /* Now fix the pointers */
3308 hdr->indices = (struct index_entry *)((long)hdr->indices + offpos);
3309 for (i = 0; i < TAG_COUNT; i++)
3310 hdr->tags[i] += offpos;
3312 return true;
3315 static bool tagcache_dumpsave(void)
3317 struct statefile_header shdr;
3318 int fd;
3320 if (!tc_stat.ramcache)
3321 return false;
3323 fd = open(TAGCACHE_STATEFILE, O_WRONLY | O_CREAT | O_TRUNC);
3324 if (fd < 0)
3326 logf("failed to create a statedump");
3327 return false;
3330 /* Create the header */
3331 shdr.hdr = hdr;
3332 memcpy(&shdr.tc_stat, &tc_stat, sizeof(struct tagcache_stat));
3333 write(fd, &shdr, sizeof(struct statefile_header));
3335 /* And dump the data too */
3336 write(fd, hdr, tc_stat.ramcache_allocated);
3337 close(fd);
3339 return true;
3341 # endif
3343 static bool load_tagcache(void)
3345 struct tagcache_header *tch;
3346 long bytesleft = tc_stat.ramcache_allocated;
3347 struct index_entry *idx;
3348 int rc, fd;
3349 char *p;
3350 int i, tag;
3352 # ifdef HAVE_DIRCACHE
3353 while (dircache_is_initializing())
3354 sleep(1);
3355 # endif
3357 logf("loading tagcache to ram...");
3359 fd = open(TAGCACHE_FILE_MASTER, O_RDONLY);
3360 if (fd < 0)
3362 logf("tagcache open failed");
3363 return false;
3366 if (ecread(fd, &hdr->h, 1, master_header_ec, tc_stat.econ)
3367 != sizeof(struct master_header)
3368 || hdr->h.tch.magic != TAGCACHE_MAGIC)
3370 logf("incorrect header");
3371 return false;
3374 idx = hdr->indices;
3376 /* Load the master index table. */
3377 for (i = 0; i < hdr->h.tch.entry_count; i++)
3379 rc = ecread(fd, idx, 1, index_entry_ec, tc_stat.econ);
3380 if (rc != sizeof(struct index_entry))
3382 logf("read error #10");
3383 close(fd);
3384 return false;
3387 bytesleft -= sizeof(struct index_entry);
3388 if (bytesleft < 0 || ((long)idx - (long)hdr->indices) >= tc_stat.ramcache_allocated)
3390 logf("too big tagcache.");
3391 close(fd);
3392 return false;
3395 idx++;
3398 close(fd);
3400 /* Load the tags. */
3401 p = (char *)idx;
3402 for (tag = 0; tag < TAG_COUNT; tag++)
3404 struct tagfile_entry *fe;
3405 char buf[TAG_MAXLEN+32];
3407 if (tagcache_is_numeric_tag(tag))
3408 continue ;
3410 //p = ((void *)p+1);
3411 p = (char *)((long)p & ~0x03) + 0x04;
3412 hdr->tags[tag] = p;
3414 /* Check the header. */
3415 tch = (struct tagcache_header *)p;
3416 p += sizeof(struct tagcache_header);
3418 if ( (fd = open_tag_fd(tch, tag, false)) < 0)
3419 return false;
3421 for (hdr->entry_count[tag] = 0;
3422 hdr->entry_count[tag] < tch->entry_count;
3423 hdr->entry_count[tag]++)
3425 long pos;
3427 if (do_timed_yield())
3429 /* Abort if we got a critical event in queue */
3430 if (check_event_queue())
3431 return false;
3434 fe = (struct tagfile_entry *)p;
3435 pos = lseek(fd, 0, SEEK_CUR);
3436 rc = ecread(fd, fe, 1, tagfile_entry_ec, tc_stat.econ);
3437 if (rc != sizeof(struct tagfile_entry))
3439 /* End of lookup table. */
3440 logf("read error #11");
3441 close(fd);
3442 return false;
3445 /* We have a special handling for the filename tags. */
3446 if (tag == tag_filename)
3448 # ifdef HAVE_DIRCACHE
3449 const struct dircache_entry *dc;
3450 # endif
3452 // FIXME: This is wrong!
3453 // idx = &hdr->indices[hdr->entry_count[i]];
3454 idx = &hdr->indices[fe->idx_id];
3456 if (fe->tag_length >= (long)sizeof(buf)-1)
3458 read(fd, buf, 10);
3459 buf[10] = '\0';
3460 logf("TAG:%s", buf);
3461 logf("too long filename");
3462 close(fd);
3463 return false;
3466 rc = read(fd, buf, fe->tag_length);
3467 if (rc != fe->tag_length)
3469 logf("read error #12");
3470 close(fd);
3471 return false;
3474 /* Check if the entry has already been removed */
3475 if (idx->flag & FLAG_DELETED)
3476 continue;
3478 /* This flag must not be used yet. */
3479 if (idx->flag & FLAG_DIRCACHE)
3481 logf("internal error!");
3482 close(fd);
3483 return false;
3486 if (idx->tag_seek[tag] != pos)
3488 logf("corrupt data structures!");
3489 close(fd);
3490 return false;
3493 # ifdef HAVE_DIRCACHE
3494 if (dircache_is_enabled())
3496 dc = dircache_get_entry_ptr(buf);
3497 if (dc == NULL)
3499 logf("Entry no longer valid.");
3500 logf("-> %s", buf);
3501 delete_entry(fe->idx_id);
3502 continue ;
3505 idx->flag |= FLAG_DIRCACHE;
3506 FLAG_SET_ATTR(idx->flag, fe->idx_id);
3507 idx->tag_seek[tag_filename] = (long)dc;
3509 else
3510 # endif
3513 # if 0 /* Maybe we could enable this for flash players. Too slow otherwise. */
3514 /* Check if entry has been removed. */
3515 if (global_settings.tagcache_autoupdate)
3517 int testfd;
3519 testfd = open(buf, O_RDONLY);
3520 if (testfd < 0)
3522 logf("Entry no longer valid.");
3523 logf("-> %s", buf);
3524 delete_entry(fe->idx_id);
3525 continue;
3527 close(testfd);
3529 # endif
3532 continue ;
3535 bytesleft -= sizeof(struct tagfile_entry) + fe->tag_length;
3536 if (bytesleft < 0)
3538 logf("too big tagcache #2");
3539 logf("tl: %d", fe->tag_length);
3540 logf("bl: %ld", bytesleft);
3541 close(fd);
3542 return false;
3545 p = fe->tag_data;
3546 rc = read(fd, fe->tag_data, fe->tag_length);
3547 p += rc;
3549 if (rc != fe->tag_length)
3551 logf("read error #13");
3552 logf("rc=0x%04x", rc); // 0x431
3553 logf("len=0x%04x", fe->tag_length); // 0x4000
3554 logf("pos=0x%04lx", lseek(fd, 0, SEEK_CUR)); // 0x433
3555 logf("tag=0x%02x", tag); // 0x00
3556 close(fd);
3557 return false;
3560 close(fd);
3563 tc_stat.ramcache_used = tc_stat.ramcache_allocated - bytesleft;
3564 logf("tagcache loaded into ram!");
3566 return true;
3568 #endif /* HAVE_TC_RAMCACHE */
3570 static bool check_deleted_files(void)
3572 int fd, testfd;
3573 char buf[TAG_MAXLEN+32];
3574 struct tagfile_entry tfe;
3576 logf("reverse scan...");
3577 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag_filename);
3578 fd = open(buf, O_RDONLY);
3580 if (fd < 0)
3582 logf("%s open fail", buf);
3583 return false;
3586 lseek(fd, sizeof(struct tagcache_header), SEEK_SET);
3587 while (ecread(fd, &tfe, 1, tagfile_entry_ec, tc_stat.econ)
3588 == sizeof(struct tagfile_entry)
3589 #ifndef __PCTOOL__
3590 && !check_event_queue()
3591 #endif
3594 if (tfe.tag_length >= (long)sizeof(buf)-1)
3596 logf("too long tag");
3597 close(fd);
3598 return false;
3601 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
3603 logf("read error #14");
3604 close(fd);
3605 return false;
3608 /* Check if the file has already deleted from the db. */
3609 if (*buf == '\0')
3610 continue;
3612 /* Now check if the file exists. */
3613 testfd = open(buf, O_RDONLY);
3614 if (testfd < 0)
3616 logf("Entry no longer valid.");
3617 logf("-> %s / %d", buf, tfe.tag_length);
3618 delete_entry(tfe.idx_id);
3620 close(testfd);
3623 close(fd);
3625 logf("done");
3627 return true;
3630 static bool check_dir(const char *dirname)
3632 DIRCACHED *dir;
3633 int len;
3634 int success = false;
3636 dir = opendir_cached(dirname);
3637 if (!dir)
3639 logf("tagcache: opendir_cached() failed");
3640 return false;
3643 /* Recursively scan the dir. */
3644 #ifdef __PCTOOL__
3645 while (1)
3646 #else
3647 while (!check_event_queue())
3648 #endif
3650 struct dircache_entry *entry;
3652 entry = readdir_cached(dir);
3654 if (entry == NULL)
3656 success = true;
3657 break ;
3660 if (!strcmp((char *)entry->d_name, ".") ||
3661 !strcmp((char *)entry->d_name, ".."))
3662 continue;
3664 yield();
3666 len = strlen(curpath);
3667 snprintf(&curpath[len], curpath_size - len, "/%s",
3668 entry->d_name);
3670 processed_dir_count++;
3671 if (entry->attribute & ATTR_DIRECTORY)
3672 check_dir(curpath);
3673 else
3674 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
3675 add_tagcache(curpath, dir->internal_entry);
3676 #else
3677 add_tagcache(curpath);
3678 #endif
3680 curpath[len] = '\0';
3683 closedir_cached(dir);
3685 return success;
3688 void build_tagcache(const char *path)
3690 struct tagcache_header header;
3691 bool ret;
3693 curpath[0] = '\0';
3694 data_size = 0;
3695 total_entry_count = 0;
3696 processed_dir_count = 0;
3698 #ifdef HAVE_DIRCACHE
3699 while (dircache_is_initializing())
3700 sleep(1);
3701 #endif
3703 logf("updating tagcache");
3705 cachefd = open(TAGCACHE_FILE_TEMP, O_RDONLY);
3706 if (cachefd >= 0)
3708 logf("skipping, cache already waiting for commit");
3709 close(cachefd);
3710 return ;
3713 cachefd = open(TAGCACHE_FILE_TEMP, O_RDWR | O_CREAT | O_TRUNC);
3714 if (cachefd < 0)
3716 logf("master file open failed: %s", TAGCACHE_FILE_TEMP);
3717 return ;
3720 filenametag_fd = open_tag_fd(&header, tag_filename, false);
3722 cpu_boost(true);
3724 logf("Scanning files...");
3725 /* Scan for new files. */
3726 memset(&header, 0, sizeof(struct tagcache_header));
3727 write(cachefd, &header, sizeof(struct tagcache_header));
3729 if (strcmp("/", path) != 0)
3730 strcpy(curpath, path);
3731 ret = check_dir(path);
3733 /* Write the header. */
3734 header.magic = TAGCACHE_MAGIC;
3735 header.datasize = data_size;
3736 header.entry_count = total_entry_count;
3737 lseek(cachefd, 0, SEEK_SET);
3738 write(cachefd, &header, sizeof(struct tagcache_header));
3739 close(cachefd);
3741 if (filenametag_fd >= 0)
3743 close(filenametag_fd);
3744 filenametag_fd = -1;
3747 if (!ret)
3749 logf("Aborted.");
3750 cpu_boost(false);
3751 return ;
3754 /* Commit changes to the database. */
3755 #ifdef __PCTOOL__
3756 allocate_tempbuf();
3757 #endif
3758 if (commit())
3760 remove(TAGCACHE_FILE_TEMP);
3761 logf("tagcache built!");
3763 #ifdef __PCTOOL__
3764 free_tempbuf();
3765 #endif
3767 #ifdef HAVE_TC_RAMCACHE
3768 if (hdr)
3770 /* Import runtime statistics if we just initialized the db. */
3771 if (hdr->h.serial == 0)
3772 queue_post(&tagcache_queue, Q_IMPORT_CHANGELOG, 0);
3774 #endif
3776 cpu_boost(false);
3779 #ifdef HAVE_TC_RAMCACHE
3780 static void load_ramcache(void)
3782 if (!hdr)
3783 return ;
3785 cpu_boost(true);
3787 /* At first we should load the cache (if exists). */
3788 tc_stat.ramcache = load_tagcache();
3790 if (!tc_stat.ramcache)
3792 /* If loading failed, it must indicate some problem with the db
3793 * so disable it entirely to prevent further issues. */
3794 tc_stat.ready = false;
3795 hdr = NULL;
3798 cpu_boost(false);
3801 void tagcache_unload_ramcache(void)
3803 tc_stat.ramcache = false;
3804 /* Just to make sure there is no statefile present. */
3805 // remove(TAGCACHE_STATEFILE);
3807 #endif
3809 #ifndef __PCTOOL__
3810 static void tagcache_thread(void)
3812 struct event ev;
3813 bool check_done = false;
3815 /* If the previous cache build/update was interrupted, commit
3816 * the changes first in foreground. */
3817 cpu_boost(true);
3818 allocate_tempbuf();
3819 commit();
3820 free_tempbuf();
3822 #ifdef HAVE_TC_RAMCACHE
3823 # ifdef HAVE_EEPROM_SETTINGS
3824 if (firmware_settings.initialized && firmware_settings.disk_clean)
3825 check_done = tagcache_dumpload();
3827 remove(TAGCACHE_STATEFILE);
3828 # endif
3830 /* Allocate space for the tagcache if found on disk. */
3831 if (global_settings.tagcache_ram && !tc_stat.ramcache)
3832 allocate_tagcache();
3833 #endif
3835 cpu_boost(false);
3836 tc_stat.initialized = true;
3838 /* Don't delay bootup with the header check but do it on background. */
3839 sleep(HZ);
3840 tc_stat.ready = check_all_headers();
3841 tc_stat.readyvalid = true;
3843 while (1)
3845 queue_wait_w_tmo(&tagcache_queue, &ev, HZ);
3847 switch (ev.id)
3849 case Q_IMPORT_CHANGELOG:
3850 tagcache_import_changelog();
3851 break;
3853 case Q_REBUILD:
3854 remove_files();
3855 build_tagcache("/");
3856 break;
3858 case Q_UPDATE:
3859 build_tagcache("/");
3860 #ifdef HAVE_TC_RAMCACHE
3861 load_ramcache();
3862 #endif
3863 check_deleted_files();
3864 break ;
3866 case Q_START_SCAN:
3867 check_done = false;
3868 case SYS_TIMEOUT:
3869 if (check_done || !tc_stat.ready)
3870 break ;
3872 #ifdef HAVE_TC_RAMCACHE
3873 if (!tc_stat.ramcache && global_settings.tagcache_ram)
3875 load_ramcache();
3876 if (tc_stat.ramcache && global_settings.tagcache_autoupdate)
3877 build_tagcache("/");
3879 else
3880 #endif
3881 if (global_settings.tagcache_autoupdate)
3883 build_tagcache("/");
3884 /* Don't do auto removal without dircache (very slow). */
3885 #ifdef HAVE_DIRCACHE
3886 if (dircache_is_enabled())
3887 check_deleted_files();
3888 #endif
3891 logf("tagcache check done");
3893 check_done = true;
3894 break ;
3896 case Q_STOP_SCAN:
3897 break ;
3899 case SYS_POWEROFF:
3900 break ;
3902 #ifndef SIMULATOR
3903 case SYS_USB_CONNECTED:
3904 logf("USB: TagCache");
3905 usb_acknowledge(SYS_USB_CONNECTED_ACK);
3906 usb_wait_for_disconnect(&tagcache_queue);
3907 break ;
3908 #endif
3913 bool tagcache_prepare_shutdown(void)
3915 if (tagcache_get_commit_step() > 0)
3916 return false;
3918 tagcache_stop_scan();
3919 while (read_lock || write_lock)
3920 sleep(1);
3922 return true;
3925 void tagcache_shutdown(void)
3927 #ifdef HAVE_EEPROM_SETTINGS
3928 if (tc_stat.ramcache)
3929 tagcache_dumpsave();
3930 #endif
3933 static int get_progress(void)
3935 int total_count = -1;
3937 #ifdef HAVE_DIRCACHE
3938 if (dircache_is_enabled())
3940 total_count = dircache_get_entry_count();
3942 else
3943 #endif
3944 #ifdef HAVE_TC_RAMCACHE
3946 if (hdr && tc_stat.ramcache)
3947 total_count = hdr->h.tch.entry_count;
3949 #endif
3951 if (total_count < 0)
3952 return -1;
3954 return processed_dir_count * 100 / total_count;
3957 struct tagcache_stat* tagcache_get_stat(void)
3959 tc_stat.progress = get_progress();
3960 tc_stat.processed_entries = processed_dir_count;
3962 return &tc_stat;
3965 void tagcache_start_scan(void)
3967 queue_post(&tagcache_queue, Q_START_SCAN, 0);
3970 bool tagcache_update(void)
3972 if (!tc_stat.ready)
3973 return false;
3975 queue_post(&tagcache_queue, Q_UPDATE, 0);
3976 return false;
3979 bool tagcache_rebuild()
3981 queue_post(&tagcache_queue, Q_REBUILD, 0);
3982 return false;
3985 void tagcache_stop_scan(void)
3987 queue_post(&tagcache_queue, Q_STOP_SCAN, 0);
3990 #ifdef HAVE_TC_RAMCACHE
3991 bool tagcache_is_ramcache(void)
3993 return tc_stat.ramcache;
3995 #endif
3997 #endif /* !__PCTOOL__ */
4000 void tagcache_init(void)
4002 memset(&tc_stat, 0, sizeof(struct tagcache_stat));
4003 memset(&current_tcmh, 0, sizeof(struct master_header));
4004 filenametag_fd = -1;
4005 write_lock = read_lock = 0;
4007 #ifndef __PCTOOL__
4008 queue_init(&tagcache_queue, true);
4009 create_thread(tagcache_thread, tagcache_stack,
4010 sizeof(tagcache_stack), tagcache_thread_name
4011 IF_PRIO(, PRIORITY_BACKGROUND)
4012 IF_COP(, CPU, false));
4013 #else
4014 tc_stat.initialized = true;
4015 allocate_tempbuf();
4016 commit();
4017 free_tempbuf();
4018 tc_stat.ready = check_all_headers();
4019 #endif
4022 #ifdef __PCTOOL__
4023 void tagcache_reverse_scan(void)
4025 logf("Checking for deleted files");
4026 check_deleted_files();
4028 #endif
4030 bool tagcache_is_initialized(void)
4032 return tc_stat.initialized;
4034 bool tagcache_is_usable(void)
4036 return tc_stat.initialized && tc_stat.ready;
4038 int tagcache_get_commit_step(void)
4040 return tc_stat.commit_step;
4042 int tagcache_get_max_commit_step(void)
4044 return (int)(sizeof(sorted_tags)/sizeof(sorted_tags[0]))+1;