1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
23 * ----------x---------x------------------x-----
25 * +---------------x-------+ | TagCache | Libraries
26 * | Modification routines | | Core |
27 * +-x---------x-----------+ | |
29 * | +------x-------------x-+ +-------------x-----+ |
30 * | | x==x Filters & clauses | |
31 * | | Search routines | +-------------------+ |
32 * | | x============================x DirCache
33 * | +-x--------------------+ | (optional)
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 * +------------------+ |
79 #include "eeprom_settings.h"
83 #define yield() do { } while(0)
84 #define sim_sleep(timeout) do { } while(0)
85 #define do_timed_yield() do { } while(0)
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";
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
{
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. */
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 */
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
;
195 /* Pointer to allocated ramcache_header */
196 static struct ramcache_header
*hdr
;
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
];
210 struct tempbuf_id_list
{
212 struct tempbuf_id_list
*next
;
215 struct tempbuf_searchidx
{
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
)
246 for (i
= 0; i
< (int)(sizeof(numeric_tags
)/sizeof(numeric_tags
[0])); i
++)
248 if (type
== numeric_tags
[i
])
255 bool tagcache_is_unique_tag(int type
)
259 for (i
= 0; i
< (int)(sizeof(unique_tags
)/sizeof(unique_tags
[0])); i
++)
261 if (type
== unique_tags
[i
])
268 bool tagcache_is_sorted_tag(int type
)
272 for (i
= 0; i
< (int)(sizeof(sorted_tags
)/sizeof(sorted_tags
[0])); i
++)
274 if (type
== sorted_tags
[i
])
281 static int open_tag_fd(struct tagcache_header
*hdr
, int tag
, bool write
)
287 if (tagcache_is_numeric_tag(tag
) || tag
< 0 || tag
>= TAG_COUNT
)
290 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag
);
292 fd
= open(buf
, write
? O_RDWR
: O_RDONLY
);
295 logf("tag file open failed: tag=%d write=%d file=%s", tag
, write
, buf
);
296 tc_stat
.ready
= false;
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;
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);
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;
335 /* Check if we tagcache is loaded into ram. */
336 if (!tc_stat
.ramcache
)
340 dc
= dircache_get_entry_ptr(filename
);
344 logf("tagcache: file not found.");
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);
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;
383 struct tagfile_entry tfe
;
385 char buf
[TAG_MAXLEN
+32];
396 if ( (fd
= open_tag_fd(&tch
, tag_filename
, false)) < 0)
403 lseek(fd
, last_pos
, SEEK_SET
);
405 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
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
))
420 if (tfe
.tag_length
>= (long)sizeof(buf
))
422 logf("too long tag #1");
428 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
430 logf("read error #2");
436 if (!strcasecmp(filename
, buf
))
438 last_pos
= pos_history
[pos_history_idx
];
443 if (pos_history_idx
< POS_HISTORY_COUNT
- 1)
457 if (fd
!= filenametag_fd
)
462 if (fd
!= filenametag_fd
)
468 static int find_index(const char *filename
)
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
);
478 idx_id
= find_entry_disk(filename
);
483 bool tagcache_find_index(struct tagcache_search
*tcs
, const char *filename
)
490 idx_id
= find_index(filename
);
494 if (!tagcache_search(tcs
, tag_filename
))
497 tcs
->entry_count
= 0;
498 tcs
->idx_id
= idx_id
;
503 static bool get_index(int masterfd
, int idxid
,
504 struct index_entry
*idx
, bool use_ram
)
508 logf("Incorrect idxid: %d", idxid
);
512 #ifdef HAVE_TC_RAMCACHE
513 if (tc_stat
.ramcache
&& use_ram
)
515 if (hdr
->indices
[idxid
].flag
& FLAG_DELETED
)
518 memcpy(idx
, &hdr
->indices
[idxid
], sizeof(struct index_entry
));
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");
534 if (idx
->flag
& FLAG_DELETED
)
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!");
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
)
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
);
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
);
584 static bool open_files(struct tagcache_search
*tcs
, int tag
)
586 if (tcs
->idxfd
[tag
] < 0)
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!");
603 static bool retrieve(struct tagcache_search
*tcs
, struct index_entry
*idx
,
604 int tag
, char *buf
, long size
)
606 struct tagfile_entry tfe
;
611 if (tagcache_is_numeric_tag(tag
))
614 seek
= idx
->tag_seek
[tag
];
617 logf("Retrieve failed");
621 #ifdef HAVE_TC_RAMCACHE
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
,
635 if (tag
!= tag_filename
)
637 ep
= (struct tagfile_entry
*)&hdr
->tags
[tag
][seek
];
638 strncpy(buf
, ep
->tag_data
, size
-1);
645 if (!open_files(tcs
, tag
))
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");
656 if (tfe
.tag_length
>= size
)
658 logf("too small buffer");
662 if (read(tcs
->idxfd
[tag
], buf
, tfe
.tag_length
) !=
665 logf("read error #6");
669 buf
[tfe
.tag_length
] = '\0';
674 static long check_virtual_tags(int tag
, const struct index_entry
*idx
)
680 case tag_virt_length_sec
:
681 data
= (idx
->tag_seek
[tag_length
]/1000) % 60;
684 case tag_virt_length_min
:
685 data
= (idx
->tag_seek
[tag_length
]/1000) / 60;
688 case tag_virt_playtime_sec
:
689 data
= (idx
->tag_seek
[tag_playtime
]/1000) % 60;
692 case tag_virt_playtime_min
:
693 data
= (idx
->tag_seek
[tag_playtime
]/1000) / 60;
696 case tag_virt_autoscore
:
697 if (idx
->tag_seek
[tag_length
] == 0
698 || idx
->tag_seek
[tag_playcount
] == 0)
704 data
= 100 * idx
->tag_seek
[tag_playtime
]
705 / idx
->tag_seek
[tag_length
]
706 / idx
->tag_seek
[tag_playcount
];
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;
716 data
= idx
->tag_seek
[tag
];
722 long tagcache_get_numeric(const struct tagcache_search
*tcs
, int tag
)
724 struct index_entry idx
;
729 if (!tagcache_is_numeric_tag(tag
))
732 if (!get_index(tcs
->masterfd
, tcs
->idx_id
, &idx
, true))
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
)
746 return !strcasecmp(&str1
[str_len
- clause_len
], str2
);
749 inline static bool str_oneof(const char *str
, const char *list
)
752 int l
, len
= strlen(str
);
756 sep
= strchr(list
, '|');
757 l
= sep
? (long)sep
- (long)list
: (int)strlen(list
);
758 if ((l
==len
) && !strncasecmp(str
, list
, len
))
760 list
+= sep
? l
+ 1 : l
;
766 static bool check_against_clause(long numeric
, const char *str
,
767 const struct tagcache_search_clause
*clause
)
771 switch (clause
->type
)
774 return numeric
== clause
->numeric_data
;
776 return numeric
!= clause
->numeric_data
;
778 return numeric
> clause
->numeric_data
;
780 return numeric
>= clause
->numeric_data
;
782 return numeric
< clause
->numeric_data
;
784 return numeric
<= clause
->numeric_data
;
786 logf("Incorrect numeric tag: %d", clause
->type
);
791 switch (clause
->type
)
794 return !strcasecmp(clause
->str
, str
);
796 return strcasecmp(clause
->str
, str
);
798 return 0>strcasecmp(clause
->str
, str
);
800 return 0>=strcasecmp(clause
->str
, str
);
802 return 0<strcasecmp(clause
->str
, str
);
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
);
818 return str_oneof(str
, clause
->str
);
821 logf("Incorrect tag: %d", clause
->type
);
828 static bool check_clauses(struct tagcache_search
*tcs
,
829 struct index_entry
*idx
,
830 struct tagcache_search_clause
**clause
, int count
)
834 #ifdef HAVE_TC_RAMCACHE
837 /* Go through all conditional clauses. */
838 for (i
= 0; i
< count
; i
++)
840 struct tagfile_entry
*tfe
;
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
);
856 tfe
= (struct tagfile_entry
*)&hdr
->tags
[clause
[i
]->tag
][seek
];
861 if (!check_against_clause(seek
, str
, clause
[i
]))
868 /* Check for conditions. */
869 for (i
= 0; i
< count
; i
++)
871 struct tagfile_entry tfe
;
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!");
889 read(fd
, str
, tfe
.tag_length
);
891 /* Check if entry has been deleted. */
896 if (!check_against_clause(seek
, str
, clause
[i
]))
904 bool tagcache_check_clauses(struct tagcache_search
*tcs
,
905 struct tagcache_search_clause
**clause
, int count
)
907 struct index_entry idx
;
912 if (!get_index(tcs
->masterfd
, tcs
->idx_id
, &idx
, true))
915 return check_clauses(tcs
, &idx
, clause
, count
);
918 static bool add_uniqbuf(struct tagcache_search
*tcs
, unsigned long id
)
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
)))
930 for (i
= 0; i
< tcs
->unique_list_count
; i
++)
932 /* Return false if entry is found. */
933 if (tcs
->unique_list
[i
] == id
)
937 if (tcs
->unique_list_count
< tcs
->unique_list_capacity
)
939 tcs
->unique_list
[i
] = id
;
940 tcs
->unique_list_count
++;
946 static bool build_lookup_list(struct tagcache_search
*tcs
)
948 struct index_entry entry
;
951 tcs
->seek_list_count
= 0;
953 #ifdef HAVE_TC_RAMCACHE
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
)
964 /* Skip deleted files. */
965 if (idx
->flag
& FLAG_DELETED
)
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
])
977 if (j
< tcs
->filter_count
)
980 /* Check for conditions. */
981 if (!check_clauses(tcs
, idx
, tcs
->clause
, tcs
->clause_count
))
984 /* Add to the seek list if not already in uniq buffer. */
985 if (!add_uniqbuf(tcs
, idx
->tag_seek
[tcs
->type
]))
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
++;
996 return tcs
->seek_list_count
> 0;
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
)
1011 /* Check if entry has been deleted. */
1012 if (entry
.flag
& FLAG_DELETED
)
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
])
1022 if (i
< tcs
->filter_count
)
1025 /* Check for conditions. */
1026 if (!check_clauses(tcs
, &entry
, tcs
->clause
, tcs
->clause_count
))
1029 /* Add to the seek list if not already in uniq buffer. */
1030 if (!add_uniqbuf(tcs
, entry
.tag_seek
[tcs
->type
]))
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
++;
1041 return tcs
->seek_list_count
> 0;
1045 static void remove_files(void)
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
))
1059 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, i
);
1065 static int open_master_fd(struct master_header
*hdr
, bool write
)
1070 fd
= open(TAGCACHE_FILE_MASTER
, write
? O_RDWR
: O_RDONLY
);
1073 logf("master file open failed for R/W");
1074 tc_stat
.ready
= false;
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
))
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;
1100 tc_stat
.econ
= true;
1105 static bool check_all_headers(void)
1107 struct master_header myhdr
;
1108 struct tagcache_header tch
;
1112 if ( (fd
= open_master_fd(&myhdr
, false)) < 0)
1118 logf("tagcache is dirty!");
1122 memcpy(¤t_tcmh
, &myhdr
, sizeof(struct master_header
));
1124 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
1126 if (tagcache_is_numeric_tag(tag
))
1129 if ( (fd
= open_tag_fd(&tch
, tag
, false)) < 0)
1138 bool tagcache_search(struct tagcache_search
*tcs
, int tag
)
1140 struct tagcache_header tag_hdr
;
1141 struct master_header master_hdr
;
1144 if (tcs
->initialized
)
1145 tagcache_search_finish(tcs
);
1150 memset(tcs
, 0, sizeof(struct tagcache_search
));
1151 if (tc_stat
.commit_step
> 0 || !tc_stat
.ready
)
1154 tcs
->position
= sizeof(struct tagcache_header
);
1157 tcs
->seek_list_count
= 0;
1158 tcs
->filter_count
= 0;
1161 for (i
= 0; i
< TAG_COUNT
; i
++)
1164 #ifndef HAVE_TC_RAMCACHE
1165 tcs
->ramsearch
= false;
1167 tcs
->ramsearch
= tc_stat
.ramcache
;
1170 tcs
->entry_count
= hdr
->entry_count
[tcs
->type
];
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)
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)
1191 tcs
->initialized
= 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
,
1208 if (tcs
->filter_count
== TAGCACHE_MAX_FILTERS
)
1211 if (!tagcache_is_unique_tag(tag
) || tagcache_is_numeric_tag(tag
))
1214 tcs
->filter_tag
[tcs
->filter_count
] = tag
;
1215 tcs
->filter_seek
[tcs
->filter_count
] = seek
;
1216 tcs
->filter_count
++;
1221 bool tagcache_search_add_clause(struct tagcache_search
*tcs
,
1222 struct tagcache_search_clause
*clause
)
1226 if (tcs
->clause_count
>= TAGCACHE_MAX_CLAUSES
)
1228 logf("Too many clauses");
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
)
1241 if (!tagcache_is_numeric_tag(clause
->tag
) && tcs
->idxfd
[clause
->tag
] < 0)
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
++;
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
;
1264 if (!tcs
->valid
|| !tc_stat
.ready
)
1267 if (tcs
->idxfd
[tcs
->type
] < 0 && !tagcache_is_numeric_tag(tcs
->type
)
1268 #ifdef HAVE_TC_RAMCACHE
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
))
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
))
1299 lseek(tcs
->idxfd
[tcs
->type
], tcs
->seek_list
[tcs
->seek_list_count
], SEEK_SET
);
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
;
1310 tcs
->result_len
= strlen(buf
) + 1;
1315 #ifdef HAVE_TC_RAMCACHE
1316 if (tcs
->ramsearch
&& TAG_FILENAME_RAM(tcs
))
1318 struct tagfile_entry
*ep
;
1320 if (tcs
->entry_count
== 0)
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
,
1335 tcs
->result_len
= strlen(buf
) + 1;
1336 tcs
->idx_id
= FLAG_GET_ATTR(flag
);
1337 tcs
->ramresult
= false;
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;
1355 if (!open_files(tcs
, tcs
->type
))
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
))
1368 if (entry
.tag_length
> (long)sizeof(buf
))
1371 logf("too long tag #2");
1375 if (read(tcs
->idxfd
[tcs
->type
], buf
, entry
.tag_length
) != entry
.tag_length
)
1378 logf("read error #4");
1383 tcs
->result_len
= strlen(tcs
->result
) + 1;
1384 tcs
->idx_id
= entry
.idx_id
;
1385 tcs
->ramresult
= false;
1390 bool tagcache_get_next(struct tagcache_search
*tcs
)
1392 while (get_next(tcs
))
1394 if (tcs
->result_len
> 1)
1401 bool tagcache_retrieve(struct tagcache_search
*tcs
, int idxid
,
1402 int tag
, char *buf
, long size
)
1404 struct index_entry idx
;
1407 if (!get_index(tcs
->masterfd
, idxid
, &idx
, true))
1410 return retrieve(tcs
, &idx
, tag
, buf
, size
);
1413 static bool update_master_header(void)
1415 struct master_header myhdr
;
1421 if ( (fd
= open_master_fd(&myhdr
, true)) < 0)
1424 myhdr
.serial
= current_tcmh
.serial
;
1425 myhdr
.commitid
= current_tcmh
.commitid
;
1428 lseek(fd
, 0, SEEK_SET
);
1429 ecwrite(fd
, &myhdr
, 1, master_header_ec
, tc_stat
.econ
);
1432 #ifdef HAVE_TC_RAMCACHE
1435 hdr
->h
.serial
= current_tcmh
.serial
;
1436 hdr
->h
.commitid
= current_tcmh
.commitid
;
1445 void tagcache_modify(struct tagcache_search
*tcs
, int type
, const char *text
)
1447 struct tagentry
*entry
;
1449 if (tcs
->type
!= tag_title
)
1452 /* We will need reserve buffer for this. */
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();
1466 void tagcache_search_finish(struct tagcache_search
*tcs
)
1470 if (!tcs
->initialized
)
1473 if (tcs
->masterfd
>= 0)
1475 close(tcs
->masterfd
);
1479 for (i
= 0; i
< TAG_COUNT
; i
++)
1481 if (tcs
->idxfd
[i
] >= 0)
1483 close(tcs
->idxfd
[i
]);
1488 tcs
->ramsearch
= false;
1490 tcs
->initialized
= 0;
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
;
1520 /* Find the corresponding entry in tagcache. */
1521 idx_id
= find_entry_ram(filename
, NULL
);
1522 if (idx_id
< 0 || !tc_stat
.ramcache
)
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)
1550 static inline void write_item(const char *item
)
1552 int len
= strlen(item
) + 1;
1555 write(cachefd
, item
, len
);
1558 static int check_if_empty(char **tag
)
1562 if (*tag
== NULL
|| *tag
[0] == '\0')
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';
1579 #define ADD_TAG(entry,tag,data) \
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
)
1588 static void add_tagcache(char *path
)
1591 struct track_info track
;
1592 struct temp_file_entry entry
;
1595 char tracknumfix
[3];
1597 int path_length
= strlen(path
);
1598 bool has_albumartist
;
1603 /* Check for overlength file path. */
1604 if (path_length
> TAG_MAXLEN
)
1606 /* Path can't be shortened. */
1607 logf("Too long path: %s", path
);
1611 /* Check if the file is supported. */
1612 if (probe_file_format(path
) == AFMT_UNKNOWN
)
1615 /* Check if the file is already cached. */
1616 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1617 if (tc_stat
.ramcache
&& dircache_is_enabled())
1619 if (find_entry_ram(path
, dc
) >= 0)
1625 if (filenametag_fd
>= 0)
1627 if (find_entry_disk(path
) >= 0)
1632 fd
= open(path
, O_RDONLY
);
1635 logf("open fail: %s", path
);
1639 memset(&track
, 0, sizeof(struct track_info
));
1640 memset(&entry
, 0, sizeof(struct temp_file_entry
));
1641 memset(&tracknumfix
, 0, sizeof(tracknumfix
));
1642 ret
= get_metadata(&track
, fd
, path
, false);
1648 logf("-> %s", path
);
1650 /* Generate track number if missing. */
1651 if (track
.id3
.tracknum
<= 0)
1653 const char *p
= strrchr(path
, '.');
1656 p
= &path
[strlen(path
)-1];
1660 if (isdigit(*p
) && isdigit(*(p
-1)))
1662 tracknumfix
[1] = *p
--;
1663 tracknumfix
[0] = *p
;
1669 if (tracknumfix
[0] != '\0')
1671 track
.id3
.tracknum
= atoi(tracknumfix
);
1672 /* Set a flag to indicate track number has been generated. */
1673 entry
.flag
|= FLAG_TRKNUMGEN
;
1677 /* Unable to generate track number. */
1678 track
.id3
.tracknum
= -1;
1683 entry
.tag_offset
[tag_year
] = track
.id3
.year
;
1684 entry
.tag_offset
[tag_tracknumber
] = track
.id3
.tracknum
;
1685 entry
.tag_offset
[tag_length
] = track
.id3
.length
;
1686 entry
.tag_offset
[tag_bitrate
] = track
.id3
.bitrate
;
1689 has_albumartist
= track
.id3
.albumartist
!= NULL
1690 && strlen(track
.id3
.albumartist
) > 0;
1692 ADD_TAG(entry
, tag_filename
, &path
);
1693 ADD_TAG(entry
, tag_title
, &track
.id3
.title
);
1694 ADD_TAG(entry
, tag_artist
, &track
.id3
.artist
);
1695 ADD_TAG(entry
, tag_album
, &track
.id3
.album
);
1696 ADD_TAG(entry
, tag_genre
, &track
.id3
.genre_string
);
1697 ADD_TAG(entry
, tag_composer
, &track
.id3
.composer
);
1698 ADD_TAG(entry
, tag_comment
, &track
.id3
.comment
);
1699 if (has_albumartist
)
1701 ADD_TAG(entry
, tag_albumartist
, &track
.id3
.albumartist
);
1705 ADD_TAG(entry
, tag_albumartist
, &track
.id3
.artist
);
1707 entry
.data_length
= offset
;
1709 /* Write the header */
1710 write(cachefd
, &entry
, sizeof(struct temp_file_entry
));
1712 /* And tags also... Correct order is critical */
1714 write_item(track
.id3
.title
);
1715 write_item(track
.id3
.artist
);
1716 write_item(track
.id3
.album
);
1717 write_item(track
.id3
.genre_string
);
1718 write_item(track
.id3
.composer
);
1719 write_item(track
.id3
.comment
);
1720 if (has_albumartist
)
1722 write_item(track
.id3
.albumartist
);
1726 write_item(track
.id3
.artist
);
1728 total_entry_count
++;
1731 static bool tempbuf_insert(char *str
, int id
, int idx_id
, bool unique
)
1733 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1734 int len
= strlen(str
)+1;
1737 unsigned *crcbuf
= (unsigned *)&tempbuf
[tempbuf_size
-4];
1738 char buf
[TAG_MAXLEN
+32];
1740 for (i
= 0; str
[i
] != '\0' && i
< (int)sizeof(buf
)-1; i
++)
1741 buf
[i
] = tolower(str
[i
]);
1744 crc32
= crc_32(buf
, i
, 0xffffffff);
1748 /* Check if the crc does not exist -> entry does not exist for sure. */
1749 for (i
= 0; i
< tempbufidx
; i
++)
1751 if (crcbuf
[-i
] != crc32
)
1754 if (!strcasecmp(str
, index
[i
].str
))
1756 if (id
< 0 || id
>= lookup_buffer_depth
)
1758 logf("lookup buf overf.: %d", id
);
1762 lookup
[id
] = &index
[i
];
1768 /* Insert to CRC buffer. */
1769 crcbuf
[-tempbufidx
] = crc32
;
1772 /* Insert it to the buffer. */
1773 tempbuf_left
-= len
;
1774 if (tempbuf_left
- 4 < 0 || tempbufidx
>= commit_entry_count
-1)
1777 if (id
>= lookup_buffer_depth
)
1779 logf("lookup buf overf. #2: %d", id
);
1785 lookup
[id
] = &index
[tempbufidx
];
1786 index
[tempbufidx
].idlist
.id
= id
;
1789 index
[tempbufidx
].idlist
.id
= -1;
1791 index
[tempbufidx
].idlist
.next
= NULL
;
1792 index
[tempbufidx
].idx_id
= idx_id
;
1793 index
[tempbufidx
].seek
= -1;
1794 index
[tempbufidx
].str
= &tempbuf
[tempbuf_pos
];
1795 memcpy(index
[tempbufidx
].str
, str
, len
);
1802 static int compare(const void *p1
, const void *p2
)
1806 struct tempbuf_searchidx
*e1
= (struct tempbuf_searchidx
*)p1
;
1807 struct tempbuf_searchidx
*e2
= (struct tempbuf_searchidx
*)p2
;
1809 if (strcmp(e1
->str
, UNTAGGED
) == 0)
1811 if (strcmp(e2
->str
, UNTAGGED
) == 0)
1815 else if (strcmp(e2
->str
, UNTAGGED
) == 0)
1818 return strncasecmp(e1
->str
, e2
->str
, TAG_MAXLEN
);
1821 static int tempbuf_sort(int fd
)
1823 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1824 struct tagfile_entry fe
;
1828 /* Generate reverse lookup entries. */
1829 for (i
= 0; i
< lookup_buffer_depth
; i
++)
1831 struct tempbuf_id_list
*idlist
;
1836 if (lookup
[i
]->idlist
.id
== i
)
1839 idlist
= &lookup
[i
]->idlist
;
1840 while (idlist
->next
!= NULL
)
1841 idlist
= idlist
->next
;
1843 tempbuf_left
-= sizeof(struct tempbuf_id_list
);
1844 if (tempbuf_left
- 4 < 0)
1847 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
1848 if (tempbuf_pos
& 0x03)
1850 tempbuf_pos
= (tempbuf_pos
& ~0x03) + 0x04;
1852 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
1854 tempbuf_pos
+= sizeof(struct tempbuf_id_list
);
1856 idlist
= idlist
->next
;
1858 idlist
->next
= NULL
;
1863 qsort(index
, tempbufidx
, sizeof(struct tempbuf_searchidx
), compare
);
1864 memset(lookup
, 0, lookup_buffer_depth
* sizeof(struct tempbuf_searchidx
**));
1866 for (i
= 0; i
< tempbufidx
; i
++)
1868 struct tempbuf_id_list
*idlist
= &index
[i
].idlist
;
1870 /* Fix the lookup list. */
1871 while (idlist
!= NULL
)
1873 if (idlist
->id
>= 0)
1874 lookup
[idlist
->id
] = &index
[i
];
1875 idlist
= idlist
->next
;
1878 index
[i
].seek
= lseek(fd
, 0, SEEK_CUR
);
1879 length
= strlen(index
[i
].str
) + 1;
1880 fe
.tag_length
= length
;
1881 fe
.idx_id
= index
[i
].idx_id
;
1883 /* Check the chunk alignment. */
1884 if ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
1885 % TAGFILE_ENTRY_CHUNK_LENGTH
)
1887 fe
.tag_length
+= TAGFILE_ENTRY_CHUNK_LENGTH
-
1888 ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
1889 % TAGFILE_ENTRY_CHUNK_LENGTH
);
1892 #ifdef TAGCACHE_STRICT_ALIGN
1893 /* Make sure the entry is long aligned. */
1894 if (index
[i
].seek
& 0x03)
1896 logf("tempbuf_sort: alignment error!");
1901 if (ecwrite(fd
, &fe
, 1, tagfile_entry_ec
, tc_stat
.econ
) !=
1902 sizeof(struct tagfile_entry
))
1904 logf("tempbuf_sort: write error #1");
1908 if (write(fd
, index
[i
].str
, length
) != length
)
1910 logf("tempbuf_sort: write error #2");
1914 /* Write some padding. */
1915 if (fe
.tag_length
- length
> 0)
1916 write(fd
, "XXXXXXXX", fe
.tag_length
- length
);
1922 inline static struct tempbuf_searchidx
* tempbuf_locate(int id
)
1924 if (id
< 0 || id
>= lookup_buffer_depth
)
1931 inline static int tempbuf_find_location(int id
)
1933 struct tempbuf_searchidx
*entry
;
1935 entry
= tempbuf_locate(id
);
1942 static bool build_numeric_indices(struct tagcache_header
*h
, int tmpfd
)
1944 struct master_header tcmh
;
1945 struct index_entry idx
;
1948 struct temp_file_entry
*entrybuf
= (struct temp_file_entry
*)tempbuf
;
1950 int entries_processed
= 0;
1953 max_entries
= tempbuf_size
/ sizeof(struct temp_file_entry
) - 1;
1955 logf("Building numeric indices...");
1956 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
1958 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
1961 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
1963 if (masterfd_pos
== filesize(masterfd
))
1965 logf("we can't append!");
1970 while (entries_processed
< h
->entry_count
)
1972 int count
= MIN(h
->entry_count
- entries_processed
, max_entries
);
1974 /* Read in as many entries as possible. */
1975 for (i
= 0; i
< count
; i
++)
1977 /* Read in numeric data. */
1978 if (read(tmpfd
, &entrybuf
[i
], sizeof(struct temp_file_entry
)) !=
1979 sizeof(struct temp_file_entry
))
1981 logf("read fail #1");
1986 /* Skip string data. */
1987 lseek(tmpfd
, entrybuf
[i
].data_length
, SEEK_CUR
);
1990 /* Commit the data to the index. */
1991 for (i
= 0; i
< count
; i
++)
1993 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
1995 if (ecread(masterfd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
1996 != sizeof(struct index_entry
))
1998 logf("read fail #2");
2003 for (j
= 0; j
< TAG_COUNT
; j
++)
2005 if (!tagcache_is_numeric_tag(j
))
2008 idx
.tag_seek
[j
] = entrybuf
[i
].tag_offset
[j
];
2010 idx
.flag
= entrybuf
[i
].flag
;
2012 if (tc_stat
.ready
&& current_tcmh
.commitid
> 0)
2014 idx
.tag_seek
[tag_commitid
] = current_tcmh
.commitid
;
2015 idx
.flag
|= FLAG_DIRTYNUM
;
2018 /* Write back the updated index. */
2019 lseek(masterfd
, loc
, SEEK_SET
);
2020 if (ecwrite(masterfd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
2021 != sizeof(struct index_entry
))
2029 entries_processed
+= count
;
2030 logf("%d/%ld entries processed", entries_processed
, h
->entry_count
);
2041 * == 0 temporary failure
2044 static int build_index(int index_type
, struct tagcache_header
*h
, int tmpfd
)
2047 struct tagcache_header tch
;
2048 struct master_header tcmh
;
2049 struct index_entry idxbuf
[IDX_BUF_DEPTH
];
2051 char buf
[TAG_MAXLEN
+32];
2052 int fd
= -1, masterfd
;
2057 logf("Building index: %d", index_type
);
2059 /* Check the number of entries we need to allocate ram for. */
2060 commit_entry_count
= h
->entry_count
+ 1;
2062 masterfd
= open_master_fd(&tcmh
, false);
2065 commit_entry_count
+= tcmh
.tch
.entry_count
;
2069 remove_files(); /* Just to be sure we are clean. */
2071 /* Open the index file, which contains the tag names. */
2072 fd
= open_tag_fd(&tch
, index_type
, true);
2075 logf("tch.datasize=%ld", tch
.datasize
);
2076 lookup_buffer_depth
= 1 +
2077 /* First part */ commit_entry_count
+
2078 /* Second part */ (tch
.datasize
/ TAGFILE_ENTRY_CHUNK_LENGTH
);
2082 lookup_buffer_depth
= 1 +
2083 /* First part */ commit_entry_count
+
2084 /* Second part */ 0;
2087 logf("lookup_buffer_depth=%ld", lookup_buffer_depth
);
2088 logf("commit_entry_count=%ld", commit_entry_count
);
2090 /* Allocate buffer for all index entries from both old and new
2093 tempbuf_pos
= commit_entry_count
* sizeof(struct tempbuf_searchidx
);
2095 /* Allocate lookup buffer. The first portion of commit_entry_count
2096 * contains the new tags in the temporary file and the second
2097 * part for locating entries already in the db.
2100 * +---------+---------------------------+
2101 * | index | position/ENTRY_CHUNK_SIZE | lookup buffer
2102 * +---------+---------------------------+
2104 * Old tags are inserted to a temporary buffer with position:
2105 * tempbuf_insert(position/ENTRY_CHUNK_SIZE, ...);
2106 * And new tags with index:
2107 * tempbuf_insert(idx, ...);
2109 * The buffer is sorted and written into tag file:
2110 * tempbuf_sort(...);
2111 * leaving master index locations messed up.
2113 * That is fixed using the lookup buffer for old tags:
2114 * new_seek = tempbuf_find_location(old_seek, ...);
2116 * new_seek = tempbuf_find_location(idx);
2118 lookup
= (struct tempbuf_searchidx
**)&tempbuf
[tempbuf_pos
];
2119 tempbuf_pos
+= lookup_buffer_depth
* sizeof(void **);
2120 memset(lookup
, 0, lookup_buffer_depth
* sizeof(void **));
2122 /* And calculate the remaining data space used mainly for storing
2123 * tag data (strings). */
2124 tempbuf_left
= tempbuf_size
- tempbuf_pos
- 8;
2125 if (tempbuf_left
- TAGFILE_ENTRY_AVG_LENGTH
* commit_entry_count
< 0)
2127 logf("Buffer way too small!");
2134 * If tag file contains unique tags (sorted index), we will load
2135 * it entirely into memory so we can resort it later for use with
2138 if (tagcache_is_sorted_tag(index_type
))
2140 logf("loading tags...");
2141 for (i
= 0; i
< tch
.entry_count
; i
++)
2143 struct tagfile_entry entry
;
2144 int loc
= lseek(fd
, 0, SEEK_CUR
);
2147 if (ecread(fd
, &entry
, 1, tagfile_entry_ec
, tc_stat
.econ
)
2148 != sizeof(struct tagfile_entry
))
2150 logf("read error #7");
2155 if (entry
.tag_length
>= (int)sizeof(buf
))
2157 logf("too long tag #3");
2162 if (read(fd
, buf
, entry
.tag_length
) != entry
.tag_length
)
2164 logf("read error #8");
2169 /* Skip deleted entries. */
2174 * Save the tag and tag id in the memory buffer. Tag id
2175 * is saved so we can later reindex the master lookup
2176 * table when the index gets resorted.
2178 ret
= tempbuf_insert(buf
, loc
/TAGFILE_ENTRY_CHUNK_LENGTH
2179 + commit_entry_count
, entry
.idx_id
,
2180 tagcache_is_unique_tag(index_type
));
2191 tempbufidx
= tch
.entry_count
;
2196 * Creating new index file to store the tags. No need to preload
2197 * anything whether the index type is sorted or not.
2199 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, index_type
);
2200 fd
= open(buf
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2203 logf("%s open fail", buf
);
2207 tch
.magic
= TAGCACHE_MAGIC
;
2208 tch
.entry_count
= 0;
2211 if (ecwrite(fd
, &tch
, 1, tagcache_header_ec
, tc_stat
.econ
)
2212 != sizeof(struct tagcache_header
))
2214 logf("header write failed");
2220 /* Loading the tag lookup file as "master file". */
2221 logf("Loading index file");
2222 masterfd
= open(TAGCACHE_FILE_MASTER
, O_RDWR
);
2226 logf("Creating new DB");
2227 masterfd
= open(TAGCACHE_FILE_MASTER
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2231 logf("Failure to create index file (%s)", TAGCACHE_FILE_MASTER
);
2236 /* Write the header (write real values later). */
2237 memset(&tcmh
, 0, sizeof(struct master_header
));
2239 tcmh
.tch
.entry_count
= 0;
2240 tcmh
.tch
.datasize
= 0;
2242 ecwrite(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
);
2244 masterfd_pos
= lseek(masterfd
, 0, SEEK_CUR
);
2249 * Master file already exists so we need to process the current
2254 if (ecread(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
) !=
2255 sizeof(struct master_header
) || tcmh
.tch
.magic
!= TAGCACHE_MAGIC
)
2257 logf("header error");
2264 * If we reach end of the master file, we need to expand it to
2265 * hold new tags. If the current index is not sorted, we can
2266 * simply append new data to end of the file.
2267 * However, if the index is sorted, we need to update all tag
2268 * pointers in the master file for the current index.
2270 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
2272 if (masterfd_pos
== filesize(masterfd
))
2274 logf("appending...");
2280 * Load new unique tags in memory to be sorted later and added
2281 * to the master lookup file.
2283 if (tagcache_is_sorted_tag(index_type
))
2285 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2286 /* h is the header of the temporary file containing new tags. */
2287 logf("inserting new tags...");
2288 for (i
= 0; i
< h
->entry_count
; i
++)
2290 struct temp_file_entry entry
;
2292 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2293 sizeof(struct temp_file_entry
))
2295 logf("read fail #3");
2301 if (entry
.tag_length
[index_type
] >= (long)sizeof(buf
))
2303 logf("too long entry!");
2308 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2309 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2310 entry
.tag_length
[index_type
])
2312 logf("read fail #4");
2317 if (tagcache_is_unique_tag(index_type
))
2318 error
= !tempbuf_insert(buf
, i
, -1, true);
2320 error
= !tempbuf_insert(buf
, i
, tcmh
.tch
.entry_count
+ i
, false);
2324 logf("insert error");
2329 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2330 entry
.tag_length
[index_type
], SEEK_CUR
);
2335 /* Sort the buffer data and write it to the index file. */
2336 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
2337 i
= tempbuf_sort(fd
);
2340 logf("sorted %d tags", i
);
2343 * Now update all indexes in the master lookup file.
2345 logf("updating indices...");
2346 lseek(masterfd
, sizeof(struct master_header
), SEEK_SET
);
2347 for (i
= 0; i
< tcmh
.tch
.entry_count
; i
+= idxbuf_pos
)
2350 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2352 idxbuf_pos
= MIN(tcmh
.tch
.entry_count
- i
, IDX_BUF_DEPTH
);
2354 if (ecread(masterfd
, idxbuf
, idxbuf_pos
, index_entry_ec
, tc_stat
.econ
)
2355 != (int)sizeof(struct index_entry
)*idxbuf_pos
)
2357 logf("read fail #5");
2361 lseek(masterfd
, loc
, SEEK_SET
);
2363 for (j
= 0; j
< idxbuf_pos
; j
++)
2365 if (idxbuf
[j
].flag
& FLAG_DELETED
)
2367 /* We can just ignore deleted entries. */
2368 idxbuf
[j
].tag_seek
[index_type
] = 0;
2372 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(
2373 idxbuf
[j
].tag_seek
[index_type
]/TAGFILE_ENTRY_CHUNK_LENGTH
2374 + commit_entry_count
);
2376 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2378 logf("update error: %d/%ld", i
+j
, tcmh
.tch
.entry_count
);
2386 /* Write back the updated index. */
2387 if (ecwrite(masterfd
, idxbuf
, idxbuf_pos
,
2388 index_entry_ec
, tc_stat
.econ
) !=
2389 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2400 * Walk through the temporary file containing the new tags.
2402 // build_normal_index(h, tmpfd, masterfd, idx);
2403 logf("updating new indices...");
2404 lseek(masterfd
, masterfd_pos
, SEEK_SET
);
2405 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2406 lseek(fd
, 0, SEEK_END
);
2407 for (i
= 0; i
< h
->entry_count
; i
+= idxbuf_pos
)
2411 idxbuf_pos
= MIN(h
->entry_count
- i
, IDX_BUF_DEPTH
);
2414 memset(idxbuf
, 0, sizeof(struct index_entry
)*IDX_BUF_DEPTH
);
2418 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2420 if (ecread(masterfd
, idxbuf
, idxbuf_pos
, index_entry_ec
, tc_stat
.econ
)
2421 != (int)sizeof(struct index_entry
)*idxbuf_pos
)
2423 logf("read fail #6");
2427 lseek(masterfd
, loc
, SEEK_SET
);
2430 /* Read entry headers. */
2431 for (j
= 0; j
< idxbuf_pos
; j
++)
2433 if (!tagcache_is_sorted_tag(index_type
))
2435 struct temp_file_entry entry
;
2436 struct tagfile_entry fe
;
2438 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2439 sizeof(struct temp_file_entry
))
2441 logf("read fail #7");
2447 if (entry
.tag_length
[index_type
] >= (int)sizeof(buf
))
2449 logf("too long entry!");
2450 logf("length=%d", entry
.tag_length
[index_type
]);
2451 logf("pos=0x%02lx", lseek(tmpfd
, 0, SEEK_CUR
));
2456 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2457 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2458 entry
.tag_length
[index_type
])
2460 logf("read fail #8");
2461 logf("offset=0x%02lx", entry
.tag_offset
[index_type
]);
2462 logf("length=0x%02x", entry
.tag_length
[index_type
]);
2467 /* Write to index file. */
2468 idxbuf
[j
].tag_seek
[index_type
] = lseek(fd
, 0, SEEK_CUR
);
2469 fe
.tag_length
= entry
.tag_length
[index_type
];
2470 fe
.idx_id
= tcmh
.tch
.entry_count
+ i
+ j
;
2471 ecwrite(fd
, &fe
, 1, tagfile_entry_ec
, tc_stat
.econ
);
2472 write(fd
, buf
, fe
.tag_length
);
2476 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2477 entry
.tag_length
[index_type
], SEEK_CUR
);
2481 /* Locate the correct entry from the sorted array. */
2482 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(i
+ j
);
2483 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2485 logf("entry not found (%d)", j
);
2493 if (ecwrite(masterfd
, idxbuf
, idxbuf_pos
,
2494 index_entry_ec
, tc_stat
.econ
) !=
2495 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2497 logf("tagcache: write fail #4");
2506 /* Finally write the header. */
2507 tch
.magic
= TAGCACHE_MAGIC
;
2508 tch
.entry_count
= tempbufidx
;
2509 tch
.datasize
= lseek(fd
, 0, SEEK_END
) - sizeof(struct tagcache_header
);
2510 lseek(fd
, 0, SEEK_SET
);
2511 ecwrite(fd
, &tch
, 1, tagcache_header_ec
, tc_stat
.econ
);
2513 if (index_type
!= tag_filename
)
2514 h
->datasize
+= tch
.datasize
;
2515 logf("s:%d/%ld/%ld", index_type
, tch
.datasize
, h
->datasize
);
2527 static bool commit(void)
2529 struct tagcache_header tch
;
2530 struct master_header tcmh
;
2534 #ifdef HAVE_DIRCACHE
2535 bool dircache_buffer_stolen
= false;
2537 bool local_allocation
= false;
2539 logf("committing tagcache");
2544 tmpfd
= open(TAGCACHE_FILE_TEMP
, O_RDONLY
);
2547 logf("nothing to commit");
2552 /* Load the header. */
2553 len
= sizeof(struct tagcache_header
);
2554 rc
= read(tmpfd
, &tch
, len
);
2556 if (tch
.magic
!= TAGCACHE_MAGIC
|| rc
!= len
)
2558 logf("incorrect header");
2560 remove(TAGCACHE_FILE_TEMP
);
2564 if (tch
.entry_count
== 0)
2566 logf("nothing to commit");
2568 remove(TAGCACHE_FILE_TEMP
);
2572 #ifdef HAVE_EEPROM_SETTINGS
2573 remove(TAGCACHE_STATEFILE
);
2576 /* At first be sure to unload the ramcache! */
2577 #ifdef HAVE_TC_RAMCACHE
2578 tc_stat
.ramcache
= false;
2583 /* Try to steal every buffer we can :) */
2584 if (tempbuf_size
== 0)
2585 local_allocation
= true;
2587 #ifdef HAVE_DIRCACHE
2588 if (tempbuf_size
== 0)
2590 /* Try to steal the dircache buffer. */
2591 tempbuf
= dircache_steal_buffer(&tempbuf_size
);
2592 tempbuf_size
&= ~0x03;
2594 if (tempbuf_size
> 0)
2596 dircache_buffer_stolen
= true;
2601 #ifdef HAVE_TC_RAMCACHE
2602 if (tempbuf_size
== 0 && tc_stat
.ramcache_allocated
> 0)
2604 tempbuf
= (char *)(hdr
+ 1);
2605 tempbuf_size
= tc_stat
.ramcache_allocated
- sizeof(struct ramcache_header
) - 128;
2606 tempbuf_size
&= ~0x03;
2610 /* And finally fail if there are no buffers available. */
2611 if (tempbuf_size
== 0)
2613 logf("delaying commit until next boot");
2614 tc_stat
.commit_delayed
= true;
2620 logf("commit %ld entries...", tch
.entry_count
);
2622 /* Mark DB dirty so it will stay disabled if commit fails. */
2623 current_tcmh
.dirty
= true;
2624 update_master_header();
2626 /* Now create the index files. */
2627 tc_stat
.commit_step
= 0;
2629 tc_stat
.commit_delayed
= false;
2631 for (i
= 0; i
< TAG_COUNT
; i
++)
2635 if (tagcache_is_numeric_tag(i
))
2638 tc_stat
.commit_step
++;
2639 ret
= build_index(i
, &tch
, tmpfd
);
2643 logf("tagcache failed init");
2647 tc_stat
.commit_delayed
= true;
2648 tc_stat
.commit_step
= 0;
2654 if (!build_numeric_indices(&tch
, tmpfd
))
2656 logf("Failure to commit numeric indices");
2659 tc_stat
.commit_step
= 0;
2665 tc_stat
.commit_step
= 0;
2667 /* Update the master index headers. */
2668 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
2674 tcmh
.tch
.entry_count
+= tch
.entry_count
;
2675 tcmh
.tch
.datasize
= sizeof(struct master_header
)
2676 + sizeof(struct index_entry
) * tcmh
.tch
.entry_count
2681 lseek(masterfd
, 0, SEEK_SET
);
2682 ecwrite(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
);
2685 logf("tagcache committed");
2686 remove(TAGCACHE_FILE_TEMP
);
2687 tc_stat
.ready
= check_all_headers();
2688 tc_stat
.readyvalid
= true;
2690 if (local_allocation
)
2696 #ifdef HAVE_DIRCACHE
2697 /* Rebuild the dircache, if we stole the buffer. */
2698 if (dircache_buffer_stolen
)
2702 #ifdef HAVE_TC_RAMCACHE
2703 /* Reload tagcache. */
2704 if (tc_stat
.ramcache_allocated
> 0)
2705 tagcache_start_scan();
2713 static void allocate_tempbuf(void)
2715 /* Yeah, malloc would be really nice now :) */
2717 tempbuf_size
= 32*1024*1024;
2718 tempbuf
= malloc(tempbuf_size
);
2720 tempbuf
= (char *)(((long)audiobuf
& ~0x03) + 0x04);
2721 tempbuf_size
= (long)audiobufend
- (long)audiobuf
- 4;
2722 audiobuf
+= tempbuf_size
;
2726 static void free_tempbuf(void)
2728 if (tempbuf_size
== 0)
2734 audiobuf
-= tempbuf_size
;
2740 long tagcache_increase_serial(void)
2750 old
= current_tcmh
.serial
++;
2751 if (!update_master_header())
2757 long tagcache_get_serial(void)
2759 return current_tcmh
.serial
;
2762 long tagcache_get_commitid(void)
2764 return current_tcmh
.commitid
;
2767 static bool modify_numeric_entry(int masterfd
, int idx_id
, int tag
, long data
)
2769 struct index_entry idx
;
2774 if (!tagcache_is_numeric_tag(tag
))
2777 if (!get_index(masterfd
, idx_id
, &idx
, false))
2780 idx
.tag_seek
[tag
] = data
;
2781 idx
.flag
|= FLAG_DIRTYNUM
;
2783 return write_index(masterfd
, idx_id
, &idx
);
2786 bool tagcache_modify_numeric_entry(struct tagcache_search
*tcs
,
2789 struct master_header myhdr
;
2791 if (tcs
->masterfd
< 0)
2793 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, true)) < 0)
2797 return modify_numeric_entry(tcs
->masterfd
, tcs
->idx_id
, tag
, data
);
2800 static bool write_tag(int fd
, const char *tagstr
, const char *datastr
)
2805 snprintf(buf
, sizeof buf
, "%s=\"", tagstr
);
2806 for (i
= strlen(buf
); i
< (long)sizeof(buf
)-3; i
++)
2808 if (*datastr
== '\0')
2811 if (*datastr
== '"')
2818 buf
[i
] = *(datastr
++);
2821 strcpy(&buf
[i
], "\" ");
2823 write(fd
, buf
, i
+ 2);
2828 static bool read_tag(char *dest
, long size
,
2829 const char *src
, const char *tagstr
)
2832 char current_tag
[32];
2834 while (*src
!= '\0')
2836 /* Skip all whitespace */
2844 /* Read in tag name */
2845 while (*src
!= '=' && *src
!= ' ')
2847 current_tag
[pos
] = *src
;
2851 if (*src
== '\0' || pos
>= (long)sizeof(current_tag
))
2854 current_tag
[pos
] = '\0';
2856 /* Read in tag data */
2858 /* Find the start. */
2859 while (*src
!= '"' && *src
!= '\0')
2862 if (*src
== '\0' || *(++src
) == '\0')
2865 /* Read the data. */
2866 for (pos
= 0; pos
< size
; pos
++)
2871 if (*src
== '\\' && *(src
+1) == '"')
2893 if (!strcasecmp(tagstr
, current_tag
))
2900 static int parse_changelog_line(int line_n
, const char *buf
, void *parameters
)
2902 struct index_entry idx
;
2903 char tag_data
[TAG_MAXLEN
+32];
2905 long masterfd
= (long)parameters
;
2906 const int import_tags
[] = { tag_playcount
, tag_rating
, tag_playtime
, tag_lastplayed
,
2914 logf("%d/%s", line_n
, buf
);
2915 if (!read_tag(tag_data
, sizeof tag_data
, buf
, "filename"))
2917 logf("filename missing");
2922 idx_id
= find_index(tag_data
);
2925 logf("entry not found");
2929 if (!get_index(masterfd
, idx_id
, &idx
, false))
2931 logf("failed to retrieve index entry");
2935 /* Stop if tag has already been modified. */
2936 if (idx
.flag
& FLAG_DIRTYNUM
)
2939 logf("import: %s", tag_data
);
2941 idx
.flag
|= FLAG_DIRTYNUM
;
2942 for (i
= 0; i
< (long)(sizeof(import_tags
)/sizeof(import_tags
[0])); i
++)
2946 if (!read_tag(tag_data
, sizeof tag_data
, buf
,
2947 tagcache_tag_to_str(import_tags
[i
])))
2952 data
= atoi(tag_data
);
2956 idx
.tag_seek
[import_tags
[i
]] = data
;
2958 if (import_tags
[i
] == tag_lastplayed
&& data
> current_tcmh
.serial
)
2959 current_tcmh
.serial
= data
;
2960 else if (import_tags
[i
] == tag_commitid
&& data
>= current_tcmh
.commitid
)
2961 current_tcmh
.commitid
= data
+ 1;
2964 return write_index(masterfd
, idx_id
, &idx
) ? 0 : -5;
2968 bool tagcache_import_changelog(void)
2970 struct master_header myhdr
;
2971 struct tagcache_header tch
;
2982 clfd
= open(TAGCACHE_FILE_CHANGELOG
, O_RDONLY
);
2985 logf("failure to open changelog");
2989 if ( (masterfd
= open_master_fd(&myhdr
, true)) < 0)
2997 filenametag_fd
= open_tag_fd(&tch
, tag_filename
, false);
2999 fast_readline(clfd
, buf
, sizeof buf
, (long *)masterfd
,
3000 parse_changelog_line
);
3005 if (filenametag_fd
>= 0)
3006 close(filenametag_fd
);
3010 update_master_header();
3016 bool tagcache_create_changelog(struct tagcache_search
*tcs
)
3018 struct master_header myhdr
;
3019 struct index_entry idx
;
3020 char buf
[TAG_MAXLEN
+32];
3028 if (!tagcache_search(tcs
, tag_filename
))
3031 /* Initialize the changelog */
3032 clfd
= open(TAGCACHE_FILE_CHANGELOG
, O_WRONLY
| O_CREAT
| O_TRUNC
);
3035 logf("failure to open changelog");
3039 if (tcs
->masterfd
< 0)
3041 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, false)) < 0)
3046 lseek(tcs
->masterfd
, 0, SEEK_SET
);
3047 ecread(tcs
->masterfd
, &myhdr
, 1, master_header_ec
, tc_stat
.econ
);
3050 write(clfd
, "## Changelog version 1\n", 23);
3052 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
3054 if (ecread(tcs
->masterfd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
3055 != sizeof(struct index_entry
))
3057 logf("read error #9");
3058 tagcache_search_finish(tcs
);
3063 /* Skip until the entry found has been modified. */
3064 if (! (idx
.flag
& FLAG_DIRTYNUM
) )
3067 /* Skip deleted entries too. */
3068 if (idx
.flag
& FLAG_DELETED
)
3071 /* Now retrieve all tags. */
3072 for (j
= 0; j
< TAG_COUNT
; j
++)
3074 if (tagcache_is_numeric_tag(j
))
3076 snprintf(temp
, sizeof temp
, "%d", idx
.tag_seek
[j
]);
3077 write_tag(clfd
, tagcache_tag_to_str(j
), temp
);
3082 tagcache_retrieve(tcs
, i
, tcs
->type
, buf
, sizeof buf
);
3083 write_tag(clfd
, tagcache_tag_to_str(j
), buf
);
3086 write(clfd
, "\n", 1);
3092 tagcache_search_finish(tcs
);
3097 static bool delete_entry(long idx_id
)
3101 struct index_entry idx
, myidx
;
3102 struct master_header myhdr
;
3103 char buf
[TAG_MAXLEN
+32];
3104 int in_use
[TAG_COUNT
];
3106 logf("delete_entry(): %ld", idx_id
);
3108 #ifdef HAVE_TC_RAMCACHE
3109 /* At first mark the entry removed from ram cache. */
3110 if (tc_stat
.ramcache
)
3111 hdr
->indices
[idx_id
].flag
|= FLAG_DELETED
;
3114 if ( (fd
= open_master_fd(&myhdr
, true) ) < 0)
3117 lseek(fd
, idx_id
* sizeof(struct index_entry
), SEEK_CUR
);
3118 if (ecread(fd
, &myidx
, 1, index_entry_ec
, tc_stat
.econ
)
3119 != sizeof(struct index_entry
))
3121 logf("delete_entry(): read error");
3126 myidx
.flag
|= FLAG_DELETED
;
3127 lseek(fd
, -sizeof(struct index_entry
), SEEK_CUR
);
3128 if (ecwrite(fd
, &myidx
, 1, index_entry_ec
, tc_stat
.econ
)
3129 != sizeof(struct index_entry
))
3131 logf("delete_entry(): write_error");
3136 /* Now check which tags are no longer in use (if any) */
3137 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3140 lseek(fd
, sizeof(struct master_header
), SEEK_SET
);
3141 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
3143 struct index_entry
*idxp
;
3145 #ifdef HAVE_TC_RAMCACHE
3146 /* Use RAM DB if available for greater speed */
3147 if (tc_stat
.ramcache
)
3148 idxp
= &hdr
->indices
[i
];
3152 if (ecread(fd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
3153 != sizeof(struct index_entry
))
3155 logf("delete_entry(): read error #2");
3162 if (idxp
->flag
& FLAG_DELETED
)
3165 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3167 if (tagcache_is_numeric_tag(tag
))
3170 if (idxp
->tag_seek
[tag
] == myidx
.tag_seek
[tag
])
3177 /* Now delete all tags no longer in use. */
3178 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3180 if (tagcache_is_numeric_tag(tag
))
3185 logf("in use: %d/%d", tag
, in_use
[tag
]);
3189 #ifdef HAVE_TC_RAMCACHE
3190 /* Delete from ram. */
3191 if (tc_stat
.ramcache
&& tag
!= tag_filename
)
3193 struct tagfile_entry
*tagentry
= get_tag(&myidx
, tag
);
3194 tagentry
->tag_data
[0] = '\0';
3198 /* Open the index file, which contains the tag names. */
3199 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag
);
3200 fd
= open(buf
, O_RDWR
);
3204 logf("open failed");
3208 /* Skip the header block */
3209 lseek(fd
, myidx
.tag_seek
[tag
] + sizeof(struct tagfile_entry
), SEEK_SET
);
3211 /* Debug, print 10 first characters of the tag
3214 logf("TAG:%s", buf);
3215 lseek(fd, -10, SEEK_CUR);
3218 /* Write first data byte in tag as \0 */
3221 /* Now tag data has been removed */
3230 * Returns true if there is an event waiting in the queue
3231 * that requires the current operation to be aborted.
3233 static bool check_event_queue(void)
3237 queue_wait_w_tmo(&tagcache_queue
, &ev
, 0);
3242 case SYS_USB_CONNECTED
:
3243 /* Put the event back into the queue. */
3244 queue_post(&tagcache_queue
, ev
.id
, ev
.data
);
3252 #ifdef HAVE_TC_RAMCACHE
3253 static bool allocate_tagcache(void)
3255 struct master_header tcmh
;
3258 /* Load the header. */
3259 if ( (fd
= open_master_fd(&tcmh
, false)) < 0)
3268 * Now calculate the required cache size plus
3269 * some extra space for alignment fixes.
3271 tc_stat
.ramcache_allocated
= tcmh
.tch
.datasize
+ 128 + TAGCACHE_RESERVE
+
3272 sizeof(struct ramcache_header
) + TAG_COUNT
*sizeof(void *);
3273 hdr
= buffer_alloc(tc_stat
.ramcache_allocated
+ 128);
3274 memset(hdr
, 0, sizeof(struct ramcache_header
));
3275 memcpy(&hdr
->h
, &tcmh
, sizeof(struct master_header
));
3276 hdr
->indices
= (struct index_entry
*)(hdr
+ 1);
3277 logf("tagcache: %d bytes allocated.", tc_stat
.ramcache_allocated
);
3282 # ifdef HAVE_EEPROM_SETTINGS
3283 static bool tagcache_dumpload(void)
3285 struct statefile_header shdr
;
3290 fd
= open(TAGCACHE_STATEFILE
, O_RDONLY
);
3293 logf("no tagcache statedump");
3297 /* Check the statefile memory placement */
3298 hdr
= buffer_alloc(0);
3299 rc
= read(fd
, &shdr
, sizeof(struct statefile_header
));
3300 if (rc
!= sizeof(struct statefile_header
)
3301 /* || (long)hdr != (long)shdr.hdr */)
3303 logf("incorrect statefile");
3309 offpos
= (long)hdr
- (long)shdr
.hdr
;
3311 /* Lets allocate real memory and load it */
3312 hdr
= buffer_alloc(shdr
.tc_stat
.ramcache_allocated
);
3313 rc
= read(fd
, hdr
, shdr
.tc_stat
.ramcache_allocated
);
3316 if (rc
!= shdr
.tc_stat
.ramcache_allocated
)
3318 logf("read failure!");
3323 memcpy(&tc_stat
, &shdr
.tc_stat
, sizeof(struct tagcache_stat
));
3325 /* Now fix the pointers */
3326 hdr
->indices
= (struct index_entry
*)((long)hdr
->indices
+ offpos
);
3327 for (i
= 0; i
< TAG_COUNT
; i
++)
3328 hdr
->tags
[i
] += offpos
;
3333 static bool tagcache_dumpsave(void)
3335 struct statefile_header shdr
;
3338 if (!tc_stat
.ramcache
)
3341 fd
= open(TAGCACHE_STATEFILE
, O_WRONLY
| O_CREAT
| O_TRUNC
);
3344 logf("failed to create a statedump");
3348 /* Create the header */
3350 memcpy(&shdr
.tc_stat
, &tc_stat
, sizeof(struct tagcache_stat
));
3351 write(fd
, &shdr
, sizeof(struct statefile_header
));
3353 /* And dump the data too */
3354 write(fd
, hdr
, tc_stat
.ramcache_allocated
);
3361 static bool load_tagcache(void)
3363 struct tagcache_header
*tch
;
3364 long bytesleft
= tc_stat
.ramcache_allocated
;
3365 struct index_entry
*idx
;
3370 # ifdef HAVE_DIRCACHE
3371 while (dircache_is_initializing())
3375 logf("loading tagcache to ram...");
3377 fd
= open(TAGCACHE_FILE_MASTER
, O_RDONLY
);
3380 logf("tagcache open failed");
3384 if (ecread(fd
, &hdr
->h
, 1, master_header_ec
, tc_stat
.econ
)
3385 != sizeof(struct master_header
)
3386 || hdr
->h
.tch
.magic
!= TAGCACHE_MAGIC
)
3388 logf("incorrect header");
3394 /* Load the master index table. */
3395 for (i
= 0; i
< hdr
->h
.tch
.entry_count
; i
++)
3397 rc
= ecread(fd
, idx
, 1, index_entry_ec
, tc_stat
.econ
);
3398 if (rc
!= sizeof(struct index_entry
))
3400 logf("read error #10");
3405 bytesleft
-= sizeof(struct index_entry
);
3406 if (bytesleft
< 0 || ((long)idx
- (long)hdr
->indices
) >= tc_stat
.ramcache_allocated
)
3408 logf("too big tagcache.");
3418 /* Load the tags. */
3420 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3422 struct tagfile_entry
*fe
;
3423 char buf
[TAG_MAXLEN
+32];
3425 if (tagcache_is_numeric_tag(tag
))
3428 //p = ((void *)p+1);
3429 p
= (char *)((long)p
& ~0x03) + 0x04;
3432 /* Check the header. */
3433 tch
= (struct tagcache_header
*)p
;
3434 p
+= sizeof(struct tagcache_header
);
3436 if ( (fd
= open_tag_fd(tch
, tag
, false)) < 0)
3439 for (hdr
->entry_count
[tag
] = 0;
3440 hdr
->entry_count
[tag
] < tch
->entry_count
;
3441 hdr
->entry_count
[tag
]++)
3445 if (do_timed_yield())
3447 /* Abort if we got a critical event in queue */
3448 if (check_event_queue())
3452 fe
= (struct tagfile_entry
*)p
;
3453 pos
= lseek(fd
, 0, SEEK_CUR
);
3454 rc
= ecread(fd
, fe
, 1, tagfile_entry_ec
, tc_stat
.econ
);
3455 if (rc
!= sizeof(struct tagfile_entry
))
3457 /* End of lookup table. */
3458 logf("read error #11");
3463 /* We have a special handling for the filename tags. */
3464 if (tag
== tag_filename
)
3466 # ifdef HAVE_DIRCACHE
3467 const struct dircache_entry
*dc
;
3470 // FIXME: This is wrong!
3471 // idx = &hdr->indices[hdr->entry_count[i]];
3472 idx
= &hdr
->indices
[fe
->idx_id
];
3474 if (fe
->tag_length
>= (long)sizeof(buf
)-1)
3478 logf("TAG:%s", buf
);
3479 logf("too long filename");
3484 rc
= read(fd
, buf
, fe
->tag_length
);
3485 if (rc
!= fe
->tag_length
)
3487 logf("read error #12");
3492 /* Check if the entry has already been removed */
3493 if (idx
->flag
& FLAG_DELETED
)
3496 /* This flag must not be used yet. */
3497 if (idx
->flag
& FLAG_DIRCACHE
)
3499 logf("internal error!");
3504 if (idx
->tag_seek
[tag
] != pos
)
3506 logf("corrupt data structures!");
3511 # ifdef HAVE_DIRCACHE
3512 if (dircache_is_enabled())
3514 dc
= dircache_get_entry_ptr(buf
);
3517 logf("Entry no longer valid.");
3519 delete_entry(fe
->idx_id
);
3523 idx
->flag
|= FLAG_DIRCACHE
;
3524 FLAG_SET_ATTR(idx
->flag
, fe
->idx_id
);
3525 idx
->tag_seek
[tag_filename
] = (long)dc
;
3531 # if 0 /* Maybe we could enable this for flash players. Too slow otherwise. */
3532 /* Check if entry has been removed. */
3533 if (global_settings
.tagcache_autoupdate
)
3537 testfd
= open(buf
, O_RDONLY
);
3540 logf("Entry no longer valid.");
3542 delete_entry(fe
->idx_id
);
3553 bytesleft
-= sizeof(struct tagfile_entry
) + fe
->tag_length
;
3556 logf("too big tagcache #2");
3557 logf("tl: %d", fe
->tag_length
);
3558 logf("bl: %ld", bytesleft
);
3564 rc
= read(fd
, fe
->tag_data
, fe
->tag_length
);
3567 if (rc
!= fe
->tag_length
)
3569 logf("read error #13");
3570 logf("rc=0x%04x", rc
); // 0x431
3571 logf("len=0x%04x", fe
->tag_length
); // 0x4000
3572 logf("pos=0x%04lx", lseek(fd
, 0, SEEK_CUR
)); // 0x433
3573 logf("tag=0x%02x", tag
); // 0x00
3581 tc_stat
.ramcache_used
= tc_stat
.ramcache_allocated
- bytesleft
;
3582 logf("tagcache loaded into ram!");
3586 #endif /* HAVE_TC_RAMCACHE */
3588 static bool check_deleted_files(void)
3591 char buf
[TAG_MAXLEN
+32];
3592 struct tagfile_entry tfe
;
3594 logf("reverse scan...");
3595 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag_filename
);
3596 fd
= open(buf
, O_RDONLY
);
3600 logf("%s open fail", buf
);
3604 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
3605 while (ecread(fd
, &tfe
, 1, tagfile_entry_ec
, tc_stat
.econ
)
3606 == sizeof(struct tagfile_entry
)
3608 && !check_event_queue()
3612 if (tfe
.tag_length
>= (long)sizeof(buf
)-1)
3614 logf("too long tag");
3619 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
3621 logf("read error #14");
3626 /* Check if the file has already deleted from the db. */
3630 /* Now check if the file exists. */
3631 testfd
= open(buf
, O_RDONLY
);
3634 logf("Entry no longer valid.");
3635 logf("-> %s / %d", buf
, tfe
.tag_length
);
3636 delete_entry(tfe
.idx_id
);
3648 static bool check_dir(const char *dirname
)
3652 int success
= false;
3654 dir
= opendir_cached(dirname
);
3657 logf("tagcache: opendir_cached() failed");
3661 /* Recursively scan the dir. */
3665 while (!check_event_queue())
3668 struct dircache_entry
*entry
;
3670 entry
= readdir_cached(dir
);
3678 if (!strcmp((char *)entry
->d_name
, ".") ||
3679 !strcmp((char *)entry
->d_name
, ".."))
3684 len
= strlen(curpath
);
3685 snprintf(&curpath
[len
], curpath_size
- len
, "/%s",
3688 processed_dir_count
++;
3689 if (entry
->attribute
& ATTR_DIRECTORY
)
3692 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
3693 add_tagcache(curpath
, dir
->internal_entry
);
3695 add_tagcache(curpath
);
3698 curpath
[len
] = '\0';
3701 closedir_cached(dir
);
3706 void build_tagcache(const char *path
)
3708 struct tagcache_header header
;
3713 total_entry_count
= 0;
3714 processed_dir_count
= 0;
3716 #ifdef HAVE_DIRCACHE
3717 while (dircache_is_initializing())
3721 logf("updating tagcache");
3723 cachefd
= open(TAGCACHE_FILE_TEMP
, O_RDONLY
);
3726 logf("skipping, cache already waiting for commit");
3731 cachefd
= open(TAGCACHE_FILE_TEMP
, O_RDWR
| O_CREAT
| O_TRUNC
);
3734 logf("master file open failed: %s", TAGCACHE_FILE_TEMP
);
3738 filenametag_fd
= open_tag_fd(&header
, tag_filename
, false);
3742 logf("Scanning files...");
3743 /* Scan for new files. */
3744 memset(&header
, 0, sizeof(struct tagcache_header
));
3745 write(cachefd
, &header
, sizeof(struct tagcache_header
));
3747 if (strcmp("/", path
) != 0)
3748 strcpy(curpath
, path
);
3749 ret
= check_dir(path
);
3751 /* Write the header. */
3752 header
.magic
= TAGCACHE_MAGIC
;
3753 header
.datasize
= data_size
;
3754 header
.entry_count
= total_entry_count
;
3755 lseek(cachefd
, 0, SEEK_SET
);
3756 write(cachefd
, &header
, sizeof(struct tagcache_header
));
3759 if (filenametag_fd
>= 0)
3761 close(filenametag_fd
);
3762 filenametag_fd
= -1;
3772 /* Commit changes to the database. */
3778 remove(TAGCACHE_FILE_TEMP
);
3779 logf("tagcache built!");
3785 #ifdef HAVE_TC_RAMCACHE
3788 /* Import runtime statistics if we just initialized the db. */
3789 if (hdr
->h
.serial
== 0)
3790 queue_post(&tagcache_queue
, Q_IMPORT_CHANGELOG
, 0);
3797 #ifdef HAVE_TC_RAMCACHE
3798 static void load_ramcache(void)
3805 /* At first we should load the cache (if exists). */
3806 tc_stat
.ramcache
= load_tagcache();
3808 if (!tc_stat
.ramcache
)
3810 /* If loading failed, it must indicate some problem with the db
3811 * so disable it entirely to prevent further issues. */
3812 tc_stat
.ready
= false;
3819 void tagcache_unload_ramcache(void)
3821 tc_stat
.ramcache
= false;
3822 /* Just to make sure there is no statefile present. */
3823 // remove(TAGCACHE_STATEFILE);
3828 static void tagcache_thread(void)
3831 bool check_done
= false;
3833 /* If the previous cache build/update was interrupted, commit
3834 * the changes first in foreground. */
3840 #ifdef HAVE_TC_RAMCACHE
3841 # ifdef HAVE_EEPROM_SETTINGS
3842 if (firmware_settings
.initialized
&& firmware_settings
.disk_clean
)
3843 check_done
= tagcache_dumpload();
3845 remove(TAGCACHE_STATEFILE
);
3848 /* Allocate space for the tagcache if found on disk. */
3849 if (global_settings
.tagcache_ram
&& !tc_stat
.ramcache
)
3850 allocate_tagcache();
3854 tc_stat
.initialized
= true;
3856 /* Don't delay bootup with the header check but do it on background. */
3858 tc_stat
.ready
= check_all_headers();
3859 tc_stat
.readyvalid
= true;
3863 queue_wait_w_tmo(&tagcache_queue
, &ev
, HZ
);
3867 case Q_IMPORT_CHANGELOG
:
3868 tagcache_import_changelog();
3873 build_tagcache("/");
3877 build_tagcache("/");
3878 #ifdef HAVE_TC_RAMCACHE
3881 check_deleted_files();
3887 if (check_done
|| !tc_stat
.ready
)
3890 #ifdef HAVE_TC_RAMCACHE
3891 if (!tc_stat
.ramcache
&& global_settings
.tagcache_ram
)
3894 if (tc_stat
.ramcache
&& global_settings
.tagcache_autoupdate
)
3895 build_tagcache("/");
3899 if (global_settings
.tagcache_autoupdate
)
3901 build_tagcache("/");
3902 /* Don't do auto removal without dircache (very slow). */
3903 #ifdef HAVE_DIRCACHE
3904 if (dircache_is_enabled())
3905 check_deleted_files();
3909 logf("tagcache check done");
3921 case SYS_USB_CONNECTED
:
3922 logf("USB: TagCache");
3923 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
3924 usb_wait_for_disconnect(&tagcache_queue
);
3931 bool tagcache_prepare_shutdown(void)
3933 if (tagcache_get_commit_step() > 0)
3936 tagcache_stop_scan();
3937 while (read_lock
|| write_lock
)
3943 void tagcache_shutdown(void)
3945 #ifdef HAVE_EEPROM_SETTINGS
3946 if (tc_stat
.ramcache
)
3947 tagcache_dumpsave();
3951 static int get_progress(void)
3953 int total_count
= -1;
3955 #ifdef HAVE_DIRCACHE
3956 if (dircache_is_enabled())
3958 total_count
= dircache_get_entry_count();
3962 #ifdef HAVE_TC_RAMCACHE
3964 if (hdr
&& tc_stat
.ramcache
)
3965 total_count
= hdr
->h
.tch
.entry_count
;
3969 if (total_count
< 0)
3972 return processed_dir_count
* 100 / total_count
;
3975 struct tagcache_stat
* tagcache_get_stat(void)
3977 tc_stat
.progress
= get_progress();
3978 tc_stat
.processed_entries
= processed_dir_count
;
3983 void tagcache_start_scan(void)
3985 queue_post(&tagcache_queue
, Q_START_SCAN
, 0);
3988 bool tagcache_update(void)
3993 queue_post(&tagcache_queue
, Q_UPDATE
, 0);
3997 bool tagcache_rebuild()
3999 queue_post(&tagcache_queue
, Q_REBUILD
, 0);
4003 void tagcache_stop_scan(void)
4005 queue_post(&tagcache_queue
, Q_STOP_SCAN
, 0);
4008 #ifdef HAVE_TC_RAMCACHE
4009 bool tagcache_is_ramcache(void)
4011 return tc_stat
.ramcache
;
4015 #endif /* !__PCTOOL__ */
4018 void tagcache_init(void)
4020 memset(&tc_stat
, 0, sizeof(struct tagcache_stat
));
4021 memset(¤t_tcmh
, 0, sizeof(struct master_header
));
4022 filenametag_fd
= -1;
4023 write_lock
= read_lock
= 0;
4026 queue_init(&tagcache_queue
, true);
4027 create_thread(tagcache_thread
, tagcache_stack
,
4028 sizeof(tagcache_stack
), tagcache_thread_name
4029 IF_PRIO(, PRIORITY_BACKGROUND
)
4030 IF_COP(, CPU
, false));
4032 tc_stat
.initialized
= true;
4036 tc_stat
.ready
= check_all_headers();
4041 void tagcache_reverse_scan(void)
4043 logf("Checking for deleted files");
4044 check_deleted_files();
4048 bool tagcache_is_initialized(void)
4050 return tc_stat
.initialized
;
4052 bool tagcache_is_usable(void)
4054 return tc_stat
.initialized
&& tc_stat
.ready
;
4056 int tagcache_get_commit_step(void)
4058 return tc_stat
.commit_step
;
4060 int tagcache_get_max_commit_step(void)
4062 return (int)(sizeof(sorted_tags
)/sizeof(sorted_tags
[0]))+1;