1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
25 * ----------x---------x------------------x-----
27 * +---------------x-------+ | TagCache | Libraries
28 * | Modification routines | | Core |
29 * +-x---------x-----------+ | |
31 * | +------x-------------x-+ +-------------x-----+ |
32 * | | x==x Filters & clauses | |
33 * | | Search routines | +-------------------+ |
34 * | | x============================x DirCache
35 * | +-x--------------------+ | (optional)
37 * | | +-------------------------------+ +---------+ |
38 * | | | DB Commit (sort,unique,index) | | | |
39 * | | +-x--------------------------x--+ | Control | |
40 * | | | (R/W) | (R) | Thread | |
41 * | | | +----------------------+ | | | |
42 * | | | | TagCache DB Builder | | +---------+ |
43 * | | | +-x-------------x------+ | |
44 * | | | | (R) | (W) | |
45 * | | | | +--x--------x---------+ |
46 * | | | | | Temporary Commit DB | |
47 * | | | | +---------------------+ |
48 * +-x----x-------x--+ |
49 * | TagCache RAM DB x==\(W) +-----------------+ |
50 * +-----------------+ \===x | |
51 * | | | | (R) | Ram DB Loader x============x DirCache
52 * +-x----x---x---x---+ /==x | | (optional)
53 * | Tagcache Disk DB x==/ +-----------------+ |
54 * +------------------+ |
58 /*#define LOGF_ENABLE*/
64 #include "ata_idle_notify.h"
69 #include "string-extra.h"
76 #include "filefuncs.h"
83 #include "eeprom_settings.h"
87 #define yield() do { } while(0)
88 #define sim_sleep(timeout) do { } while(0)
89 #define do_timed_yield() do { } while(0)
93 /* Tag Cache thread. */
94 static struct event_queue tagcache_queue
;
95 static long tagcache_stack
[(DEFAULT_STACK_SIZE
+ 0x4000)/sizeof(long)];
96 static const char tagcache_thread_name
[] = "tagcache";
99 /* Previous path when scanning directory tree recursively. */
100 static char curpath
[TAG_MAXLEN
+32];
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 #define SORTED_TAGS_COUNT 8
110 #define TAGCACHE_IS_UNIQUE(tag) (BIT_N(tag) & TAGCACHE_UNIQUE_TAGS)
111 #define TAGCACHE_IS_SORTED(tag) (BIT_N(tag) & TAGCACHE_SORTED_TAGS)
112 #define TAGCACHE_IS_NUMERIC_OR_NONUNIQUE(tag) \
113 (BIT_N(tag) & (TAGCACHE_NUMERIC_TAGS | ~TAGCACHE_UNIQUE_TAGS))
114 /* Tags we want to get sorted (loaded to the tempbuf). */
115 #define TAGCACHE_SORTED_TAGS ((1LU << tag_artist) | (1LU << tag_album) | \
116 (1LU << tag_genre) | (1LU << tag_composer) | (1LU << tag_comment) | \
117 (1LU << tag_albumartist) | (1LU << tag_grouping) | (1LU << tag_title))
119 /* Uniqued tags (we can use these tags with filters and conditional clauses). */
120 #define TAGCACHE_UNIQUE_TAGS ((1LU << tag_artist) | (1LU << tag_album) | \
121 (1LU << tag_genre) | (1LU << tag_composer) | (1LU << tag_comment) | \
122 (1LU << tag_albumartist) | (1LU << tag_grouping))
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", "grouping", "year",
127 "discnumber", "tracknumber", "bitrate", "length", "playcount", "rating",
128 "playtime", "lastplayed", "commitid", "mtime" };
130 /* Status information of the tagcache. */
131 static struct tagcache_stat tc_stat
;
133 /* Queue commands. */
134 enum tagcache_queue
{
141 /* Internal tagcache command queue. */
142 CMD_UPDATE_MASTER_HEADER
,
146 struct tagcache_command_entry
{
154 static struct tagcache_command_entry command_queue
[TAGCACHE_COMMAND_QUEUE_LENGTH
];
155 static volatile int command_queue_widx
= 0;
156 static volatile int command_queue_ridx
= 0;
157 static struct mutex command_queue_mutex
;
160 /* Tag database structures. */
162 /* Variable-length tag entry in tag files. */
163 struct tagfile_entry
{
164 int32_t tag_length
; /* Length of the data in bytes including '\0' */
165 int32_t idx_id
; /* Corresponding entry location in index file of not unique tags */
166 char tag_data
[0]; /* Begin of the tag data */
169 /* Fixed-size tag entry in master db index. */
171 int32_t tag_seek
[TAG_COUNT
]; /* Location of tag data or numeric tag data */
172 int32_t flag
; /* Status flags */
175 /* Header is the same in every file. */
176 struct tagcache_header
{
177 int32_t magic
; /* Header version number */
178 int32_t datasize
; /* Data size in bytes */
179 int32_t entry_count
; /* Number of entries in this file */
182 struct master_header
{
183 struct tagcache_header tch
;
184 int32_t serial
; /* Increasing counting number */
185 int32_t commitid
; /* Number of commits so far */
189 /* For the endianess correction */
190 static const char *tagfile_entry_ec
= "ll";
192 Note: This should be (1 + TAG_COUNT) amount of l's.
194 static const char *index_entry_ec
= "lllllllllllllllllllll";
196 static const char *tagcache_header_ec
= "lll";
197 static const char *master_header_ec
= "llllll";
199 static struct master_header current_tcmh
;
201 #ifdef HAVE_TC_RAMCACHE
202 /* Header is created when loading database to ram. */
203 struct ramcache_header
{
204 struct master_header h
; /* Header from the master index */
205 struct index_entry
*indices
; /* Master index file content */
206 char *tags
[TAG_COUNT
]; /* Tag file content (not including filename tag) */
207 int entry_count
[TAG_COUNT
]; /* Number of entries in the indices. */
210 # ifdef HAVE_EEPROM_SETTINGS
211 struct statefile_header
{
212 struct ramcache_header
*hdr
;
213 struct tagcache_stat tc_stat
;
217 /* Pointer to allocated ramcache_header */
218 static struct ramcache_header
*hdr
;
222 * Full tag entries stored in a temporary file waiting
223 * for commit to the cache. */
224 struct temp_file_entry
{
225 long tag_offset
[TAG_COUNT
];
226 short tag_length
[TAG_COUNT
];
232 struct tempbuf_id_list
{
234 struct tempbuf_id_list
*next
;
237 struct tempbuf_searchidx
{
241 struct tempbuf_id_list idlist
;
244 /* Lookup buffer for fixing messed up index while after sorting. */
245 static long commit_entry_count
;
246 static long lookup_buffer_depth
;
247 static struct tempbuf_searchidx
**lookup
;
249 /* Used when building the temporary file. */
250 static int cachefd
= -1, filenametag_fd
;
251 static int total_entry_count
= 0;
252 static int data_size
= 0;
253 static int processed_dir_count
;
255 /* Thread safe locking */
256 static volatile int write_lock
;
257 static volatile int read_lock
;
259 static bool delete_entry(long idx_id
);
261 const char* tagcache_tag_to_str(int tag
)
263 return tags_str
[tag
];
266 /* Helper functions for the two most read/write data structure: tagfile_entry and index_entry */
267 static ssize_t
ecread_tagfile_entry(int fd
, struct tagfile_entry
*buf
)
269 return ecread(fd
, buf
, 1, tagfile_entry_ec
, tc_stat
.econ
);
272 static ssize_t
ecread_index_entry(int fd
, struct index_entry
*buf
)
274 return ecread(fd
, buf
, 1, index_entry_ec
, tc_stat
.econ
);
277 static ssize_t
ecwrite_index_entry(int fd
, struct index_entry
*buf
)
279 return ecwrite(fd
, buf
, 1, index_entry_ec
, tc_stat
.econ
);
284 * Returns true if specified flag is still present, i.e., dircache
285 * has not been reloaded.
287 static bool is_dircache_intact(void)
289 return dircache_get_appflag(DIRCACHE_APPFLAG_TAGCACHE
);
293 static int open_tag_fd(struct tagcache_header
*hdr
, int tag
, bool write
)
296 char buf
[MAX_PATH
], path
[MAX_PATH
];
300 if (TAGCACHE_IS_NUMERIC(tag
) || tag
< 0 || tag
>= TAG_COUNT
)
303 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag
);
304 file
= get_user_file_path(buf
, IS_FILE
| NEED_WRITE
, path
, sizeof(path
));
306 fd
= open(file
, write
? O_RDWR
: O_RDONLY
);
309 logf("tag file open failed: tag=%d write=%d file=%s", tag
, write
, buf
);
310 tc_stat
.ready
= false;
314 /* Check the header. */
315 rc
= ecread(fd
, hdr
, 1, tagcache_header_ec
, tc_stat
.econ
);
316 if (hdr
->magic
!= TAGCACHE_MAGIC
|| rc
!= sizeof(struct tagcache_header
))
318 logf("header error");
319 tc_stat
.ready
= false;
327 static int open_master_fd(struct master_header
*hdr
, bool write
)
333 fd
= open(get_user_file_path(TAGCACHE_FILE_MASTER
,
336 write
? O_RDWR
: O_RDONLY
);
339 logf("master file open failed for R/W");
340 tc_stat
.ready
= false;
344 tc_stat
.econ
= false;
346 /* Check the header. */
347 rc
= read(fd
, hdr
, sizeof(struct master_header
));
348 if (hdr
->tch
.magic
== TAGCACHE_MAGIC
&& rc
== sizeof(struct master_header
))
354 /* Trying to read again, this time with endianess correction enabled. */
355 lseek(fd
, 0, SEEK_SET
);
357 rc
= ecread(fd
, hdr
, 1, master_header_ec
, true);
358 if (hdr
->tch
.magic
!= TAGCACHE_MAGIC
|| rc
!= sizeof(struct master_header
))
360 logf("header error");
361 tc_stat
.ready
= false;
372 static bool do_timed_yield(void)
374 /* Sorting can lock up for quite a while, so yield occasionally */
375 static long wakeup_tick
= 0;
376 if (TIME_AFTER(current_tick
, wakeup_tick
))
378 wakeup_tick
= current_tick
+ (HZ
/4);
386 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
387 static long find_entry_ram(const char *filename
,
388 const struct dircache_entry
*dc
)
390 static long last_pos
= 0;
393 /* Check if we tagcache is loaded into ram. */
394 if (!tc_stat
.ramcache
)
398 dc
= dircache_get_entry_ptr(filename
);
402 logf("tagcache: file not found.");
413 for (; i
< hdr
->h
.tch
.entry_count
; i
++)
415 if (hdr
->indices
[i
].tag_seek
[tag_filename
] == (long)dc
)
417 last_pos
= MAX(0, i
- 3);
434 static long find_entry_disk(const char *filename
, bool localfd
)
436 struct tagcache_header tch
;
437 static long last_pos
= -1;
438 long pos_history
[POS_HISTORY_COUNT
];
439 long pos_history_idx
= 0;
441 struct tagfile_entry tfe
;
443 char buf
[TAG_MAXLEN
+32];
451 if (fd
< 0 || localfd
)
454 if ( (fd
= open_tag_fd(&tch
, tag_filename
, false)) < 0)
461 lseek(fd
, last_pos
, SEEK_SET
);
463 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
467 pos
= lseek(fd
, 0, SEEK_CUR
);
468 for (i
= pos_history_idx
-1; i
>= 0; i
--)
469 pos_history
[i
+1] = pos_history
[i
];
470 pos_history
[0] = pos
;
472 if (ecread_tagfile_entry(fd
, &tfe
)
473 != sizeof(struct tagfile_entry
))
478 if (tfe
.tag_length
>= (long)sizeof(buf
))
480 logf("too long tag #1");
488 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
490 logf("read error #2");
498 if (!strcasecmp(filename
, buf
))
500 last_pos
= pos_history
[pos_history_idx
];
505 if (pos_history_idx
< POS_HISTORY_COUNT
- 1)
519 if (fd
!= filenametag_fd
|| localfd
)
524 if (fd
!= filenametag_fd
|| localfd
)
530 static int find_index(const char *filename
)
534 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
535 if (tc_stat
.ramcache
&& is_dircache_intact())
536 idx_id
= find_entry_ram(filename
, NULL
);
540 idx_id
= find_entry_disk(filename
, true);
545 bool tagcache_find_index(struct tagcache_search
*tcs
, const char *filename
)
552 idx_id
= find_index(filename
);
556 if (!tagcache_search(tcs
, tag_filename
))
559 tcs
->entry_count
= 0;
560 tcs
->idx_id
= idx_id
;
565 static bool get_index(int masterfd
, int idxid
,
566 struct index_entry
*idx
, bool use_ram
)
568 bool localfd
= false;
572 logf("Incorrect idxid: %d", idxid
);
576 #ifdef HAVE_TC_RAMCACHE
577 if (tc_stat
.ramcache
&& use_ram
)
579 if (hdr
->indices
[idxid
].flag
& FLAG_DELETED
)
582 # ifdef HAVE_DIRCACHE
583 if (!(hdr
->indices
[idxid
].flag
& FLAG_DIRCACHE
)
584 || is_dircache_intact())
587 memcpy(idx
, &hdr
->indices
[idxid
], sizeof(struct index_entry
));
597 struct master_header tcmh
;
600 masterfd
= open_master_fd(&tcmh
, false);
605 lseek(masterfd
, idxid
* sizeof(struct index_entry
)
606 + sizeof(struct master_header
), SEEK_SET
);
607 if (ecread_index_entry(masterfd
, idx
)
608 != sizeof(struct index_entry
))
610 logf("read error #3");
620 if (idx
->flag
& FLAG_DELETED
)
628 static bool write_index(int masterfd
, int idxid
, struct index_entry
*idx
)
630 /* We need to exclude all memory only flags & tags when writing to disk. */
631 if (idx
->flag
& FLAG_DIRCACHE
)
633 logf("memory only flags!");
637 #ifdef HAVE_TC_RAMCACHE
638 /* Only update numeric data. Writing the whole index to RAM by memcpy
639 * destroys dircache pointers!
641 if (tc_stat
.ramcache
)
644 struct index_entry
*idx_ram
= &hdr
->indices
[idxid
];
646 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
648 if (TAGCACHE_IS_NUMERIC(tag
))
650 idx_ram
->tag_seek
[tag
] = idx
->tag_seek
[tag
];
654 /* Don't touch the dircache flag or attributes. */
655 idx_ram
->flag
= (idx
->flag
& 0x0000ffff)
656 | (idx_ram
->flag
& (0xffff0000 | FLAG_DIRCACHE
));
660 lseek(masterfd
, idxid
* sizeof(struct index_entry
)
661 + sizeof(struct master_header
), SEEK_SET
);
662 if (ecwrite_index_entry(masterfd
, idx
) != sizeof(struct index_entry
))
664 logf("write error #3");
665 logf("idxid: %d", idxid
);
672 #endif /* !__PCTOOL__ */
674 static bool open_files(struct tagcache_search
*tcs
, int tag
)
676 if (tcs
->idxfd
[tag
] < 0)
678 char fn
[MAX_PATH
], path
[MAX_PATH
];
681 snprintf(fn
, sizeof fn
, TAGCACHE_FILE_INDEX
, tag
);
682 file
= get_user_file_path(fn
, IS_FILE
| NEED_WRITE
, path
, sizeof(path
));
683 tcs
->idxfd
[tag
] = open(file
, O_RDONLY
);
686 if (tcs
->idxfd
[tag
] < 0)
688 logf("File not open!");
695 static bool retrieve(struct tagcache_search
*tcs
, struct index_entry
*idx
,
696 int tag
, char *buf
, long size
)
698 struct tagfile_entry tfe
;
703 if (TAGCACHE_IS_NUMERIC(tag
))
706 seek
= idx
->tag_seek
[tag
];
709 logf("Retrieve failed");
713 #ifdef HAVE_TC_RAMCACHE
716 struct tagfile_entry
*ep
;
718 # ifdef HAVE_DIRCACHE
719 if (tag
== tag_filename
&& (idx
->flag
& FLAG_DIRCACHE
)
720 && is_dircache_intact())
722 dircache_copy_path((struct dircache_entry
*)seek
,
728 if (tag
!= tag_filename
)
730 ep
= (struct tagfile_entry
*)&hdr
->tags
[tag
][seek
];
731 strlcpy(buf
, ep
->tag_data
, size
);
738 if (!open_files(tcs
, tag
))
741 lseek(tcs
->idxfd
[tag
], seek
, SEEK_SET
);
742 if (ecread_tagfile_entry(tcs
->idxfd
[tag
], &tfe
)
743 != sizeof(struct tagfile_entry
))
745 logf("read error #5");
749 if (tfe
.tag_length
>= size
)
751 logf("too small buffer");
755 if (read(tcs
->idxfd
[tag
], buf
, tfe
.tag_length
) !=
758 logf("read error #6");
762 buf
[tfe
.tag_length
] = '\0';
767 static long check_virtual_tags(int tag
, const struct index_entry
*idx
)
773 case tag_virt_length_sec
:
774 data
= (idx
->tag_seek
[tag_length
]/1000) % 60;
777 case tag_virt_length_min
:
778 data
= (idx
->tag_seek
[tag_length
]/1000) / 60;
781 case tag_virt_playtime_sec
:
782 data
= (idx
->tag_seek
[tag_playtime
]/1000) % 60;
785 case tag_virt_playtime_min
:
786 data
= (idx
->tag_seek
[tag_playtime
]/1000) / 60;
789 case tag_virt_autoscore
:
790 if (idx
->tag_seek
[tag_length
] == 0
791 || idx
->tag_seek
[tag_playcount
] == 0)
797 /* A straight calculus gives:
798 autoscore = 100 * playtime / length / playcout (1)
799 Now, consider the euclidian division of playtime by length:
800 playtime = alpha * length + beta
804 autoscore = 100 * (alpha / playcout + beta / length / playcount)
805 Both terms should be small enough to avoid any overflow
807 data
= 100 * (idx
->tag_seek
[tag_playtime
] / idx
->tag_seek
[tag_length
])
808 + (100 * (idx
->tag_seek
[tag_playtime
] % idx
->tag_seek
[tag_length
])) / idx
->tag_seek
[tag_length
];
809 data
/= idx
->tag_seek
[tag_playcount
];
813 /* How many commits before the file has been added to the DB. */
814 case tag_virt_entryage
:
815 data
= current_tcmh
.commitid
- idx
->tag_seek
[tag_commitid
] - 1;
819 data
= idx
->tag_seek
[tag
];
825 long tagcache_get_numeric(const struct tagcache_search
*tcs
, int tag
)
827 struct index_entry idx
;
832 if (!TAGCACHE_IS_NUMERIC(tag
))
835 if (!get_index(tcs
->masterfd
, tcs
->idx_id
, &idx
, true))
838 return check_virtual_tags(tag
, &idx
);
841 inline static bool str_ends_with(const char *str1
, const char *str2
)
843 int str_len
= strlen(str1
);
844 int clause_len
= strlen(str2
);
846 if (clause_len
> str_len
)
849 return !strcasecmp(&str1
[str_len
- clause_len
], str2
);
852 inline static bool str_oneof(const char *str
, const char *list
)
855 int l
, len
= strlen(str
);
859 sep
= strchr(list
, '|');
860 l
= sep
? (long)sep
- (long)list
: (int)strlen(list
);
861 if ((l
==len
) && !strncasecmp(str
, list
, len
))
863 list
+= sep
? l
+ 1 : l
;
869 static bool check_against_clause(long numeric
, const char *str
,
870 const struct tagcache_search_clause
*clause
)
874 switch (clause
->type
)
877 return numeric
== clause
->numeric_data
;
879 return numeric
!= clause
->numeric_data
;
881 return numeric
> clause
->numeric_data
;
883 return numeric
>= clause
->numeric_data
;
885 return numeric
< clause
->numeric_data
;
887 return numeric
<= clause
->numeric_data
;
889 logf("Incorrect numeric tag: %d", clause
->type
);
894 switch (clause
->type
)
897 return !strcasecmp(clause
->str
, str
);
899 return strcasecmp(clause
->str
, str
);
901 return 0>strcasecmp(clause
->str
, str
);
903 return 0>=strcasecmp(clause
->str
, str
);
905 return 0<strcasecmp(clause
->str
, str
);
907 return 0<=strcasecmp(clause
->str
, str
);
908 case clause_contains
:
909 return (strcasestr(str
, clause
->str
) != NULL
);
910 case clause_not_contains
:
911 return (strcasestr(str
, clause
->str
) == NULL
);
912 case clause_begins_with
:
913 return (strcasestr(str
, clause
->str
) == str
);
914 case clause_not_begins_with
:
915 return (strcasestr(str
, clause
->str
) != str
);
916 case clause_ends_with
:
917 return str_ends_with(str
, clause
->str
);
918 case clause_not_ends_with
:
919 return !str_ends_with(str
, clause
->str
);
921 return str_oneof(str
, clause
->str
);
924 logf("Incorrect tag: %d", clause
->type
);
931 static bool check_clauses(struct tagcache_search
*tcs
,
932 struct index_entry
*idx
,
933 struct tagcache_search_clause
**clause
, int count
)
937 #ifdef HAVE_TC_RAMCACHE
940 /* Go through all conditional clauses. */
941 for (i
= 0; i
< count
; i
++)
943 struct tagfile_entry
*tfe
;
948 seek
= check_virtual_tags(clause
[i
]->tag
, idx
);
950 if (!TAGCACHE_IS_NUMERIC(clause
[i
]->tag
))
952 if (clause
[i
]->tag
== tag_filename
)
954 retrieve(tcs
, idx
, tag_filename
, buf
, sizeof buf
);
959 tfe
= (struct tagfile_entry
*)&hdr
->tags
[clause
[i
]->tag
][seek
];
964 if (!check_against_clause(seek
, str
, clause
[i
]))
971 /* Check for conditions. */
972 for (i
= 0; i
< count
; i
++)
974 struct tagfile_entry tfe
;
978 seek
= check_virtual_tags(clause
[i
]->tag
, idx
);
980 memset(str
, 0, sizeof str
);
981 if (!TAGCACHE_IS_NUMERIC(clause
[i
]->tag
))
983 int fd
= tcs
->idxfd
[clause
[i
]->tag
];
984 lseek(fd
, seek
, SEEK_SET
);
985 ecread_tagfile_entry(fd
, &tfe
);
986 if (tfe
.tag_length
>= (int)sizeof(str
))
988 logf("Too long tag read!");
992 read(fd
, str
, tfe
.tag_length
);
994 /* Check if entry has been deleted. */
999 if (!check_against_clause(seek
, str
, clause
[i
]))
1007 bool tagcache_check_clauses(struct tagcache_search
*tcs
,
1008 struct tagcache_search_clause
**clause
, int count
)
1010 struct index_entry idx
;
1015 if (!get_index(tcs
->masterfd
, tcs
->idx_id
, &idx
, true))
1018 return check_clauses(tcs
, &idx
, clause
, count
);
1021 static bool add_uniqbuf(struct tagcache_search
*tcs
, unsigned long id
)
1025 /* If uniq buffer is not defined we must return true for search to work. */
1026 if (tcs
->unique_list
== NULL
|| (!TAGCACHE_IS_UNIQUE(tcs
->type
)
1027 && !TAGCACHE_IS_NUMERIC(tcs
->type
)))
1032 for (i
= 0; i
< tcs
->unique_list_count
; i
++)
1034 /* Return false if entry is found. */
1035 if (tcs
->unique_list
[i
] == id
)
1039 if (tcs
->unique_list_count
< tcs
->unique_list_capacity
)
1041 tcs
->unique_list
[i
] = id
;
1042 tcs
->unique_list_count
++;
1048 static bool build_lookup_list(struct tagcache_search
*tcs
)
1050 struct index_entry entry
;
1053 tcs
->seek_list_count
= 0;
1055 #ifdef HAVE_TC_RAMCACHE
1057 # ifdef HAVE_DIRCACHE
1058 && (tcs
->type
!= tag_filename
|| is_dircache_intact())
1062 for (i
= tcs
->seek_pos
; i
< hdr
->h
.tch
.entry_count
; i
++)
1064 struct tagcache_seeklist_entry
*seeklist
;
1065 struct index_entry
*idx
= &hdr
->indices
[i
];
1066 if (tcs
->seek_list_count
== SEEK_LIST_SIZE
)
1069 /* Skip deleted files. */
1070 if (idx
->flag
& FLAG_DELETED
)
1073 /* Go through all filters.. */
1074 for (j
= 0; j
< tcs
->filter_count
; j
++)
1076 if (idx
->tag_seek
[tcs
->filter_tag
[j
]] != tcs
->filter_seek
[j
])
1082 if (j
< tcs
->filter_count
)
1085 /* Check for conditions. */
1086 if (!check_clauses(tcs
, idx
, tcs
->clause
, tcs
->clause_count
))
1089 /* Add to the seek list if not already in uniq buffer. */
1090 if (!add_uniqbuf(tcs
, idx
->tag_seek
[tcs
->type
]))
1094 seeklist
= &tcs
->seeklist
[tcs
->seek_list_count
];
1095 seeklist
->seek
= idx
->tag_seek
[tcs
->type
];
1096 seeklist
->flag
= idx
->flag
;
1097 seeklist
->idx_id
= i
;
1098 tcs
->seek_list_count
++;
1103 return tcs
->seek_list_count
> 0;
1107 if (tcs
->masterfd
< 0)
1109 struct master_header tcmh
;
1110 tcs
->masterfd
= open_master_fd(&tcmh
, false);
1113 lseek(tcs
->masterfd
, tcs
->seek_pos
* sizeof(struct index_entry
) +
1114 sizeof(struct master_header
), SEEK_SET
);
1116 while (ecread_index_entry(tcs
->masterfd
, &entry
)
1117 == sizeof(struct index_entry
))
1119 struct tagcache_seeklist_entry
*seeklist
;
1121 if (tcs
->seek_list_count
== SEEK_LIST_SIZE
)
1127 /* Check if entry has been deleted. */
1128 if (entry
.flag
& FLAG_DELETED
)
1131 /* Go through all filters.. */
1132 for (j
= 0; j
< tcs
->filter_count
; j
++)
1134 if (entry
.tag_seek
[tcs
->filter_tag
[j
]] != tcs
->filter_seek
[j
])
1138 if (j
< tcs
->filter_count
)
1141 /* Check for conditions. */
1142 if (!check_clauses(tcs
, &entry
, tcs
->clause
, tcs
->clause_count
))
1145 /* Add to the seek list if not already in uniq buffer. */
1146 if (!add_uniqbuf(tcs
, entry
.tag_seek
[tcs
->type
]))
1150 seeklist
= &tcs
->seeklist
[tcs
->seek_list_count
];
1151 seeklist
->seek
= entry
.tag_seek
[tcs
->type
];
1152 seeklist
->flag
= entry
.flag
;
1153 seeklist
->idx_id
= i
;
1154 tcs
->seek_list_count
++;
1159 return tcs
->seek_list_count
> 0;
1163 static void remove_files(void)
1168 tc_stat
.ready
= false;
1169 tc_stat
.ramcache
= false;
1170 tc_stat
.econ
= false;
1171 remove(get_user_file_path(TAGCACHE_FILE_MASTER
, NEED_WRITE
|IS_FILE
,
1173 for (i
= 0; i
< TAG_COUNT
; i
++)
1175 char buf2
[MAX_PATH
];
1176 if (TAGCACHE_IS_NUMERIC(i
))
1179 /* database_%d.tcd -> database_0.tcd */
1180 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, i
);
1181 remove(get_user_file_path(buf
, NEED_WRITE
| IS_FILE
, buf2
, sizeof(buf2
)));
1186 static bool check_all_headers(void)
1188 struct master_header myhdr
;
1189 struct tagcache_header tch
;
1193 if ( (fd
= open_master_fd(&myhdr
, false)) < 0)
1199 logf("tagcache is dirty!");
1203 memcpy(¤t_tcmh
, &myhdr
, sizeof(struct master_header
));
1205 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
1207 if (TAGCACHE_IS_NUMERIC(tag
))
1210 if ( (fd
= open_tag_fd(&tch
, tag
, false)) < 0)
1219 bool tagcache_is_busy(void)
1221 return read_lock
|| write_lock
;
1224 bool tagcache_search(struct tagcache_search
*tcs
, int tag
)
1226 struct tagcache_header tag_hdr
;
1227 struct master_header master_hdr
;
1233 memset(tcs
, 0, sizeof(struct tagcache_search
));
1234 if (tc_stat
.commit_step
> 0 || !tc_stat
.ready
)
1237 tcs
->position
= sizeof(struct tagcache_header
);
1240 tcs
->list_position
= 0;
1241 tcs
->seek_list_count
= 0;
1242 tcs
->filter_count
= 0;
1245 for (i
= 0; i
< TAG_COUNT
; i
++)
1248 #ifndef HAVE_TC_RAMCACHE
1249 tcs
->ramsearch
= false;
1251 tcs
->ramsearch
= tc_stat
.ramcache
;
1254 tcs
->entry_count
= hdr
->entry_count
[tcs
->type
];
1259 /* Always open as R/W so we can pass tcs to functions that modify data also
1260 * without failing. */
1261 tcs
->masterfd
= open_master_fd(&master_hdr
, true);
1262 if (tcs
->masterfd
< 0)
1265 if (!TAGCACHE_IS_NUMERIC(tcs
->type
))
1267 tcs
->idxfd
[tcs
->type
] = open_tag_fd(&tag_hdr
, tcs
->type
, false);
1268 if (tcs
->idxfd
[tcs
->type
] < 0)
1271 tcs
->entry_count
= tag_hdr
.entry_count
;
1275 tcs
->entry_count
= master_hdr
.tch
.entry_count
;
1280 tcs
->initialized
= true;
1286 void tagcache_search_set_uniqbuf(struct tagcache_search
*tcs
,
1287 void *buffer
, long length
)
1289 tcs
->unique_list
= (unsigned long *)buffer
;
1290 tcs
->unique_list_capacity
= length
/ sizeof(*tcs
->unique_list
);
1291 tcs
->unique_list_count
= 0;
1294 bool tagcache_search_add_filter(struct tagcache_search
*tcs
,
1297 if (tcs
->filter_count
== TAGCACHE_MAX_FILTERS
)
1300 if (TAGCACHE_IS_NUMERIC_OR_NONUNIQUE(tag
))
1303 tcs
->filter_tag
[tcs
->filter_count
] = tag
;
1304 tcs
->filter_seek
[tcs
->filter_count
] = seek
;
1305 tcs
->filter_count
++;
1310 bool tagcache_search_add_clause(struct tagcache_search
*tcs
,
1311 struct tagcache_search_clause
*clause
)
1315 if (tcs
->clause_count
>= TAGCACHE_MAX_CLAUSES
)
1317 logf("Too many clauses");
1321 /* Check if there is already a similar filter in present (filters are
1322 * much faster than clauses).
1324 for (i
= 0; i
< tcs
->filter_count
; i
++)
1326 if (tcs
->filter_tag
[i
] == clause
->tag
)
1330 if (!TAGCACHE_IS_NUMERIC(clause
->tag
) && tcs
->idxfd
[clause
->tag
] < 0)
1332 char buf
[MAX_PATH
], path
[MAX_PATH
];
1334 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, clause
->tag
);
1335 file
= get_user_file_path(buf
, IS_FILE
| NEED_WRITE
, path
, sizeof(path
));
1336 tcs
->idxfd
[clause
->tag
] = open(file
, O_RDONLY
);
1339 tcs
->clause
[tcs
->clause_count
] = clause
;
1340 tcs
->clause_count
++;
1345 static bool get_next(struct tagcache_search
*tcs
)
1347 static char buf
[TAG_MAXLEN
+32];
1348 struct tagfile_entry entry
;
1351 if (!tcs
->valid
|| !tc_stat
.ready
)
1354 if (tcs
->idxfd
[tcs
->type
] < 0 && !TAGCACHE_IS_NUMERIC(tcs
->type
)
1355 #ifdef HAVE_TC_RAMCACHE
1361 /* Relative fetch. */
1362 if (tcs
->filter_count
> 0 || tcs
->clause_count
> 0
1363 || TAGCACHE_IS_NUMERIC(tcs
->type
)
1364 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1365 /* We need to retrieve flag status for dircache. */
1366 || (tcs
->ramsearch
&& tcs
->type
== tag_filename
)
1370 struct tagcache_seeklist_entry
*seeklist
;
1372 /* Check for end of list. */
1373 if (tcs
->list_position
== tcs
->seek_list_count
)
1375 tcs
->list_position
= 0;
1377 /* Try to fetch more. */
1378 if (!build_lookup_list(tcs
))
1385 seeklist
= &tcs
->seeklist
[tcs
->list_position
];
1386 flag
= seeklist
->flag
;
1387 tcs
->position
= seeklist
->seek
;
1388 tcs
->idx_id
= seeklist
->idx_id
;
1389 tcs
->list_position
++;
1393 if (tcs
->entry_count
== 0)
1402 tcs
->result_seek
= tcs
->position
;
1404 if (TAGCACHE_IS_NUMERIC(tcs
->type
))
1406 snprintf(buf
, sizeof(buf
), "%ld", tcs
->position
);
1408 tcs
->result_len
= strlen(buf
) + 1;
1413 #ifdef HAVE_TC_RAMCACHE
1417 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1418 if (tcs
->type
== tag_filename
&& (flag
& FLAG_DIRCACHE
)
1419 && is_dircache_intact())
1421 dircache_copy_path((struct dircache_entry
*)tcs
->position
,
1424 tcs
->result_len
= strlen(buf
) + 1;
1425 tcs
->ramresult
= false;
1431 if (tcs
->type
!= tag_filename
)
1433 struct tagfile_entry
*ep
;
1435 ep
= (struct tagfile_entry
*)&hdr
->tags
[tcs
->type
][tcs
->position
];
1436 tcs
->result
= ep
->tag_data
;
1437 tcs
->result_len
= strlen(tcs
->result
) + 1;
1438 tcs
->idx_id
= ep
->idx_id
;
1439 tcs
->ramresult
= true;
1441 /* Increase position for the next run. This may get overwritten. */
1442 tcs
->position
+= sizeof(struct tagfile_entry
) + ep
->tag_length
;
1449 if (!open_files(tcs
, tcs
->type
))
1455 /* Seek stream to the correct position and continue to direct fetch. */
1456 lseek(tcs
->idxfd
[tcs
->type
], tcs
->position
, SEEK_SET
);
1458 if (ecread_tagfile_entry(tcs
->idxfd
[tcs
->type
], &entry
) != sizeof(struct tagfile_entry
))
1460 logf("read error #5");
1465 if (entry
.tag_length
> (long)sizeof(buf
))
1468 logf("too long tag #2");
1469 logf("P:%lX/%lX", tcs
->position
, entry
.tag_length
);
1473 if (read(tcs
->idxfd
[tcs
->type
], buf
, entry
.tag_length
) != entry
.tag_length
)
1476 logf("read error #4");
1481 Update the position for the next read (this may be overridden
1482 if filters or clauses are being used).
1484 tcs
->position
+= sizeof(struct tagfile_entry
) + entry
.tag_length
;
1486 tcs
->result_len
= strlen(tcs
->result
) + 1;
1487 tcs
->idx_id
= entry
.idx_id
;
1488 tcs
->ramresult
= false;
1493 bool tagcache_get_next(struct tagcache_search
*tcs
)
1495 while (get_next(tcs
))
1497 if (tcs
->result_len
> 1)
1504 bool tagcache_retrieve(struct tagcache_search
*tcs
, int idxid
,
1505 int tag
, char *buf
, long size
)
1507 struct index_entry idx
;
1510 if (!get_index(tcs
->masterfd
, idxid
, &idx
, true))
1513 return retrieve(tcs
, &idx
, tag
, buf
, size
);
1516 static bool update_master_header(void)
1518 struct master_header myhdr
;
1524 if ( (fd
= open_master_fd(&myhdr
, true)) < 0)
1527 myhdr
.serial
= current_tcmh
.serial
;
1528 myhdr
.commitid
= current_tcmh
.commitid
;
1529 myhdr
.dirty
= current_tcmh
.dirty
;
1532 lseek(fd
, 0, SEEK_SET
);
1533 ecwrite(fd
, &myhdr
, 1, master_header_ec
, tc_stat
.econ
);
1536 #ifdef HAVE_TC_RAMCACHE
1539 hdr
->h
.serial
= current_tcmh
.serial
;
1540 hdr
->h
.commitid
= current_tcmh
.commitid
;
1541 hdr
->h
.dirty
= current_tcmh
.dirty
;
1550 void tagcache_modify(struct tagcache_search
*tcs
, int type
, const char *text
)
1552 struct tagentry
*entry
;
1554 if (tcs
->type
!= tag_title
)
1557 /* We will need reserve buffer for this. */
1560 struct tagfile_entry
*ep
;
1562 ep
= (struct tagfile_entry
*)&hdr
->tags
[tcs
->type
][tcs
->result_seek
];
1563 tcs
->seek_list
[tcs
->seek_list_count
];
1566 entry
= find_entry_ram();
1571 void tagcache_search_finish(struct tagcache_search
*tcs
)
1575 if (!tcs
->initialized
)
1578 if (tcs
->masterfd
>= 0)
1580 close(tcs
->masterfd
);
1584 for (i
= 0; i
< TAG_COUNT
; i
++)
1586 if (tcs
->idxfd
[i
] >= 0)
1588 close(tcs
->idxfd
[i
]);
1593 tcs
->ramsearch
= false;
1595 tcs
->initialized
= 0;
1600 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1601 static struct tagfile_entry
*get_tag(const struct index_entry
*entry
, int tag
)
1603 return (struct tagfile_entry
*)&hdr
->tags
[tag
][entry
->tag_seek
[tag
]];
1606 static long get_tag_numeric(const struct index_entry
*entry
, int tag
)
1608 return check_virtual_tags(tag
, entry
);
1611 static char* get_tag_string(const struct index_entry
*entry
, int tag
)
1613 char* s
= get_tag(entry
, tag
)->tag_data
;
1614 return strcmp(s
, UNTAGGED
) ? s
: NULL
;
1617 bool tagcache_fill_tags(struct mp3entry
*id3
, const char *filename
)
1619 struct index_entry
*entry
;
1622 if (!tc_stat
.ready
|| !tc_stat
.ramcache
)
1625 /* Find the corresponding entry in tagcache. */
1626 idx_id
= find_entry_ram(filename
, NULL
);
1630 entry
= &hdr
->indices
[idx_id
];
1632 memset(id3
, 0, sizeof(struct mp3entry
));
1634 id3
->title
= get_tag_string(entry
, tag_title
);
1635 id3
->artist
= get_tag_string(entry
, tag_artist
);
1636 id3
->album
= get_tag_string(entry
, tag_album
);
1637 id3
->genre_string
= get_tag_string(entry
, tag_genre
);
1638 id3
->composer
= get_tag_string(entry
, tag_composer
);
1639 id3
->comment
= get_tag_string(entry
, tag_comment
);
1640 id3
->albumartist
= get_tag_string(entry
, tag_albumartist
);
1641 id3
->grouping
= get_tag_string(entry
, tag_grouping
);
1643 id3
->length
= get_tag_numeric(entry
, tag_length
);
1644 id3
->playcount
= get_tag_numeric(entry
, tag_playcount
);
1645 id3
->rating
= get_tag_numeric(entry
, tag_rating
);
1646 id3
->lastplayed
= get_tag_numeric(entry
, tag_lastplayed
);
1647 id3
->score
= get_tag_numeric(entry
, tag_virt_autoscore
) / 10;
1648 id3
->year
= get_tag_numeric(entry
, tag_year
);
1650 id3
->discnum
= get_tag_numeric(entry
, tag_discnumber
);
1651 id3
->tracknum
= get_tag_numeric(entry
, tag_tracknumber
);
1652 id3
->bitrate
= get_tag_numeric(entry
, tag_bitrate
);
1653 if (id3
->bitrate
== 0)
1660 static inline void write_item(const char *item
)
1662 int len
= strlen(item
) + 1;
1665 write(cachefd
, item
, len
);
1668 static int check_if_empty(char **tag
)
1672 if (*tag
== NULL
|| **tag
== '\0')
1675 return sizeof(UNTAGGED
); /* Tag length */
1678 length
= strlen(*tag
);
1679 if (length
> TAG_MAXLEN
)
1681 logf("over length tag: %s", *tag
);
1682 length
= TAG_MAXLEN
;
1683 (*tag
)[length
] = '\0';
1689 #define ADD_TAG(entry,tag,data) \
1691 entry.tag_offset[tag] = offset; \
1692 entry.tag_length[tag] = check_if_empty(data); \
1693 offset += entry.tag_length[tag]
1694 /* GCC 3.4.6 for Coldfire can choose to inline this function. Not a good
1695 * idea, as it uses lots of stack and is called from a recursive function
1698 static void __attribute__ ((noinline
)) add_tagcache(char *path
,
1700 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1701 ,const struct dircache_entry
*dc
1705 struct mp3entry id3
;
1706 struct temp_file_entry entry
;
1710 char tracknumfix
[3];
1712 int path_length
= strlen(path
);
1713 bool has_albumartist
;
1717 /* Crude logging for the sim - to aid in debugging */
1718 int logfd
= open(ROCKBOX_DIR
"/database.log",
1719 O_WRONLY
| O_APPEND
| O_CREAT
, 0666);
1721 write(logfd
, path
, strlen(path
));
1722 write(logfd
, "\n", 1);
1730 /* Check for overlength file path. */
1731 if (path_length
> TAG_MAXLEN
)
1733 /* Path can't be shortened. */
1734 logf("Too long path: %s", path
);
1738 /* Check if the file is supported. */
1739 if (probe_file_format(path
) == AFMT_UNKNOWN
)
1742 /* Check if the file is already cached. */
1743 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1744 if (tc_stat
.ramcache
&& is_dircache_intact())
1746 idx_id
= find_entry_ram(path
, dc
);
1750 /* Be sure the entry doesn't exist. */
1751 if (filenametag_fd
>= 0 && idx_id
< 0)
1752 idx_id
= find_entry_disk(path
, false);
1754 /* Check if file has been modified. */
1757 struct index_entry idx
;
1759 /* TODO: Mark that the index exists (for fast reverse scan) */
1760 //found_idx[idx_id/8] |= idx_id%8;
1762 if (!get_index(-1, idx_id
, &idx
, true))
1764 logf("failed to retrieve index entry");
1768 if ((unsigned long)idx
.tag_seek
[tag_mtime
] == mtime
)
1770 /* No changes to file. */
1774 /* Metadata might have been changed. Delete the entry. */
1775 logf("Re-adding: %s", path
);
1776 if (!delete_entry(idx_id
))
1778 logf("delete_entry failed: %d", idx_id
);
1783 fd
= open(path
, O_RDONLY
);
1786 logf("open fail: %s", path
);
1790 memset(&id3
, 0, sizeof(struct mp3entry
));
1791 memset(&entry
, 0, sizeof(struct temp_file_entry
));
1792 memset(&tracknumfix
, 0, sizeof(tracknumfix
));
1793 ret
= get_metadata(&id3
, fd
, path
);
1799 logf("-> %s", path
);
1801 /* Generate track number if missing. */
1802 if (id3
.tracknum
<= 0)
1804 const char *p
= strrchr(path
, '.');
1807 p
= &path
[strlen(path
)-1];
1811 if (isdigit(*p
) && isdigit(*(p
-1)))
1813 tracknumfix
[1] = *p
--;
1814 tracknumfix
[0] = *p
;
1820 if (tracknumfix
[0] != '\0')
1822 id3
.tracknum
= atoi(tracknumfix
);
1823 /* Set a flag to indicate track number has been generated. */
1824 entry
.flag
|= FLAG_TRKNUMGEN
;
1828 /* Unable to generate track number. */
1834 entry
.tag_offset
[tag_year
] = id3
.year
;
1835 entry
.tag_offset
[tag_discnumber
] = id3
.discnum
;
1836 entry
.tag_offset
[tag_tracknumber
] = id3
.tracknum
;
1837 entry
.tag_offset
[tag_length
] = id3
.length
;
1838 entry
.tag_offset
[tag_bitrate
] = id3
.bitrate
;
1839 entry
.tag_offset
[tag_mtime
] = mtime
;
1842 has_albumartist
= id3
.albumartist
!= NULL
1843 && strlen(id3
.albumartist
) > 0;
1844 has_grouping
= id3
.grouping
!= NULL
1845 && strlen(id3
.grouping
) > 0;
1847 ADD_TAG(entry
, tag_filename
, &path
);
1848 ADD_TAG(entry
, tag_title
, &id3
.title
);
1849 ADD_TAG(entry
, tag_artist
, &id3
.artist
);
1850 ADD_TAG(entry
, tag_album
, &id3
.album
);
1851 ADD_TAG(entry
, tag_genre
, &id3
.genre_string
);
1852 ADD_TAG(entry
, tag_composer
, &id3
.composer
);
1853 ADD_TAG(entry
, tag_comment
, &id3
.comment
);
1854 if (has_albumartist
)
1856 ADD_TAG(entry
, tag_albumartist
, &id3
.albumartist
);
1860 ADD_TAG(entry
, tag_albumartist
, &id3
.artist
);
1864 ADD_TAG(entry
, tag_grouping
, &id3
.grouping
);
1868 ADD_TAG(entry
, tag_grouping
, &id3
.title
);
1870 entry
.data_length
= offset
;
1872 /* Write the header */
1873 write(cachefd
, &entry
, sizeof(struct temp_file_entry
));
1875 /* And tags also... Correct order is critical */
1877 write_item(id3
.title
);
1878 write_item(id3
.artist
);
1879 write_item(id3
.album
);
1880 write_item(id3
.genre_string
);
1881 write_item(id3
.composer
);
1882 write_item(id3
.comment
);
1883 if (has_albumartist
)
1885 write_item(id3
.albumartist
);
1889 write_item(id3
.artist
);
1893 write_item(id3
.grouping
);
1897 write_item(id3
.title
);
1899 total_entry_count
++;
1902 static bool tempbuf_insert(char *str
, int id
, int idx_id
, bool unique
)
1904 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1905 int len
= strlen(str
)+1;
1908 unsigned *crcbuf
= (unsigned *)&tempbuf
[tempbuf_size
-4];
1909 char buf
[TAG_MAXLEN
+32];
1911 for (i
= 0; str
[i
] != '\0' && i
< (int)sizeof(buf
)-1; i
++)
1912 buf
[i
] = tolower(str
[i
]);
1915 crc32
= crc_32(buf
, i
, 0xffffffff);
1919 /* Check if the crc does not exist -> entry does not exist for sure. */
1920 for (i
= 0; i
< tempbufidx
; i
++)
1922 if (crcbuf
[-i
] != crc32
)
1925 if (!strcasecmp(str
, index
[i
].str
))
1927 if (id
< 0 || id
>= lookup_buffer_depth
)
1929 logf("lookup buf overf.: %d", id
);
1933 lookup
[id
] = &index
[i
];
1939 /* Insert to CRC buffer. */
1940 crcbuf
[-tempbufidx
] = crc32
;
1943 /* Insert it to the buffer. */
1944 tempbuf_left
-= len
;
1945 if (tempbuf_left
- 4 < 0 || tempbufidx
>= commit_entry_count
-1)
1948 if (id
>= lookup_buffer_depth
)
1950 logf("lookup buf overf. #2: %d", id
);
1956 lookup
[id
] = &index
[tempbufidx
];
1957 index
[tempbufidx
].idlist
.id
= id
;
1960 index
[tempbufidx
].idlist
.id
= -1;
1962 index
[tempbufidx
].idlist
.next
= NULL
;
1963 index
[tempbufidx
].idx_id
= idx_id
;
1964 index
[tempbufidx
].seek
= -1;
1965 index
[tempbufidx
].str
= &tempbuf
[tempbuf_pos
];
1966 memcpy(index
[tempbufidx
].str
, str
, len
);
1973 static int compare(const void *p1
, const void *p2
)
1977 struct tempbuf_searchidx
*e1
= (struct tempbuf_searchidx
*)p1
;
1978 struct tempbuf_searchidx
*e2
= (struct tempbuf_searchidx
*)p2
;
1980 if (strcmp(e1
->str
, UNTAGGED
) == 0)
1982 if (strcmp(e2
->str
, UNTAGGED
) == 0)
1986 else if (strcmp(e2
->str
, UNTAGGED
) == 0)
1989 return strncasecmp(e1
->str
, e2
->str
, TAG_MAXLEN
);
1992 static int tempbuf_sort(int fd
)
1994 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1995 struct tagfile_entry fe
;
1999 /* Generate reverse lookup entries. */
2000 for (i
= 0; i
< lookup_buffer_depth
; i
++)
2002 struct tempbuf_id_list
*idlist
;
2007 if (lookup
[i
]->idlist
.id
== i
)
2010 idlist
= &lookup
[i
]->idlist
;
2011 while (idlist
->next
!= NULL
)
2012 idlist
= idlist
->next
;
2014 tempbuf_left
-= sizeof(struct tempbuf_id_list
);
2015 if (tempbuf_left
- 4 < 0)
2018 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
2019 if (tempbuf_pos
& 0x03)
2021 tempbuf_pos
= (tempbuf_pos
& ~0x03) + 0x04;
2023 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
2025 tempbuf_pos
+= sizeof(struct tempbuf_id_list
);
2027 idlist
= idlist
->next
;
2029 idlist
->next
= NULL
;
2034 qsort(index
, tempbufidx
, sizeof(struct tempbuf_searchidx
), compare
);
2035 memset(lookup
, 0, lookup_buffer_depth
* sizeof(struct tempbuf_searchidx
**));
2037 for (i
= 0; i
< tempbufidx
; i
++)
2039 struct tempbuf_id_list
*idlist
= &index
[i
].idlist
;
2041 /* Fix the lookup list. */
2042 while (idlist
!= NULL
)
2044 if (idlist
->id
>= 0)
2045 lookup
[idlist
->id
] = &index
[i
];
2046 idlist
= idlist
->next
;
2049 index
[i
].seek
= lseek(fd
, 0, SEEK_CUR
);
2050 length
= strlen(index
[i
].str
) + 1;
2051 fe
.tag_length
= length
;
2052 fe
.idx_id
= index
[i
].idx_id
;
2054 /* Check the chunk alignment. */
2055 if ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
2056 % TAGFILE_ENTRY_CHUNK_LENGTH
)
2058 fe
.tag_length
+= TAGFILE_ENTRY_CHUNK_LENGTH
-
2059 ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
2060 % TAGFILE_ENTRY_CHUNK_LENGTH
);
2063 #ifdef TAGCACHE_STRICT_ALIGN
2064 /* Make sure the entry is long aligned. */
2065 if (index
[i
].seek
& 0x03)
2067 logf("tempbuf_sort: alignment error!");
2072 if (ecwrite(fd
, &fe
, 1, tagfile_entry_ec
, tc_stat
.econ
) !=
2073 sizeof(struct tagfile_entry
))
2075 logf("tempbuf_sort: write error #1");
2079 if (write(fd
, index
[i
].str
, length
) != length
)
2081 logf("tempbuf_sort: write error #2");
2085 /* Write some padding. */
2086 if (fe
.tag_length
- length
> 0)
2087 write(fd
, "XXXXXXXX", fe
.tag_length
- length
);
2093 inline static struct tempbuf_searchidx
* tempbuf_locate(int id
)
2095 if (id
< 0 || id
>= lookup_buffer_depth
)
2102 inline static int tempbuf_find_location(int id
)
2104 struct tempbuf_searchidx
*entry
;
2106 entry
= tempbuf_locate(id
);
2113 static bool build_numeric_indices(struct tagcache_header
*h
, int tmpfd
)
2115 struct master_header tcmh
;
2116 struct index_entry idx
;
2119 struct temp_file_entry
*entrybuf
= (struct temp_file_entry
*)tempbuf
;
2121 int entries_processed
= 0;
2123 char buf
[TAG_MAXLEN
];
2125 max_entries
= tempbuf_size
/ sizeof(struct temp_file_entry
) - 1;
2127 logf("Building numeric indices...");
2128 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2130 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
2133 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
2135 if (masterfd_pos
== filesize(masterfd
))
2137 logf("we can't append!");
2142 while (entries_processed
< h
->entry_count
)
2144 int count
= MIN(h
->entry_count
- entries_processed
, max_entries
);
2146 /* Read in as many entries as possible. */
2147 for (i
= 0; i
< count
; i
++)
2149 struct temp_file_entry
*tfe
= &entrybuf
[i
];
2152 /* Read in numeric data. */
2153 if (read(tmpfd
, tfe
, sizeof(struct temp_file_entry
)) !=
2154 sizeof(struct temp_file_entry
))
2156 logf("read fail #1");
2161 datastart
= lseek(tmpfd
, 0, SEEK_CUR
);
2164 * Read string data from the following tags:
2170 * A crc32 hash is calculated from the read data
2171 * and stored back to the data offset field kept in memory.
2173 #define tmpdb_read_string_tag(tag) \
2174 lseek(tmpfd, tfe->tag_offset[tag], SEEK_CUR); \
2175 if ((unsigned long)tfe->tag_length[tag] > sizeof buf) \
2177 logf("read fail: buffer overflow"); \
2182 if (read(tmpfd, buf, tfe->tag_length[tag]) != \
2183 tfe->tag_length[tag]) \
2185 logf("read fail #2"); \
2190 tfe->tag_offset[tag] = crc_32(buf, strlen(buf), 0xffffffff); \
2191 lseek(tmpfd, datastart, SEEK_SET)
2193 tmpdb_read_string_tag(tag_filename
);
2194 tmpdb_read_string_tag(tag_artist
);
2195 tmpdb_read_string_tag(tag_album
);
2196 tmpdb_read_string_tag(tag_title
);
2198 /* Seek to the end of the string data. */
2199 lseek(tmpfd
, tfe
->data_length
, SEEK_CUR
);
2202 /* Backup the master index position. */
2203 masterfd_pos
= lseek(masterfd
, 0, SEEK_CUR
);
2204 lseek(masterfd
, sizeof(struct master_header
), SEEK_SET
);
2206 /* Check if we can resurrect some deleted runtime statistics data. */
2207 for (i
= 0; i
< tcmh
.tch
.entry_count
; i
++)
2209 /* Read the index entry. */
2210 if (ecread_index_entry(masterfd
, &idx
)
2211 != sizeof(struct index_entry
))
2213 logf("read fail #3");
2219 * Skip unless the entry is marked as being deleted
2220 * or the data has already been resurrected.
2222 if (!(idx
.flag
& FLAG_DELETED
) || idx
.flag
& FLAG_RESURRECTED
)
2225 /* Now try to match the entry. */
2227 * To succesfully match a song, the following conditions
2230 * For numeric fields: tag_length
2231 * - Full identical match is required
2233 * If tag_filename matches, no further checking necessary.
2235 * For string hashes: tag_artist, tag_album, tag_title
2236 * - Two of these must match
2238 for (j
= 0; j
< count
; j
++)
2240 struct temp_file_entry
*tfe
= &entrybuf
[j
];
2242 /* Try to match numeric fields first. */
2243 if (tfe
->tag_offset
[tag_length
] != idx
.tag_seek
[tag_length
])
2246 /* Now it's time to do the hash matching. */
2247 if (tfe
->tag_offset
[tag_filename
] != idx
.tag_seek
[tag_filename
])
2249 int match_count
= 0;
2251 /* No filename match, check if we can match two other tags. */
2252 #define tmpdb_match(tag) \
2253 if (tfe->tag_offset[tag] == idx.tag_seek[tag]) \
2256 tmpdb_match(tag_artist
);
2257 tmpdb_match(tag_album
);
2258 tmpdb_match(tag_title
);
2260 if (match_count
< 2)
2262 /* Still no match found, give up. */
2267 /* A match found, now copy & resurrect the statistical data. */
2268 #define tmpdb_copy_tag(tag) \
2269 tfe->tag_offset[tag] = idx.tag_seek[tag]
2271 tmpdb_copy_tag(tag_playcount
);
2272 tmpdb_copy_tag(tag_rating
);
2273 tmpdb_copy_tag(tag_playtime
);
2274 tmpdb_copy_tag(tag_lastplayed
);
2275 tmpdb_copy_tag(tag_commitid
);
2277 /* Avoid processing this entry again. */
2278 idx
.flag
|= FLAG_RESURRECTED
;
2280 lseek(masterfd
, -sizeof(struct index_entry
), SEEK_CUR
);
2281 if (ecwrite_index_entry(masterfd
, &idx
) != sizeof(struct index_entry
))
2283 logf("masterfd writeback fail #1");
2288 logf("Entry resurrected");
2293 /* Restore the master index position. */
2294 lseek(masterfd
, masterfd_pos
, SEEK_SET
);
2296 /* Commit the data to the index. */
2297 for (i
= 0; i
< count
; i
++)
2299 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2301 if (ecread_index_entry(masterfd
, &idx
) != sizeof(struct index_entry
))
2303 logf("read fail #3");
2308 for (j
= 0; j
< TAG_COUNT
; j
++)
2310 if (!TAGCACHE_IS_NUMERIC(j
))
2313 idx
.tag_seek
[j
] = entrybuf
[i
].tag_offset
[j
];
2315 idx
.flag
= entrybuf
[i
].flag
;
2317 if (idx
.tag_seek
[tag_commitid
])
2319 /* Data has been resurrected. */
2320 idx
.flag
|= FLAG_DIRTYNUM
;
2322 else if (tc_stat
.ready
&& current_tcmh
.commitid
> 0)
2324 idx
.tag_seek
[tag_commitid
] = current_tcmh
.commitid
;
2325 idx
.flag
|= FLAG_DIRTYNUM
;
2328 /* Write back the updated index. */
2329 lseek(masterfd
, loc
, SEEK_SET
);
2330 if (ecwrite_index_entry(masterfd
, &idx
) != sizeof(struct index_entry
))
2338 entries_processed
+= count
;
2339 logf("%d/%ld entries processed", entries_processed
, h
->entry_count
);
2350 * == 0 temporary failure
2353 static int build_index(int index_type
, struct tagcache_header
*h
, int tmpfd
)
2356 struct tagcache_header tch
;
2357 struct master_header tcmh
;
2358 struct index_entry idxbuf
[IDX_BUF_DEPTH
];
2360 char buf
[TAG_MAXLEN
+32], path
[MAX_PATH
];
2362 int fd
= -1, masterfd
;
2367 logf("Building index: %d", index_type
);
2369 /* Check the number of entries we need to allocate ram for. */
2370 commit_entry_count
= h
->entry_count
+ 1;
2372 masterfd
= open_master_fd(&tcmh
, false);
2375 commit_entry_count
+= tcmh
.tch
.entry_count
;
2379 remove_files(); /* Just to be sure we are clean. */
2381 /* Open the index file, which contains the tag names. */
2382 fd
= open_tag_fd(&tch
, index_type
, true);
2385 logf("tch.datasize=%ld", tch
.datasize
);
2386 lookup_buffer_depth
= 1 +
2387 /* First part */ commit_entry_count
+
2388 /* Second part */ (tch
.datasize
/ TAGFILE_ENTRY_CHUNK_LENGTH
);
2392 lookup_buffer_depth
= 1 +
2393 /* First part */ commit_entry_count
+
2394 /* Second part */ 0;
2397 logf("lookup_buffer_depth=%ld", lookup_buffer_depth
);
2398 logf("commit_entry_count=%ld", commit_entry_count
);
2400 /* Allocate buffer for all index entries from both old and new
2403 tempbuf_pos
= commit_entry_count
* sizeof(struct tempbuf_searchidx
);
2405 /* Allocate lookup buffer. The first portion of commit_entry_count
2406 * contains the new tags in the temporary file and the second
2407 * part for locating entries already in the db.
2410 * +---------+---------------------------+
2411 * | index | position/ENTRY_CHUNK_SIZE | lookup buffer
2412 * +---------+---------------------------+
2414 * Old tags are inserted to a temporary buffer with position:
2415 * tempbuf_insert(position/ENTRY_CHUNK_SIZE, ...);
2416 * And new tags with index:
2417 * tempbuf_insert(idx, ...);
2419 * The buffer is sorted and written into tag file:
2420 * tempbuf_sort(...);
2421 * leaving master index locations messed up.
2423 * That is fixed using the lookup buffer for old tags:
2424 * new_seek = tempbuf_find_location(old_seek, ...);
2426 * new_seek = tempbuf_find_location(idx);
2428 lookup
= (struct tempbuf_searchidx
**)&tempbuf
[tempbuf_pos
];
2429 tempbuf_pos
+= lookup_buffer_depth
* sizeof(void **);
2430 memset(lookup
, 0, lookup_buffer_depth
* sizeof(void **));
2432 /* And calculate the remaining data space used mainly for storing
2433 * tag data (strings). */
2434 tempbuf_left
= tempbuf_size
- tempbuf_pos
- 8;
2435 if (tempbuf_left
- TAGFILE_ENTRY_AVG_LENGTH
* commit_entry_count
< 0)
2437 logf("Buffer way too small!");
2444 * If tag file contains unique tags (sorted index), we will load
2445 * it entirely into memory so we can resort it later for use with
2448 if (TAGCACHE_IS_SORTED(index_type
))
2450 logf("loading tags...");
2451 for (i
= 0; i
< tch
.entry_count
; i
++)
2453 struct tagfile_entry entry
;
2454 int loc
= lseek(fd
, 0, SEEK_CUR
);
2457 if (ecread_tagfile_entry(fd
, &entry
) != sizeof(struct tagfile_entry
))
2459 logf("read error #7");
2464 if (entry
.tag_length
>= (int)sizeof(buf
))
2466 logf("too long tag #3");
2471 if (read(fd
, buf
, entry
.tag_length
) != entry
.tag_length
)
2473 logf("read error #8");
2478 /* Skip deleted entries. */
2483 * Save the tag and tag id in the memory buffer. Tag id
2484 * is saved so we can later reindex the master lookup
2485 * table when the index gets resorted.
2487 ret
= tempbuf_insert(buf
, loc
/TAGFILE_ENTRY_CHUNK_LENGTH
2488 + commit_entry_count
, entry
.idx_id
,
2489 TAGCACHE_IS_UNIQUE(index_type
));
2500 tempbufidx
= tch
.entry_count
;
2505 * Creating new index file to store the tags. No need to preload
2506 * anything whether the index type is sorted or not.
2508 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, index_type
);
2509 file
= get_user_file_path(buf
, IS_FILE
| NEED_WRITE
, path
, sizeof(path
));
2510 fd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
2513 logf("%s open fail", buf
);
2517 tch
.magic
= TAGCACHE_MAGIC
;
2518 tch
.entry_count
= 0;
2521 if (ecwrite(fd
, &tch
, 1, tagcache_header_ec
, tc_stat
.econ
)
2522 != sizeof(struct tagcache_header
))
2524 logf("header write failed");
2530 file
= get_user_file_path(TAGCACHE_FILE_MASTER
,
2533 /* Loading the tag lookup file as "master file". */
2534 logf("Loading index file");
2535 masterfd
= open(file
, O_RDWR
);
2539 logf("Creating new DB");
2540 masterfd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
2544 logf("Failure to create index file (%s)", file
);
2549 /* Write the header (write real values later). */
2550 memset(&tcmh
, 0, sizeof(struct master_header
));
2552 tcmh
.tch
.entry_count
= 0;
2553 tcmh
.tch
.datasize
= 0;
2555 ecwrite(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
);
2557 masterfd_pos
= lseek(masterfd
, 0, SEEK_CUR
);
2562 * Master file already exists so we need to process the current
2567 if (ecread(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
) !=
2568 sizeof(struct master_header
) || tcmh
.tch
.magic
!= TAGCACHE_MAGIC
)
2570 logf("header error");
2577 * If we reach end of the master file, we need to expand it to
2578 * hold new tags. If the current index is not sorted, we can
2579 * simply append new data to end of the file.
2580 * However, if the index is sorted, we need to update all tag
2581 * pointers in the master file for the current index.
2583 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
2585 if (masterfd_pos
== filesize(masterfd
))
2587 logf("appending...");
2593 * Load new unique tags in memory to be sorted later and added
2594 * to the master lookup file.
2596 if (TAGCACHE_IS_SORTED(index_type
))
2598 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2599 /* h is the header of the temporary file containing new tags. */
2600 logf("inserting new tags...");
2601 for (i
= 0; i
< h
->entry_count
; i
++)
2603 struct temp_file_entry entry
;
2605 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2606 sizeof(struct temp_file_entry
))
2608 logf("read fail #3");
2614 if (entry
.tag_length
[index_type
] >= (long)sizeof(buf
))
2616 logf("too long entry!");
2621 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2622 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2623 entry
.tag_length
[index_type
])
2625 logf("read fail #4");
2630 if (TAGCACHE_IS_UNIQUE(index_type
))
2631 error
= !tempbuf_insert(buf
, i
, -1, true);
2633 error
= !tempbuf_insert(buf
, i
, tcmh
.tch
.entry_count
+ i
, false);
2637 logf("insert error");
2642 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2643 entry
.tag_length
[index_type
], SEEK_CUR
);
2648 /* Sort the buffer data and write it to the index file. */
2649 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
2651 * We need to truncate the index file now. There can be junk left
2652 * at the end of file (however, we _should_ always follow the
2653 * entry_count and don't crash with that).
2655 ftruncate(fd
, lseek(fd
, 0, SEEK_CUR
));
2657 i
= tempbuf_sort(fd
);
2660 logf("sorted %d tags", i
);
2663 * Now update all indexes in the master lookup file.
2665 logf("updating indices...");
2666 lseek(masterfd
, sizeof(struct master_header
), SEEK_SET
);
2667 for (i
= 0; i
< tcmh
.tch
.entry_count
; i
+= idxbuf_pos
)
2670 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2672 idxbuf_pos
= MIN(tcmh
.tch
.entry_count
- i
, IDX_BUF_DEPTH
);
2674 if (ecread(masterfd
, idxbuf
, idxbuf_pos
, index_entry_ec
, tc_stat
.econ
)
2675 != (int)sizeof(struct index_entry
)*idxbuf_pos
)
2677 logf("read fail #5");
2681 lseek(masterfd
, loc
, SEEK_SET
);
2683 for (j
= 0; j
< idxbuf_pos
; j
++)
2685 if (idxbuf
[j
].flag
& FLAG_DELETED
)
2687 /* We can just ignore deleted entries. */
2688 // idxbuf[j].tag_seek[index_type] = 0;
2692 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(
2693 idxbuf
[j
].tag_seek
[index_type
]/TAGFILE_ENTRY_CHUNK_LENGTH
2694 + commit_entry_count
);
2696 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2698 logf("update error: %ld/%d/%ld",
2699 idxbuf
[j
].flag
, i
+j
, tcmh
.tch
.entry_count
);
2707 /* Write back the updated index. */
2708 if (ecwrite(masterfd
, idxbuf
, idxbuf_pos
,
2709 index_entry_ec
, tc_stat
.econ
) !=
2710 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2721 * Walk through the temporary file containing the new tags.
2723 // build_normal_index(h, tmpfd, masterfd, idx);
2724 logf("updating new indices...");
2725 lseek(masterfd
, masterfd_pos
, SEEK_SET
);
2726 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2727 lseek(fd
, 0, SEEK_END
);
2728 for (i
= 0; i
< h
->entry_count
; i
+= idxbuf_pos
)
2732 idxbuf_pos
= MIN(h
->entry_count
- i
, IDX_BUF_DEPTH
);
2735 memset(idxbuf
, 0, sizeof(struct index_entry
)*IDX_BUF_DEPTH
);
2739 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2741 if (ecread(masterfd
, idxbuf
, idxbuf_pos
, index_entry_ec
, tc_stat
.econ
)
2742 != (int)sizeof(struct index_entry
)*idxbuf_pos
)
2744 logf("read fail #6");
2748 lseek(masterfd
, loc
, SEEK_SET
);
2751 /* Read entry headers. */
2752 for (j
= 0; j
< idxbuf_pos
; j
++)
2754 if (!TAGCACHE_IS_SORTED(index_type
))
2756 struct temp_file_entry entry
;
2757 struct tagfile_entry fe
;
2759 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2760 sizeof(struct temp_file_entry
))
2762 logf("read fail #7");
2768 if (entry
.tag_length
[index_type
] >= (int)sizeof(buf
))
2770 logf("too long entry!");
2771 logf("length=%d", entry
.tag_length
[index_type
]);
2772 logf("pos=0x%02lx", lseek(tmpfd
, 0, SEEK_CUR
));
2777 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2778 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2779 entry
.tag_length
[index_type
])
2781 logf("read fail #8");
2782 logf("offset=0x%02lx", entry
.tag_offset
[index_type
]);
2783 logf("length=0x%02x", entry
.tag_length
[index_type
]);
2788 /* Write to index file. */
2789 idxbuf
[j
].tag_seek
[index_type
] = lseek(fd
, 0, SEEK_CUR
);
2790 fe
.tag_length
= entry
.tag_length
[index_type
];
2791 fe
.idx_id
= tcmh
.tch
.entry_count
+ i
+ j
;
2792 ecwrite(fd
, &fe
, 1, tagfile_entry_ec
, tc_stat
.econ
);
2793 write(fd
, buf
, fe
.tag_length
);
2797 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2798 entry
.tag_length
[index_type
], SEEK_CUR
);
2802 /* Locate the correct entry from the sorted array. */
2803 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(i
+ j
);
2804 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2806 logf("entry not found (%d)", j
);
2814 if (ecwrite(masterfd
, idxbuf
, idxbuf_pos
,
2815 index_entry_ec
, tc_stat
.econ
) !=
2816 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2818 logf("tagcache: write fail #4");
2827 /* Finally write the header. */
2828 tch
.magic
= TAGCACHE_MAGIC
;
2829 tch
.entry_count
= tempbufidx
;
2830 tch
.datasize
= lseek(fd
, 0, SEEK_END
) - sizeof(struct tagcache_header
);
2831 lseek(fd
, 0, SEEK_SET
);
2832 ecwrite(fd
, &tch
, 1, tagcache_header_ec
, tc_stat
.econ
);
2834 if (index_type
!= tag_filename
)
2835 h
->datasize
+= tch
.datasize
;
2836 logf("s:%d/%ld/%ld", index_type
, tch
.datasize
, h
->datasize
);
2848 static bool commit(void)
2850 struct tagcache_header tch
;
2851 struct master_header tcmh
;
2852 char path
[MAX_PATH
];
2857 #ifdef HAVE_DIRCACHE
2858 bool dircache_buffer_stolen
= false;
2860 bool local_allocation
= false;
2862 logf("committing tagcache");
2867 file
= get_user_file_path(TAGCACHE_FILE_TEMP
,
2868 IS_FILE
|NEED_WRITE
, path
, sizeof(path
));
2870 tmpfd
= open(file
, O_RDONLY
);
2873 logf("nothing to commit");
2878 /* Load the header. */
2879 len
= sizeof(struct tagcache_header
);
2880 rc
= read(tmpfd
, &tch
, len
);
2882 if (tch
.magic
!= TAGCACHE_MAGIC
|| rc
!= len
)
2884 logf("incorrect tmpheader");
2890 if (tch
.entry_count
== 0)
2892 logf("nothing to commit");
2898 /* Fully initialize existing headers (if any) before going further. */
2899 tc_stat
.ready
= check_all_headers();
2901 #ifdef HAVE_EEPROM_SETTINGS
2902 remove(get_user_file_path(TAGCACHE_STATEFILE
, IS_FILE
| NEED_WRITE
,
2903 path
, sizeof(path
)));
2906 /* At first be sure to unload the ramcache! */
2907 #ifdef HAVE_TC_RAMCACHE
2908 tc_stat
.ramcache
= false;
2913 /* Try to steal every buffer we can :) */
2914 if (tempbuf_size
== 0)
2915 local_allocation
= true;
2917 #ifdef HAVE_DIRCACHE
2918 if (tempbuf_size
== 0)
2920 /* Try to steal the dircache buffer. */
2921 tempbuf
= dircache_steal_buffer(&tempbuf_size
);
2922 tempbuf_size
&= ~0x03;
2924 if (tempbuf_size
> 0)
2926 dircache_buffer_stolen
= true;
2931 #ifdef HAVE_TC_RAMCACHE
2932 if (tempbuf_size
== 0 && tc_stat
.ramcache_allocated
> 0)
2934 tempbuf
= (char *)(hdr
+ 1);
2935 tempbuf_size
= tc_stat
.ramcache_allocated
- sizeof(struct ramcache_header
) - 128;
2936 tempbuf_size
&= ~0x03;
2940 /* And finally fail if there are no buffers available. */
2941 if (tempbuf_size
== 0)
2943 logf("delaying commit until next boot");
2944 tc_stat
.commit_delayed
= true;
2950 logf("commit %ld entries...", tch
.entry_count
);
2952 /* Mark DB dirty so it will stay disabled if commit fails. */
2953 current_tcmh
.dirty
= true;
2954 update_master_header();
2956 /* Now create the index files. */
2957 tc_stat
.commit_step
= 0;
2959 tc_stat
.commit_delayed
= false;
2961 for (i
= 0; i
< TAG_COUNT
; i
++)
2965 if (TAGCACHE_IS_NUMERIC(i
))
2968 tc_stat
.commit_step
++;
2969 ret
= build_index(i
, &tch
, tmpfd
);
2973 logf("tagcache failed init");
2975 tc_stat
.commit_delayed
= true;
2977 tc_stat
.commit_step
= 0;
2983 if (!build_numeric_indices(&tch
, tmpfd
))
2985 logf("Failure to commit numeric indices");
2987 tc_stat
.commit_step
= 0;
2995 tc_stat
.commit_step
= 0;
2997 /* Update the master index headers. */
2998 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
3004 tcmh
.tch
.entry_count
+= tch
.entry_count
;
3005 tcmh
.tch
.datasize
= sizeof(struct master_header
)
3006 + sizeof(struct index_entry
) * tcmh
.tch
.entry_count
3011 lseek(masterfd
, 0, SEEK_SET
);
3012 ecwrite(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
);
3015 logf("tagcache committed");
3016 tc_stat
.ready
= check_all_headers();
3017 tc_stat
.readyvalid
= true;
3019 if (local_allocation
)
3025 #ifdef HAVE_DIRCACHE
3026 /* Rebuild the dircache, if we stole the buffer. */
3027 if (dircache_buffer_stolen
)
3031 #ifdef HAVE_TC_RAMCACHE
3032 /* Reload tagcache. */
3033 if (tc_stat
.ramcache_allocated
> 0)
3034 tagcache_start_scan();
3042 static void allocate_tempbuf(void)
3044 /* Yeah, malloc would be really nice now :) */
3046 tempbuf_size
= 32*1024*1024;
3047 tempbuf
= malloc(tempbuf_size
);
3049 tempbuf
= (char *)(((long)audiobuf
& ~0x03) + 0x04);
3050 tempbuf_size
= (long)audiobufend
- (long)audiobuf
- 4;
3051 audiobuf
+= tempbuf_size
;
3055 static void free_tempbuf(void)
3057 if (tempbuf_size
== 0)
3063 audiobuf
-= tempbuf_size
;
3071 static bool modify_numeric_entry(int masterfd
, int idx_id
, int tag
, long data
)
3073 struct index_entry idx
;
3078 if (!TAGCACHE_IS_NUMERIC(tag
))
3081 if (!get_index(masterfd
, idx_id
, &idx
, false))
3084 idx
.tag_seek
[tag
] = data
;
3085 idx
.flag
|= FLAG_DIRTYNUM
;
3087 return write_index(masterfd
, idx_id
, &idx
);
3091 bool tagcache_modify_numeric_entry(struct tagcache_search
*tcs
,
3094 struct master_header myhdr
;
3096 if (tcs
->masterfd
< 0)
3098 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, true)) < 0)
3102 return modify_numeric_entry(tcs
->masterfd
, tcs
->idx_id
, tag
, data
);
3106 #define COMMAND_QUEUE_IS_EMPTY (command_queue_ridx == command_queue_widx)
3108 static bool command_queue_is_full(void)
3112 next
= command_queue_widx
+ 1;
3113 if (next
>= TAGCACHE_COMMAND_QUEUE_LENGTH
)
3116 return (next
== command_queue_ridx
);
3119 static void command_queue_sync_callback(void *data
)
3122 struct master_header myhdr
;
3125 mutex_lock(&command_queue_mutex
);
3127 if ( (masterfd
= open_master_fd(&myhdr
, true)) < 0)
3130 while (command_queue_ridx
!= command_queue_widx
)
3132 struct tagcache_command_entry
*ce
= &command_queue
[command_queue_ridx
];
3134 switch (ce
->command
)
3136 case CMD_UPDATE_MASTER_HEADER
:
3139 update_master_header();
3141 /* Re-open the masterfd. */
3142 if ( (masterfd
= open_master_fd(&myhdr
, true)) < 0)
3147 case CMD_UPDATE_NUMERIC
:
3149 modify_numeric_entry(masterfd
, ce
->idx_id
, ce
->tag
, ce
->data
);
3154 if (++command_queue_ridx
>= TAGCACHE_COMMAND_QUEUE_LENGTH
)
3155 command_queue_ridx
= 0;
3160 tc_stat
.queue_length
= 0;
3161 mutex_unlock(&command_queue_mutex
);
3164 static void run_command_queue(bool force
)
3166 if (COMMAND_QUEUE_IS_EMPTY
)
3169 if (force
|| command_queue_is_full())
3170 command_queue_sync_callback(NULL
);
3172 register_storage_idle_func(command_queue_sync_callback
);
3175 static void queue_command(int cmd
, long idx_id
, int tag
, long data
)
3181 mutex_lock(&command_queue_mutex
);
3182 next
= command_queue_widx
+ 1;
3183 if (next
>= TAGCACHE_COMMAND_QUEUE_LENGTH
)
3186 /* Make sure queue is not full. */
3187 if (next
!= command_queue_ridx
)
3189 struct tagcache_command_entry
*ce
= &command_queue
[command_queue_widx
];
3192 ce
->idx_id
= idx_id
;
3196 command_queue_widx
= next
;
3198 tc_stat
.queue_length
++;
3200 mutex_unlock(&command_queue_mutex
);
3204 /* Queue is full, try again later... */
3205 mutex_unlock(&command_queue_mutex
);
3210 long tagcache_increase_serial(void)
3220 old
= current_tcmh
.serial
++;
3221 queue_command(CMD_UPDATE_MASTER_HEADER
, 0, 0, 0);
3226 void tagcache_update_numeric(int idx_id
, int tag
, long data
)
3228 queue_command(CMD_UPDATE_NUMERIC
, idx_id
, tag
, data
);
3230 #endif /* !__PCTOOL__ */
3232 long tagcache_get_serial(void)
3234 return current_tcmh
.serial
;
3237 long tagcache_get_commitid(void)
3239 return current_tcmh
.commitid
;
3242 static bool write_tag(int fd
, const char *tagstr
, const char *datastr
)
3247 snprintf(buf
, sizeof buf
, "%s=\"", tagstr
);
3248 for (i
= strlen(buf
); i
< (long)sizeof(buf
)-4; i
++)
3250 if (*datastr
== '\0')
3253 if (*datastr
== '"' || *datastr
== '\\')
3256 buf
[i
] = *(datastr
++);
3259 strcpy(&buf
[i
], "\" ");
3261 write(fd
, buf
, i
+ 2);
3268 static bool read_tag(char *dest
, long size
,
3269 const char *src
, const char *tagstr
)
3272 char current_tag
[32];
3274 while (*src
!= '\0')
3276 /* Skip all whitespace */
3284 /* Read in tag name */
3285 while (*src
!= '=' && *src
!= ' ')
3287 current_tag
[pos
] = *src
;
3291 if (*src
== '\0' || pos
>= (long)sizeof(current_tag
))
3294 current_tag
[pos
] = '\0';
3296 /* Read in tag data */
3298 /* Find the start. */
3299 while (*src
!= '"' && *src
!= '\0')
3302 if (*src
== '\0' || *(++src
) == '\0')
3305 /* Read the data. */
3306 for (pos
= 0; pos
< size
; pos
++)
3313 dest
[pos
] = *(src
+1);
3333 if (!strcasecmp(tagstr
, current_tag
))
3340 static int parse_changelog_line(int line_n
, const char *buf
, void *parameters
)
3342 struct index_entry idx
;
3343 char tag_data
[TAG_MAXLEN
+32];
3345 long masterfd
= (long)parameters
;
3346 const int import_tags
[] = { tag_playcount
, tag_rating
, tag_playtime
, tag_lastplayed
,
3354 logf("%d/%s", line_n
, buf
);
3355 if (!read_tag(tag_data
, sizeof tag_data
, buf
, "filename"))
3357 logf("filename missing");
3362 idx_id
= find_index(tag_data
);
3365 logf("entry not found");
3369 if (!get_index(masterfd
, idx_id
, &idx
, false))
3371 logf("failed to retrieve index entry");
3375 /* Stop if tag has already been modified. */
3376 if (idx
.flag
& FLAG_DIRTYNUM
)
3379 logf("import: %s", tag_data
);
3381 idx
.flag
|= FLAG_DIRTYNUM
;
3382 for (i
= 0; i
< (long)(sizeof(import_tags
)/sizeof(import_tags
[0])); i
++)
3386 if (!read_tag(tag_data
, sizeof tag_data
, buf
,
3387 tagcache_tag_to_str(import_tags
[i
])))
3392 data
= atoi(tag_data
);
3396 idx
.tag_seek
[import_tags
[i
]] = data
;
3398 if (import_tags
[i
] == tag_lastplayed
&& data
>= current_tcmh
.serial
)
3399 current_tcmh
.serial
= data
+ 1;
3400 else if (import_tags
[i
] == tag_commitid
&& data
>= current_tcmh
.commitid
)
3401 current_tcmh
.commitid
= data
+ 1;
3404 return write_index(masterfd
, idx_id
, &idx
) ? 0 : -5;
3407 bool tagcache_import_changelog(void)
3409 struct master_header myhdr
;
3410 struct tagcache_header tch
;
3413 char buf
[MAX(MAX_PATH
, 2048)];
3422 file
= get_user_file_path(TAGCACHE_FILE_CHANGELOG
,
3423 IS_FILE
|NEED_WRITE
, buf
, sizeof(buf
));
3424 clfd
= open(file
, O_RDONLY
);
3427 logf("failure to open changelog");
3431 if ( (masterfd
= open_master_fd(&myhdr
, true)) < 0)
3439 filenametag_fd
= open_tag_fd(&tch
, tag_filename
, false);
3441 fast_readline(clfd
, buf
, sizeof buf
, (long *)masterfd
,
3442 parse_changelog_line
);
3447 if (filenametag_fd
>= 0)
3449 close(filenametag_fd
);
3450 filenametag_fd
= -1;
3455 update_master_header();
3460 #endif /* !__PCTOOL__ */
3462 bool tagcache_create_changelog(struct tagcache_search
*tcs
)
3464 struct master_header myhdr
;
3465 struct index_entry idx
;
3467 char buf
[MAX(TAG_MAXLEN
+32, MAX_PATH
)];
3475 if (!tagcache_search(tcs
, tag_filename
))
3478 /* Initialize the changelog */
3479 file
= get_user_file_path(TAGCACHE_FILE_CHANGELOG
, IS_FILE
| NEED_WRITE
,
3481 clfd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
3484 logf("failure to open changelog");
3488 if (tcs
->masterfd
< 0)
3490 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, false)) < 0)
3495 lseek(tcs
->masterfd
, 0, SEEK_SET
);
3496 ecread(tcs
->masterfd
, &myhdr
, 1, master_header_ec
, tc_stat
.econ
);
3499 write(clfd
, "## Changelog version 1\n", 23);
3501 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
3503 if (ecread_index_entry(tcs
->masterfd
, &idx
) != sizeof(struct index_entry
))
3505 logf("read error #9");
3506 tagcache_search_finish(tcs
);
3511 /* Skip until the entry found has been modified. */
3512 if (! (idx
.flag
& FLAG_DIRTYNUM
) )
3515 /* Skip deleted entries too. */
3516 if (idx
.flag
& FLAG_DELETED
)
3519 /* Now retrieve all tags. */
3520 for (j
= 0; j
< TAG_COUNT
; j
++)
3522 if (TAGCACHE_IS_NUMERIC(j
))
3524 snprintf(temp
, sizeof temp
, "%d", (int)idx
.tag_seek
[j
]);
3525 write_tag(clfd
, tagcache_tag_to_str(j
), temp
);
3530 tagcache_retrieve(tcs
, i
, tcs
->type
, buf
, sizeof buf
);
3531 write_tag(clfd
, tagcache_tag_to_str(j
), buf
);
3534 write(clfd
, "\n", 1);
3540 tagcache_search_finish(tcs
);
3545 static bool delete_entry(long idx_id
)
3550 struct index_entry idx
, myidx
;
3551 struct master_header myhdr
;
3552 char buf
[TAG_MAXLEN
+32];
3553 int in_use
[TAG_COUNT
];
3555 logf("delete_entry(): %ld", idx_id
);
3557 #ifdef HAVE_TC_RAMCACHE
3558 /* At first mark the entry removed from ram cache. */
3559 if (tc_stat
.ramcache
)
3560 hdr
->indices
[idx_id
].flag
|= FLAG_DELETED
;
3563 if ( (masterfd
= open_master_fd(&myhdr
, true) ) < 0)
3566 lseek(masterfd
, idx_id
* sizeof(struct index_entry
), SEEK_CUR
);
3567 if (ecread_index_entry(masterfd
, &myidx
) != sizeof(struct index_entry
))
3569 logf("delete_entry(): read error");
3573 if (myidx
.flag
& FLAG_DELETED
)
3575 logf("delete_entry(): already deleted!");
3579 myidx
.flag
|= FLAG_DELETED
;
3580 lseek(masterfd
, -sizeof(struct index_entry
), SEEK_CUR
);
3581 if (ecwrite_index_entry(masterfd
, &myidx
) != sizeof(struct index_entry
))
3583 logf("delete_entry(): write_error #1");
3587 /* Now check which tags are no longer in use (if any) */
3588 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3591 lseek(masterfd
, sizeof(struct master_header
), SEEK_SET
);
3592 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
3594 struct index_entry
*idxp
;
3596 #ifdef HAVE_TC_RAMCACHE
3597 /* Use RAM DB if available for greater speed */
3598 if (tc_stat
.ramcache
)
3599 idxp
= &hdr
->indices
[i
];
3603 if (ecread_index_entry(masterfd
, &idx
) != sizeof(struct index_entry
))
3605 logf("delete_entry(): read error #2");
3611 if (idxp
->flag
& FLAG_DELETED
)
3614 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3616 if (TAGCACHE_IS_NUMERIC(tag
))
3619 if (idxp
->tag_seek
[tag
] == myidx
.tag_seek
[tag
])
3624 /* Now delete all tags no longer in use. */
3625 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3627 struct tagcache_header tch
;
3628 int oldseek
= myidx
.tag_seek
[tag
];
3630 if (TAGCACHE_IS_NUMERIC(tag
))
3634 * Replace tag seek with a hash value of the field string data.
3635 * That way runtime statistics of moved or altered files can be
3638 #ifdef HAVE_TC_RAMCACHE
3639 if (tc_stat
.ramcache
&& tag
!= tag_filename
)
3641 struct tagfile_entry
*tfe
;
3642 int32_t *seek
= &hdr
->indices
[idx_id
].tag_seek
[tag
];
3644 tfe
= (struct tagfile_entry
*)&hdr
->tags
[tag
][*seek
];
3645 *seek
= crc_32(tfe
->tag_data
, strlen(tfe
->tag_data
), 0xffffffff);
3646 myidx
.tag_seek
[tag
] = *seek
;
3651 struct tagfile_entry tfe
;
3653 /* Open the index file, which contains the tag names. */
3654 if ((fd
= open_tag_fd(&tch
, tag
, true)) < 0)
3657 /* Skip the header block */
3658 lseek(fd
, myidx
.tag_seek
[tag
], SEEK_SET
);
3659 if (ecread_tagfile_entry(fd
, &tfe
) != sizeof(struct tagfile_entry
))
3661 logf("delete_entry(): read error #3");
3665 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
3667 logf("delete_entry(): read error #4");
3671 myidx
.tag_seek
[tag
] = crc_32(buf
, strlen(buf
), 0xffffffff);
3676 logf("in use: %d/%d", tag
, in_use
[tag
]);
3685 #ifdef HAVE_TC_RAMCACHE
3686 /* Delete from ram. */
3687 if (tc_stat
.ramcache
&& tag
!= tag_filename
)
3689 struct tagfile_entry
*tagentry
= (struct tagfile_entry
*)&hdr
->tags
[tag
][oldseek
];
3690 tagentry
->tag_data
[0] = '\0';
3694 /* Open the index file, which contains the tag names. */
3697 if ((fd
= open_tag_fd(&tch
, tag
, true)) < 0)
3701 /* Skip the header block */
3702 lseek(fd
, oldseek
+ sizeof(struct tagfile_entry
), SEEK_SET
);
3704 /* Debug, print 10 first characters of the tag
3707 logf("TAG:%s", buf);
3708 lseek(fd, -10, SEEK_CUR);
3711 /* Write first data byte in tag as \0 */
3714 /* Now tag data has been removed */
3719 /* Write index entry back into master index. */
3720 lseek(masterfd
, sizeof(struct master_header
) +
3721 (idx_id
* sizeof(struct index_entry
)), SEEK_SET
);
3722 if (ecwrite_index_entry(masterfd
, &myidx
) != sizeof(struct index_entry
))
3724 logf("delete_entry(): write_error #2");
3743 * Returns true if there is an event waiting in the queue
3744 * that requires the current operation to be aborted.
3746 static bool check_event_queue(void)
3748 struct queue_event ev
;
3750 if(!queue_peek(&tagcache_queue
, &ev
))
3757 case SYS_USB_CONNECTED
:
3765 #ifdef HAVE_TC_RAMCACHE
3766 static bool allocate_tagcache(void)
3768 struct master_header tcmh
;
3771 /* Load the header. */
3772 if ( (fd
= open_master_fd(&tcmh
, false)) < 0)
3781 * Now calculate the required cache size plus
3782 * some extra space for alignment fixes.
3784 tc_stat
.ramcache_allocated
= tcmh
.tch
.datasize
+ 128 + TAGCACHE_RESERVE
+
3785 sizeof(struct ramcache_header
) + TAG_COUNT
*sizeof(void *);
3786 hdr
= buffer_alloc(tc_stat
.ramcache_allocated
+ 128);
3787 memset(hdr
, 0, sizeof(struct ramcache_header
));
3788 memcpy(&hdr
->h
, &tcmh
, sizeof(struct master_header
));
3789 hdr
->indices
= (struct index_entry
*)(hdr
+ 1);
3790 logf("tagcache: %d bytes allocated.", tc_stat
.ramcache_allocated
);
3795 # ifdef HAVE_EEPROM_SETTINGS
3796 static bool tagcache_dumpload(void)
3798 struct statefile_header shdr
;
3799 char path
[MAX_PATH
];
3805 file
= get_user_file_path(TAGCACHE_STATEFILE
, IS_FILE
| NEED_WRITE
,
3806 path
, sizeof(path
));
3807 fd
= open(file
, O_RDONLY
);
3810 logf("no tagcache statedump");
3814 /* Check the statefile memory placement */
3815 hdr
= buffer_alloc(0);
3816 rc
= read(fd
, &shdr
, sizeof(struct statefile_header
));
3817 if (rc
!= sizeof(struct statefile_header
)
3818 /* || (long)hdr != (long)shdr.hdr */)
3820 logf("incorrect statefile");
3826 offpos
= (long)hdr
- (long)shdr
.hdr
;
3828 /* Lets allocate real memory and load it */
3829 hdr
= buffer_alloc(shdr
.tc_stat
.ramcache_allocated
);
3830 rc
= read(fd
, hdr
, shdr
.tc_stat
.ramcache_allocated
);
3833 if (rc
!= shdr
.tc_stat
.ramcache_allocated
)
3835 logf("read failure!");
3840 memcpy(&tc_stat
, &shdr
.tc_stat
, sizeof(struct tagcache_stat
));
3842 /* Now fix the pointers */
3843 hdr
->indices
= (struct index_entry
*)((long)hdr
->indices
+ offpos
);
3844 for (i
= 0; i
< TAG_COUNT
; i
++)
3845 hdr
->tags
[i
] += offpos
;
3850 static bool tagcache_dumpsave(void)
3852 struct statefile_header shdr
;
3853 char path
[MAX_PATH
];
3857 if (!tc_stat
.ramcache
)
3860 file
= get_user_file_path(TAGCACHE_STATEFILE
, IS_FILE
| NEED_WRITE
,
3861 path
, sizeof(path
));
3862 fd
= open(file
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
3865 logf("failed to create a statedump");
3869 /* Create the header */
3871 memcpy(&shdr
.tc_stat
, &tc_stat
, sizeof(struct tagcache_stat
));
3872 write(fd
, &shdr
, sizeof(struct statefile_header
));
3874 /* And dump the data too */
3875 write(fd
, hdr
, tc_stat
.ramcache_allocated
);
3882 static bool load_tagcache(void)
3884 struct tagcache_header
*tch
;
3885 long bytesleft
= tc_stat
.ramcache_allocated
;
3886 struct index_entry
*idx
;
3888 char *p
, path
[MAX_PATH
];
3892 # ifdef HAVE_DIRCACHE
3893 while (dircache_is_initializing())
3896 dircache_set_appflag(DIRCACHE_APPFLAG_TAGCACHE
);
3899 logf("loading tagcache to ram...");
3901 file
= get_user_file_path(TAGCACHE_FILE_MASTER
,
3903 path
, sizeof(path
));
3904 fd
= open(file
, O_RDONLY
);
3907 logf("tagcache open failed");
3911 if (ecread(fd
, &hdr
->h
, 1, master_header_ec
, tc_stat
.econ
)
3912 != sizeof(struct master_header
)
3913 || hdr
->h
.tch
.magic
!= TAGCACHE_MAGIC
)
3915 logf("incorrect header");
3921 /* Load the master index table. */
3922 for (i
= 0; i
< hdr
->h
.tch
.entry_count
; i
++)
3924 rc
= ecread_index_entry(fd
, idx
);
3925 if (rc
!= sizeof(struct index_entry
))
3927 logf("read error #10");
3932 bytesleft
-= sizeof(struct index_entry
);
3933 if (bytesleft
< 0 || ((long)idx
- (long)hdr
->indices
) >= tc_stat
.ramcache_allocated
)
3935 logf("too big tagcache.");
3945 /* Load the tags. */
3947 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3949 struct tagfile_entry
*fe
;
3950 char buf
[TAG_MAXLEN
+32];
3952 if (TAGCACHE_IS_NUMERIC(tag
))
3955 //p = ((void *)p+1);
3956 p
= (char *)((long)p
& ~0x03) + 0x04;
3959 /* Check the header. */
3960 tch
= (struct tagcache_header
*)p
;
3961 p
+= sizeof(struct tagcache_header
);
3963 if ( (fd
= open_tag_fd(tch
, tag
, false)) < 0)
3966 for (hdr
->entry_count
[tag
] = 0;
3967 hdr
->entry_count
[tag
] < tch
->entry_count
;
3968 hdr
->entry_count
[tag
]++)
3972 if (do_timed_yield())
3974 /* Abort if we got a critical event in queue */
3975 if (check_event_queue())
3979 fe
= (struct tagfile_entry
*)p
;
3980 pos
= lseek(fd
, 0, SEEK_CUR
);
3981 rc
= ecread_tagfile_entry(fd
, fe
);
3982 if (rc
!= sizeof(struct tagfile_entry
))
3984 /* End of lookup table. */
3985 logf("read error #11");
3990 /* We have a special handling for the filename tags. */
3991 if (tag
== tag_filename
)
3993 # ifdef HAVE_DIRCACHE
3994 const struct dircache_entry
*dc
;
3997 // FIXME: This is wrong!
3998 // idx = &hdr->indices[hdr->entry_count[i]];
3999 idx
= &hdr
->indices
[fe
->idx_id
];
4001 if (fe
->tag_length
>= (long)sizeof(buf
)-1)
4005 logf("TAG:%s", buf
);
4006 logf("too long filename");
4011 rc
= read(fd
, buf
, fe
->tag_length
);
4012 if (rc
!= fe
->tag_length
)
4014 logf("read error #12");
4019 /* Check if the entry has already been removed */
4020 if (idx
->flag
& FLAG_DELETED
)
4023 /* This flag must not be used yet. */
4024 if (idx
->flag
& FLAG_DIRCACHE
)
4026 logf("internal error!");
4031 if (idx
->tag_seek
[tag
] != pos
)
4033 logf("corrupt data structures!");
4038 # ifdef HAVE_DIRCACHE
4039 if (dircache_is_enabled())
4041 dc
= dircache_get_entry_ptr(buf
);
4044 logf("Entry no longer valid.");
4046 if (global_settings
.tagcache_autoupdate
)
4047 delete_entry(fe
->idx_id
);
4051 idx
->flag
|= FLAG_DIRCACHE
;
4052 idx
->tag_seek
[tag_filename
] = (long)dc
;
4057 /* This will be very slow unless dircache is enabled
4058 or target is flash based, but do it anyway for
4060 /* Check if entry has been removed. */
4061 if (global_settings
.tagcache_autoupdate
)
4063 if (!file_exists(buf
))
4065 logf("Entry no longer valid.");
4067 delete_entry(fe
->idx_id
);
4076 bytesleft
-= sizeof(struct tagfile_entry
) + fe
->tag_length
;
4079 logf("too big tagcache #2");
4080 logf("tl: %ld", fe
->tag_length
);
4081 logf("bl: %ld", bytesleft
);
4087 rc
= read(fd
, fe
->tag_data
, fe
->tag_length
);
4090 if (rc
!= fe
->tag_length
)
4092 logf("read error #13");
4093 logf("rc=0x%04x", rc
); // 0x431
4094 logf("len=0x%04lx", fe
->tag_length
); // 0x4000
4095 logf("pos=0x%04lx", lseek(fd
, 0, SEEK_CUR
)); // 0x433
4096 logf("tag=0x%02x", tag
); // 0x00
4104 tc_stat
.ramcache_used
= tc_stat
.ramcache_allocated
- bytesleft
;
4105 logf("tagcache loaded into ram!");
4109 #endif /* HAVE_TC_RAMCACHE */
4111 static bool check_deleted_files(void)
4114 char buf
[TAG_MAXLEN
+32], path
[MAX_PATH
];
4116 struct tagfile_entry tfe
;
4118 logf("reverse scan...");
4119 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag_filename
);
4120 file
= get_user_file_path(buf
, IS_FILE
| NEED_WRITE
, path
, sizeof(path
));
4121 fd
= open(file
, O_RDONLY
);
4125 logf("%s open fail", buf
);
4129 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
4130 while (ecread_tagfile_entry(fd
, &tfe
) == sizeof(struct tagfile_entry
)
4132 && !check_event_queue()
4136 if (tfe
.tag_length
>= (long)sizeof(buf
)-1)
4138 logf("too long tag");
4143 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
4145 logf("read error #14");
4150 /* Check if the file has already deleted from the db. */
4154 /* Now check if the file exists. */
4155 if (!file_exists(buf
))
4157 logf("Entry no longer valid.");
4158 logf("-> %s / %ld", buf
, tfe
.tag_length
);
4159 delete_entry(tfe
.idx_id
);
4171 /* Note that this function must not be inlined, otherwise the whole point
4172 * of having the code in a separate function is lost.
4174 static void __attribute__ ((noinline
)) check_ignore(const char *dirname
,
4175 int *ignore
, int *unignore
)
4177 char newpath
[MAX_PATH
];
4179 /* check for a database.ignore file */
4180 snprintf(newpath
, MAX_PATH
, "%s/database.ignore", dirname
);
4181 *ignore
= file_exists(newpath
);
4182 /* check for a database.unignore file */
4183 snprintf(newpath
, MAX_PATH
, "%s/database.unignore", dirname
);
4184 *unignore
= file_exists(newpath
);
4188 static bool check_dir(const char *dirname
, int add_files
)
4192 int success
= false;
4193 int ignore
, unignore
;
4195 dir
= opendir(dirname
);
4198 logf("tagcache: opendir(%s) failed", dirname
);
4202 /* check for a database.ignore and database.unignore */
4203 check_ignore(dirname
, &ignore
, &unignore
);
4205 /* don't do anything if both ignore and unignore are there */
4206 if (ignore
!= unignore
)
4207 add_files
= unignore
;
4209 /* Recursively scan the dir. */
4213 while (!check_event_queue())
4216 struct dirent
*entry
;
4218 entry
= readdir(dir
);
4226 struct dirinfo info
= dir_get_info(dir
, entry
);
4228 if (!strcmp((char *)entry
->d_name
, ".") ||
4229 !strcmp((char *)entry
->d_name
, ".."))
4234 len
= strlen(curpath
);
4235 snprintf(&curpath
[len
], sizeof(curpath
) - len
, "/%s",
4238 processed_dir_count
++;
4239 if (info
.attribute
& ATTR_DIRECTORY
)
4240 check_dir(curpath
, add_files
);
4243 tc_stat
.curentry
= curpath
;
4245 /* Add a new entry to the temporary db file. */
4246 add_tagcache(curpath
, (info
.wrtdate
<< 16) | info
.wrttime
4247 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
4248 , dir
->internal_entry
4252 /* Wait until current path for debug screen is read and unset. */
4253 while (tc_stat
.syncscreen
&& tc_stat
.curentry
!= NULL
)
4256 tc_stat
.curentry
= NULL
;
4259 curpath
[len
] = '\0';
4267 void tagcache_screensync_event(void)
4269 tc_stat
.curentry
= NULL
;
4272 void tagcache_screensync_enable(bool state
)
4274 tc_stat
.syncscreen
= state
;
4277 void tagcache_build(const char *path
)
4279 struct tagcache_header header
;
4286 total_entry_count
= 0;
4287 processed_dir_count
= 0;
4289 #ifdef HAVE_DIRCACHE
4290 while (dircache_is_initializing())
4294 logf("updating tagcache");
4296 file
= get_user_file_path(TAGCACHE_FILE_TEMP
,
4297 IS_FILE
|NEED_WRITE
, buf
, sizeof(buf
));
4300 if (file_exists(file
))
4302 logf("skipping, cache already waiting for commit");
4306 cachefd
= open(file
, O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
4309 logf("master file open failed: %s", file
);
4313 filenametag_fd
= open_tag_fd(&header
, tag_filename
, false);
4317 logf("Scanning files...");
4318 /* Scan for new files. */
4319 memset(&header
, 0, sizeof(struct tagcache_header
));
4320 write(cachefd
, &header
, sizeof(struct tagcache_header
));
4322 if (strcmp("/", path
) != 0)
4323 strcpy(curpath
, path
);
4324 ret
= check_dir(path
, true);
4326 /* Write the header. */
4327 header
.magic
= TAGCACHE_MAGIC
;
4328 header
.datasize
= data_size
;
4329 header
.entry_count
= total_entry_count
;
4330 lseek(cachefd
, 0, SEEK_SET
);
4331 write(cachefd
, &header
, sizeof(struct tagcache_header
));
4334 if (filenametag_fd
>= 0)
4336 close(filenametag_fd
);
4337 filenametag_fd
= -1;
4347 /* Commit changes to the database. */
4354 logf("tagcache built!");
4360 #ifdef HAVE_TC_RAMCACHE
4363 /* Import runtime statistics if we just initialized the db. */
4364 if (hdr
->h
.serial
== 0)
4365 queue_post(&tagcache_queue
, Q_IMPORT_CHANGELOG
, 0);
4372 #ifdef HAVE_TC_RAMCACHE
4373 static void load_ramcache(void)
4380 /* At first we should load the cache (if exists). */
4381 tc_stat
.ramcache
= load_tagcache();
4383 if (!tc_stat
.ramcache
)
4385 /* If loading failed, it must indicate some problem with the db
4386 * so disable it entirely to prevent further issues. */
4387 tc_stat
.ready
= false;
4394 void tagcache_unload_ramcache(void)
4396 tc_stat
.ramcache
= false;
4397 /* Just to make sure there is no statefile present. */
4400 char path
[MAX_PATH
];
4401 remove(get_user_file_path(TAGCACHE_STATEFILE
, IS_FILE
| NEED_WRITE
,
4402 path
, sizeof(path
)));
4408 static void tagcache_thread(void)
4410 struct queue_event ev
;
4411 bool check_done
= false;
4412 char path
[MAX_PATH
];
4414 /* If the previous cache build/update was interrupted, commit
4415 * the changes first in foreground. */
4421 #ifdef HAVE_TC_RAMCACHE
4422 # ifdef HAVE_EEPROM_SETTINGS
4423 if (firmware_settings
.initialized
&& firmware_settings
.disk_clean
4424 && global_settings
.tagcache_ram
)
4426 check_done
= tagcache_dumpload();
4429 remove(get_user_file_path(TAGCACHE_STATEFILE
, IS_FILE
| NEED_WRITE
,
4430 path
, sizeof(path
)));
4433 /* Allocate space for the tagcache if found on disk. */
4434 if (global_settings
.tagcache_ram
&& !tc_stat
.ramcache
)
4435 allocate_tagcache();
4439 tc_stat
.initialized
= true;
4441 /* Don't delay bootup with the header check but do it on background. */
4445 tc_stat
.ready
= check_all_headers();
4446 tc_stat
.readyvalid
= true;
4451 run_command_queue(false);
4453 queue_wait_w_tmo(&tagcache_queue
, &ev
, HZ
);
4457 case Q_IMPORT_CHANGELOG
:
4458 tagcache_import_changelog();
4463 remove(get_user_file_path(TAGCACHE_FILE_TEMP
,
4464 IS_FILE
|NEED_WRITE
, path
, sizeof(path
)));
4465 tagcache_build("/");
4469 tagcache_build("/");
4470 #ifdef HAVE_TC_RAMCACHE
4473 check_deleted_files();
4479 if (check_done
|| !tc_stat
.ready
)
4482 #ifdef HAVE_TC_RAMCACHE
4483 if (!tc_stat
.ramcache
&& global_settings
.tagcache_ram
)
4486 if (tc_stat
.ramcache
&& global_settings
.tagcache_autoupdate
)
4487 tagcache_build("/");
4491 if (global_settings
.tagcache_autoupdate
)
4493 tagcache_build("/");
4495 /* This will be very slow unless dircache is enabled
4496 or target is flash based, but do it anyway for
4498 check_deleted_files();
4501 logf("tagcache check done");
4512 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
4513 case SYS_USB_CONNECTED
:
4514 logf("USB: TagCache");
4515 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
4516 usb_wait_for_disconnect(&tagcache_queue
);
4523 bool tagcache_prepare_shutdown(void)
4525 if (tagcache_get_commit_step() > 0)
4528 tagcache_stop_scan();
4529 while (read_lock
|| write_lock
)
4535 void tagcache_shutdown(void)
4537 /* Flush the command queue. */
4538 run_command_queue(true);
4540 #ifdef HAVE_EEPROM_SETTINGS
4541 if (tc_stat
.ramcache
)
4542 tagcache_dumpsave();
4546 static int get_progress(void)
4548 int total_count
= -1;
4550 #ifdef HAVE_DIRCACHE
4551 if (dircache_is_enabled())
4553 total_count
= dircache_get_entry_count();
4557 #ifdef HAVE_TC_RAMCACHE
4559 if (hdr
&& tc_stat
.ramcache
)
4560 total_count
= hdr
->h
.tch
.entry_count
;
4564 if (total_count
< 0)
4567 return processed_dir_count
* 100 / total_count
;
4570 struct tagcache_stat
* tagcache_get_stat(void)
4572 tc_stat
.progress
= get_progress();
4573 tc_stat
.processed_entries
= processed_dir_count
;
4578 void tagcache_start_scan(void)
4580 queue_post(&tagcache_queue
, Q_START_SCAN
, 0);
4583 bool tagcache_update(void)
4588 queue_post(&tagcache_queue
, Q_UPDATE
, 0);
4592 bool tagcache_rebuild()
4594 queue_post(&tagcache_queue
, Q_REBUILD
, 0);
4598 void tagcache_stop_scan(void)
4600 queue_post(&tagcache_queue
, Q_STOP_SCAN
, 0);
4603 #ifdef HAVE_TC_RAMCACHE
4604 bool tagcache_is_ramcache(void)
4606 return tc_stat
.ramcache
;
4610 #endif /* !__PCTOOL__ */
4613 void tagcache_init(void)
4615 memset(&tc_stat
, 0, sizeof(struct tagcache_stat
));
4616 memset(¤t_tcmh
, 0, sizeof(struct master_header
));
4617 filenametag_fd
= -1;
4618 write_lock
= read_lock
= 0;
4621 mutex_init(&command_queue_mutex
);
4622 queue_init(&tagcache_queue
, true);
4623 create_thread(tagcache_thread
, tagcache_stack
,
4624 sizeof(tagcache_stack
), 0, tagcache_thread_name
4625 IF_PRIO(, PRIORITY_BACKGROUND
)
4628 tc_stat
.initialized
= true;
4632 tc_stat
.ready
= check_all_headers();
4637 void tagcache_reverse_scan(void)
4639 logf("Checking for deleted files");
4640 check_deleted_files();
4644 bool tagcache_is_initialized(void)
4646 return tc_stat
.initialized
;
4648 bool tagcache_is_usable(void)
4650 return tc_stat
.initialized
&& tc_stat
.ready
;
4652 int tagcache_get_commit_step(void)
4654 return tc_stat
.commit_step
;
4656 int tagcache_get_max_commit_step(void)
4658 return (int)(SORTED_TAGS_COUNT
)+1;