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
);
1602 /* Check for overlength file path. */
1603 if (path_length
> TAG_MAXLEN
)
1605 /* Path can't be shortened. */
1606 logf("Too long path: %s", path
);
1610 /* Check if the file is supported. */
1611 if (probe_file_format(path
) == AFMT_UNKNOWN
)
1614 /* Check if the file is already cached. */
1615 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
1616 if (tc_stat
.ramcache
&& dircache_is_enabled())
1618 if (find_entry_ram(path
, dc
) >= 0)
1624 if (filenametag_fd
>= 0)
1626 if (find_entry_disk(path
) >= 0)
1631 fd
= open(path
, O_RDONLY
);
1634 logf("open fail: %s", path
);
1638 memset(&track
, 0, sizeof(struct track_info
));
1639 memset(&entry
, 0, sizeof(struct temp_file_entry
));
1640 memset(&tracknumfix
, 0, sizeof(tracknumfix
));
1641 ret
= get_metadata(&track
, fd
, path
, false);
1647 logf("-> %s", path
);
1649 /* Generate track number if missing. */
1650 if (track
.id3
.tracknum
<= 0)
1652 const char *p
= strrchr(path
, '.');
1655 p
= &path
[strlen(path
)-1];
1659 if (isdigit(*p
) && isdigit(*(p
-1)))
1661 tracknumfix
[1] = *p
--;
1662 tracknumfix
[0] = *p
;
1668 if (tracknumfix
[0] != '\0')
1670 track
.id3
.tracknum
= atoi(tracknumfix
);
1671 /* Set a flag to indicate track number has been generated. */
1672 entry
.flag
|= FLAG_TRKNUMGEN
;
1676 /* Unable to generate track number. */
1677 track
.id3
.tracknum
= -1;
1682 entry
.tag_offset
[tag_year
] = track
.id3
.year
;
1683 entry
.tag_offset
[tag_tracknumber
] = track
.id3
.tracknum
;
1684 entry
.tag_offset
[tag_length
] = track
.id3
.length
;
1685 entry
.tag_offset
[tag_bitrate
] = track
.id3
.bitrate
;
1688 ADD_TAG(entry
, tag_filename
, &path
);
1689 ADD_TAG(entry
, tag_title
, &track
.id3
.title
);
1690 ADD_TAG(entry
, tag_artist
, &track
.id3
.artist
);
1691 ADD_TAG(entry
, tag_album
, &track
.id3
.album
);
1692 ADD_TAG(entry
, tag_genre
, &track
.id3
.genre_string
);
1693 ADD_TAG(entry
, tag_composer
, &track
.id3
.composer
);
1694 ADD_TAG(entry
, tag_comment
, &track
.id3
.comment
);
1695 ADD_TAG(entry
, tag_albumartist
, &track
.id3
.albumartist
);
1696 entry
.data_length
= offset
;
1698 /* Write the header */
1699 write(cachefd
, &entry
, sizeof(struct temp_file_entry
));
1701 /* And tags also... Correct order is critical */
1703 write_item(track
.id3
.title
);
1704 write_item(track
.id3
.artist
);
1705 write_item(track
.id3
.album
);
1706 write_item(track
.id3
.genre_string
);
1707 write_item(track
.id3
.composer
);
1708 write_item(track
.id3
.comment
);
1709 write_item(track
.id3
.albumartist
);
1710 total_entry_count
++;
1713 static bool tempbuf_insert(char *str
, int id
, int idx_id
, bool unique
)
1715 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1716 int len
= strlen(str
)+1;
1719 unsigned *crcbuf
= (unsigned *)&tempbuf
[tempbuf_size
-4];
1720 char buf
[TAG_MAXLEN
+32];
1722 for (i
= 0; str
[i
] != '\0' && i
< (int)sizeof(buf
)-1; i
++)
1723 buf
[i
] = tolower(str
[i
]);
1726 crc32
= crc_32(buf
, i
, 0xffffffff);
1730 /* Check if the crc does not exist -> entry does not exist for sure. */
1731 for (i
= 0; i
< tempbufidx
; i
++)
1733 if (crcbuf
[-i
] != crc32
)
1736 if (!strcasecmp(str
, index
[i
].str
))
1738 if (id
< 0 || id
>= lookup_buffer_depth
)
1740 logf("lookup buf overf.: %d", id
);
1744 lookup
[id
] = &index
[i
];
1750 /* Insert to CRC buffer. */
1751 crcbuf
[-tempbufidx
] = crc32
;
1754 /* Insert it to the buffer. */
1755 tempbuf_left
-= len
;
1756 if (tempbuf_left
- 4 < 0 || tempbufidx
>= commit_entry_count
-1)
1759 if (id
>= lookup_buffer_depth
)
1761 logf("lookup buf overf. #2: %d", id
);
1767 lookup
[id
] = &index
[tempbufidx
];
1768 index
[tempbufidx
].idlist
.id
= id
;
1771 index
[tempbufidx
].idlist
.id
= -1;
1773 index
[tempbufidx
].idlist
.next
= NULL
;
1774 index
[tempbufidx
].idx_id
= idx_id
;
1775 index
[tempbufidx
].seek
= -1;
1776 index
[tempbufidx
].str
= &tempbuf
[tempbuf_pos
];
1777 memcpy(index
[tempbufidx
].str
, str
, len
);
1784 static int compare(const void *p1
, const void *p2
)
1788 struct tempbuf_searchidx
*e1
= (struct tempbuf_searchidx
*)p1
;
1789 struct tempbuf_searchidx
*e2
= (struct tempbuf_searchidx
*)p2
;
1791 if (strcmp(e1
->str
, UNTAGGED
) == 0)
1793 if (strcmp(e2
->str
, UNTAGGED
) == 0)
1797 else if (strcmp(e2
->str
, UNTAGGED
) == 0)
1800 return strncasecmp(e1
->str
, e2
->str
, TAG_MAXLEN
);
1803 static int tempbuf_sort(int fd
)
1805 struct tempbuf_searchidx
*index
= (struct tempbuf_searchidx
*)tempbuf
;
1806 struct tagfile_entry fe
;
1810 /* Generate reverse lookup entries. */
1811 for (i
= 0; i
< lookup_buffer_depth
; i
++)
1813 struct tempbuf_id_list
*idlist
;
1818 if (lookup
[i
]->idlist
.id
== i
)
1821 idlist
= &lookup
[i
]->idlist
;
1822 while (idlist
->next
!= NULL
)
1823 idlist
= idlist
->next
;
1825 tempbuf_left
-= sizeof(struct tempbuf_id_list
);
1826 if (tempbuf_left
- 4 < 0)
1829 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
1830 if (tempbuf_pos
& 0x03)
1832 tempbuf_pos
= (tempbuf_pos
& ~0x03) + 0x04;
1834 idlist
->next
= (struct tempbuf_id_list
*)&tempbuf
[tempbuf_pos
];
1836 tempbuf_pos
+= sizeof(struct tempbuf_id_list
);
1838 idlist
= idlist
->next
;
1840 idlist
->next
= NULL
;
1845 qsort(index
, tempbufidx
, sizeof(struct tempbuf_searchidx
), compare
);
1846 memset(lookup
, 0, lookup_buffer_depth
* sizeof(struct tempbuf_searchidx
**));
1848 for (i
= 0; i
< tempbufidx
; i
++)
1850 struct tempbuf_id_list
*idlist
= &index
[i
].idlist
;
1852 /* Fix the lookup list. */
1853 while (idlist
!= NULL
)
1855 if (idlist
->id
>= 0)
1856 lookup
[idlist
->id
] = &index
[i
];
1857 idlist
= idlist
->next
;
1860 index
[i
].seek
= lseek(fd
, 0, SEEK_CUR
);
1861 length
= strlen(index
[i
].str
) + 1;
1862 fe
.tag_length
= length
;
1863 fe
.idx_id
= index
[i
].idx_id
;
1865 /* Check the chunk alignment. */
1866 if ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
1867 % TAGFILE_ENTRY_CHUNK_LENGTH
)
1869 fe
.tag_length
+= TAGFILE_ENTRY_CHUNK_LENGTH
-
1870 ((fe
.tag_length
+ sizeof(struct tagfile_entry
))
1871 % TAGFILE_ENTRY_CHUNK_LENGTH
);
1874 #ifdef TAGCACHE_STRICT_ALIGN
1875 /* Make sure the entry is long aligned. */
1876 if (index
[i
].seek
& 0x03)
1878 logf("tempbuf_sort: alignment error!");
1883 if (ecwrite(fd
, &fe
, 1, tagfile_entry_ec
, tc_stat
.econ
) !=
1884 sizeof(struct tagfile_entry
))
1886 logf("tempbuf_sort: write error #1");
1890 if (write(fd
, index
[i
].str
, length
) != length
)
1892 logf("tempbuf_sort: write error #2");
1896 /* Write some padding. */
1897 if (fe
.tag_length
- length
> 0)
1898 write(fd
, "XXXXXXXX", fe
.tag_length
- length
);
1904 inline static struct tempbuf_searchidx
* tempbuf_locate(int id
)
1906 if (id
< 0 || id
>= lookup_buffer_depth
)
1913 inline static int tempbuf_find_location(int id
)
1915 struct tempbuf_searchidx
*entry
;
1917 entry
= tempbuf_locate(id
);
1924 static bool build_numeric_indices(struct tagcache_header
*h
, int tmpfd
)
1926 struct master_header tcmh
;
1927 struct index_entry idx
;
1930 struct temp_file_entry
*entrybuf
= (struct temp_file_entry
*)tempbuf
;
1932 int entries_processed
= 0;
1935 max_entries
= tempbuf_size
/ sizeof(struct temp_file_entry
) - 1;
1937 logf("Building numeric indices...");
1938 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
1940 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
1943 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
1945 if (masterfd_pos
== filesize(masterfd
))
1947 logf("we can't append!");
1952 while (entries_processed
< h
->entry_count
)
1954 int count
= MIN(h
->entry_count
- entries_processed
, max_entries
);
1956 /* Read in as many entries as possible. */
1957 for (i
= 0; i
< count
; i
++)
1959 /* Read in numeric data. */
1960 if (read(tmpfd
, &entrybuf
[i
], sizeof(struct temp_file_entry
)) !=
1961 sizeof(struct temp_file_entry
))
1963 logf("read fail #1");
1968 /* Skip string data. */
1969 lseek(tmpfd
, entrybuf
[i
].data_length
, SEEK_CUR
);
1972 /* Commit the data to the index. */
1973 for (i
= 0; i
< count
; i
++)
1975 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
1977 if (ecread(masterfd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
1978 != sizeof(struct index_entry
))
1980 logf("read fail #2");
1985 for (j
= 0; j
< TAG_COUNT
; j
++)
1987 if (!tagcache_is_numeric_tag(j
))
1990 idx
.tag_seek
[j
] = entrybuf
[i
].tag_offset
[j
];
1992 idx
.flag
= entrybuf
[i
].flag
;
1994 if (tc_stat
.ready
&& current_tcmh
.commitid
> 0)
1996 idx
.tag_seek
[tag_commitid
] = current_tcmh
.commitid
;
1997 idx
.flag
|= FLAG_DIRTYNUM
;
2000 /* Write back the updated index. */
2001 lseek(masterfd
, loc
, SEEK_SET
);
2002 if (ecwrite(masterfd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
2003 != sizeof(struct index_entry
))
2011 entries_processed
+= count
;
2012 logf("%d/%ld entries processed", entries_processed
, h
->entry_count
);
2023 * == 0 temporary failure
2026 static int build_index(int index_type
, struct tagcache_header
*h
, int tmpfd
)
2029 struct tagcache_header tch
;
2030 struct master_header tcmh
;
2031 struct index_entry idxbuf
[IDX_BUF_DEPTH
];
2033 char buf
[TAG_MAXLEN
+32];
2034 int fd
= -1, masterfd
;
2039 logf("Building index: %d", index_type
);
2041 /* Check the number of entries we need to allocate ram for. */
2042 commit_entry_count
= h
->entry_count
+ 1;
2044 masterfd
= open_master_fd(&tcmh
, false);
2047 commit_entry_count
+= tcmh
.tch
.entry_count
;
2051 remove_files(); /* Just to be sure we are clean. */
2053 /* Open the index file, which contains the tag names. */
2054 fd
= open_tag_fd(&tch
, index_type
, true);
2057 logf("tch.datasize=%ld", tch
.datasize
);
2058 lookup_buffer_depth
= 1 +
2059 /* First part */ commit_entry_count
+
2060 /* Second part */ (tch
.datasize
/ TAGFILE_ENTRY_CHUNK_LENGTH
);
2064 lookup_buffer_depth
= 1 +
2065 /* First part */ commit_entry_count
+
2066 /* Second part */ 0;
2069 logf("lookup_buffer_depth=%ld", lookup_buffer_depth
);
2070 logf("commit_entry_count=%ld", commit_entry_count
);
2072 /* Allocate buffer for all index entries from both old and new
2075 tempbuf_pos
= commit_entry_count
* sizeof(struct tempbuf_searchidx
);
2077 /* Allocate lookup buffer. The first portion of commit_entry_count
2078 * contains the new tags in the temporary file and the second
2079 * part for locating entries already in the db.
2082 * +---------+---------------------------+
2083 * | index | position/ENTRY_CHUNK_SIZE | lookup buffer
2084 * +---------+---------------------------+
2086 * Old tags are inserted to a temporary buffer with position:
2087 * tempbuf_insert(position/ENTRY_CHUNK_SIZE, ...);
2088 * And new tags with index:
2089 * tempbuf_insert(idx, ...);
2091 * The buffer is sorted and written into tag file:
2092 * tempbuf_sort(...);
2093 * leaving master index locations messed up.
2095 * That is fixed using the lookup buffer for old tags:
2096 * new_seek = tempbuf_find_location(old_seek, ...);
2098 * new_seek = tempbuf_find_location(idx);
2100 lookup
= (struct tempbuf_searchidx
**)&tempbuf
[tempbuf_pos
];
2101 tempbuf_pos
+= lookup_buffer_depth
* sizeof(void **);
2102 memset(lookup
, 0, lookup_buffer_depth
* sizeof(void **));
2104 /* And calculate the remaining data space used mainly for storing
2105 * tag data (strings). */
2106 tempbuf_left
= tempbuf_size
- tempbuf_pos
- 8;
2107 if (tempbuf_left
- TAGFILE_ENTRY_AVG_LENGTH
* commit_entry_count
< 0)
2109 logf("Buffer way too small!");
2116 * If tag file contains unique tags (sorted index), we will load
2117 * it entirely into memory so we can resort it later for use with
2120 if (tagcache_is_sorted_tag(index_type
))
2122 logf("loading tags...");
2123 for (i
= 0; i
< tch
.entry_count
; i
++)
2125 struct tagfile_entry entry
;
2126 int loc
= lseek(fd
, 0, SEEK_CUR
);
2129 if (ecread(fd
, &entry
, 1, tagfile_entry_ec
, tc_stat
.econ
)
2130 != sizeof(struct tagfile_entry
))
2132 logf("read error #7");
2137 if (entry
.tag_length
>= (int)sizeof(buf
))
2139 logf("too long tag #3");
2144 if (read(fd
, buf
, entry
.tag_length
) != entry
.tag_length
)
2146 logf("read error #8");
2151 /* Skip deleted entries. */
2156 * Save the tag and tag id in the memory buffer. Tag id
2157 * is saved so we can later reindex the master lookup
2158 * table when the index gets resorted.
2160 ret
= tempbuf_insert(buf
, loc
/TAGFILE_ENTRY_CHUNK_LENGTH
2161 + commit_entry_count
, entry
.idx_id
,
2162 tagcache_is_unique_tag(index_type
));
2173 tempbufidx
= tch
.entry_count
;
2178 * Creating new index file to store the tags. No need to preload
2179 * anything whether the index type is sorted or not.
2181 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, index_type
);
2182 fd
= open(buf
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2185 logf("%s open fail", buf
);
2189 tch
.magic
= TAGCACHE_MAGIC
;
2190 tch
.entry_count
= 0;
2193 if (ecwrite(fd
, &tch
, 1, tagcache_header_ec
, tc_stat
.econ
)
2194 != sizeof(struct tagcache_header
))
2196 logf("header write failed");
2202 /* Loading the tag lookup file as "master file". */
2203 logf("Loading index file");
2204 masterfd
= open(TAGCACHE_FILE_MASTER
, O_RDWR
);
2208 logf("Creating new DB");
2209 masterfd
= open(TAGCACHE_FILE_MASTER
, O_WRONLY
| O_CREAT
| O_TRUNC
);
2213 logf("Failure to create index file (%s)", TAGCACHE_FILE_MASTER
);
2218 /* Write the header (write real values later). */
2219 memset(&tcmh
, 0, sizeof(struct master_header
));
2221 tcmh
.tch
.entry_count
= 0;
2222 tcmh
.tch
.datasize
= 0;
2224 ecwrite(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
);
2226 masterfd_pos
= lseek(masterfd
, 0, SEEK_CUR
);
2231 * Master file already exists so we need to process the current
2236 if (ecread(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
) !=
2237 sizeof(struct master_header
) || tcmh
.tch
.magic
!= TAGCACHE_MAGIC
)
2239 logf("header error");
2246 * If we reach end of the master file, we need to expand it to
2247 * hold new tags. If the current index is not sorted, we can
2248 * simply append new data to end of the file.
2249 * However, if the index is sorted, we need to update all tag
2250 * pointers in the master file for the current index.
2252 masterfd_pos
= lseek(masterfd
, tcmh
.tch
.entry_count
* sizeof(struct index_entry
),
2254 if (masterfd_pos
== filesize(masterfd
))
2256 logf("appending...");
2262 * Load new unique tags in memory to be sorted later and added
2263 * to the master lookup file.
2265 if (tagcache_is_sorted_tag(index_type
))
2267 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2268 /* h is the header of the temporary file containing new tags. */
2269 logf("inserting new tags...");
2270 for (i
= 0; i
< h
->entry_count
; i
++)
2272 struct temp_file_entry entry
;
2274 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2275 sizeof(struct temp_file_entry
))
2277 logf("read fail #3");
2283 if (entry
.tag_length
[index_type
] >= (long)sizeof(buf
))
2285 logf("too long entry!");
2290 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2291 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2292 entry
.tag_length
[index_type
])
2294 logf("read fail #4");
2299 if (tagcache_is_unique_tag(index_type
))
2300 error
= !tempbuf_insert(buf
, i
, -1, true);
2302 error
= !tempbuf_insert(buf
, i
, tcmh
.tch
.entry_count
+ i
, false);
2306 logf("insert error");
2311 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2312 entry
.tag_length
[index_type
], SEEK_CUR
);
2317 /* Sort the buffer data and write it to the index file. */
2318 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
2319 i
= tempbuf_sort(fd
);
2322 logf("sorted %d tags", i
);
2325 * Now update all indexes in the master lookup file.
2327 logf("updating indices...");
2328 lseek(masterfd
, sizeof(struct master_header
), SEEK_SET
);
2329 for (i
= 0; i
< tcmh
.tch
.entry_count
; i
+= idxbuf_pos
)
2332 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2334 idxbuf_pos
= MIN(tcmh
.tch
.entry_count
- i
, IDX_BUF_DEPTH
);
2336 if (ecread(masterfd
, idxbuf
, idxbuf_pos
, index_entry_ec
, tc_stat
.econ
)
2337 != (int)sizeof(struct index_entry
)*idxbuf_pos
)
2339 logf("read fail #5");
2343 lseek(masterfd
, loc
, SEEK_SET
);
2345 for (j
= 0; j
< idxbuf_pos
; j
++)
2347 if (idxbuf
[j
].flag
& FLAG_DELETED
)
2349 /* We can just ignore deleted entries. */
2350 idxbuf
[j
].tag_seek
[index_type
] = 0;
2354 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(
2355 idxbuf
[j
].tag_seek
[index_type
]/TAGFILE_ENTRY_CHUNK_LENGTH
2356 + commit_entry_count
);
2358 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2360 logf("update error: %d/%ld", i
+j
, tcmh
.tch
.entry_count
);
2368 /* Write back the updated index. */
2369 if (ecwrite(masterfd
, idxbuf
, idxbuf_pos
,
2370 index_entry_ec
, tc_stat
.econ
) !=
2371 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2382 * Walk through the temporary file containing the new tags.
2384 // build_normal_index(h, tmpfd, masterfd, idx);
2385 logf("updating new indices...");
2386 lseek(masterfd
, masterfd_pos
, SEEK_SET
);
2387 lseek(tmpfd
, sizeof(struct tagcache_header
), SEEK_SET
);
2388 lseek(fd
, 0, SEEK_END
);
2389 for (i
= 0; i
< h
->entry_count
; i
+= idxbuf_pos
)
2393 idxbuf_pos
= MIN(h
->entry_count
- i
, IDX_BUF_DEPTH
);
2396 memset(idxbuf
, 0, sizeof(struct index_entry
)*IDX_BUF_DEPTH
);
2400 int loc
= lseek(masterfd
, 0, SEEK_CUR
);
2402 if (ecread(masterfd
, idxbuf
, idxbuf_pos
, index_entry_ec
, tc_stat
.econ
)
2403 != (int)sizeof(struct index_entry
)*idxbuf_pos
)
2405 logf("read fail #6");
2409 lseek(masterfd
, loc
, SEEK_SET
);
2412 /* Read entry headers. */
2413 for (j
= 0; j
< idxbuf_pos
; j
++)
2415 if (!tagcache_is_sorted_tag(index_type
))
2417 struct temp_file_entry entry
;
2418 struct tagfile_entry fe
;
2420 if (read(tmpfd
, &entry
, sizeof(struct temp_file_entry
)) !=
2421 sizeof(struct temp_file_entry
))
2423 logf("read fail #7");
2429 if (entry
.tag_length
[index_type
] >= (int)sizeof(buf
))
2431 logf("too long entry!");
2432 logf("length=%d", entry
.tag_length
[index_type
]);
2433 logf("pos=0x%02lx", lseek(tmpfd
, 0, SEEK_CUR
));
2438 lseek(tmpfd
, entry
.tag_offset
[index_type
], SEEK_CUR
);
2439 if (read(tmpfd
, buf
, entry
.tag_length
[index_type
]) !=
2440 entry
.tag_length
[index_type
])
2442 logf("read fail #8");
2443 logf("offset=0x%02lx", entry
.tag_offset
[index_type
]);
2444 logf("length=0x%02x", entry
.tag_length
[index_type
]);
2449 /* Write to index file. */
2450 idxbuf
[j
].tag_seek
[index_type
] = lseek(fd
, 0, SEEK_CUR
);
2451 fe
.tag_length
= entry
.tag_length
[index_type
];
2452 fe
.idx_id
= tcmh
.tch
.entry_count
+ i
+ j
;
2453 ecwrite(fd
, &fe
, 1, tagfile_entry_ec
, tc_stat
.econ
);
2454 write(fd
, buf
, fe
.tag_length
);
2458 lseek(tmpfd
, entry
.data_length
- entry
.tag_offset
[index_type
] -
2459 entry
.tag_length
[index_type
], SEEK_CUR
);
2463 /* Locate the correct entry from the sorted array. */
2464 idxbuf
[j
].tag_seek
[index_type
] = tempbuf_find_location(i
+ j
);
2465 if (idxbuf
[j
].tag_seek
[index_type
] < 0)
2467 logf("entry not found (%d)", j
);
2475 if (ecwrite(masterfd
, idxbuf
, idxbuf_pos
,
2476 index_entry_ec
, tc_stat
.econ
) !=
2477 (int)sizeof(struct index_entry
)*idxbuf_pos
)
2479 logf("tagcache: write fail #4");
2488 /* Finally write the header. */
2489 tch
.magic
= TAGCACHE_MAGIC
;
2490 tch
.entry_count
= tempbufidx
;
2491 tch
.datasize
= lseek(fd
, 0, SEEK_END
) - sizeof(struct tagcache_header
);
2492 lseek(fd
, 0, SEEK_SET
);
2493 ecwrite(fd
, &tch
, 1, tagcache_header_ec
, tc_stat
.econ
);
2495 if (index_type
!= tag_filename
)
2496 h
->datasize
+= tch
.datasize
;
2497 logf("s:%d/%ld/%ld", index_type
, tch
.datasize
, h
->datasize
);
2509 static bool commit(void)
2511 struct tagcache_header tch
;
2512 struct master_header tcmh
;
2516 #ifdef HAVE_DIRCACHE
2517 bool dircache_buffer_stolen
= false;
2519 bool local_allocation
= false;
2521 logf("committing tagcache");
2526 tmpfd
= open(TAGCACHE_FILE_TEMP
, O_RDONLY
);
2529 logf("nothing to commit");
2534 /* Load the header. */
2535 len
= sizeof(struct tagcache_header
);
2536 rc
= read(tmpfd
, &tch
, len
);
2538 if (tch
.magic
!= TAGCACHE_MAGIC
|| rc
!= len
)
2540 logf("incorrect header");
2542 remove(TAGCACHE_FILE_TEMP
);
2546 if (tch
.entry_count
== 0)
2548 logf("nothing to commit");
2550 remove(TAGCACHE_FILE_TEMP
);
2554 #ifdef HAVE_EEPROM_SETTINGS
2555 remove(TAGCACHE_STATEFILE
);
2558 /* At first be sure to unload the ramcache! */
2559 #ifdef HAVE_TC_RAMCACHE
2560 tc_stat
.ramcache
= false;
2565 /* Try to steal every buffer we can :) */
2566 if (tempbuf_size
== 0)
2567 local_allocation
= true;
2569 #ifdef HAVE_DIRCACHE
2570 if (tempbuf_size
== 0)
2572 /* Try to steal the dircache buffer. */
2573 tempbuf
= dircache_steal_buffer(&tempbuf_size
);
2574 tempbuf_size
&= ~0x03;
2576 if (tempbuf_size
> 0)
2578 dircache_buffer_stolen
= true;
2583 #ifdef HAVE_TC_RAMCACHE
2584 if (tempbuf_size
== 0 && tc_stat
.ramcache_allocated
> 0)
2586 tempbuf
= (char *)(hdr
+ 1);
2587 tempbuf_size
= tc_stat
.ramcache_allocated
- sizeof(struct ramcache_header
) - 128;
2588 tempbuf_size
&= ~0x03;
2592 /* And finally fail if there are no buffers available. */
2593 if (tempbuf_size
== 0)
2595 logf("delaying commit until next boot");
2596 tc_stat
.commit_delayed
= true;
2602 logf("commit %ld entries...", tch
.entry_count
);
2604 /* Mark DB dirty so it will stay disabled if commit fails. */
2605 current_tcmh
.dirty
= true;
2606 update_master_header();
2608 /* Now create the index files. */
2609 tc_stat
.commit_step
= 0;
2611 tc_stat
.commit_delayed
= false;
2613 for (i
= 0; i
< TAG_COUNT
; i
++)
2617 if (tagcache_is_numeric_tag(i
))
2620 tc_stat
.commit_step
++;
2621 ret
= build_index(i
, &tch
, tmpfd
);
2625 logf("tagcache failed init");
2629 tc_stat
.commit_delayed
= true;
2630 tc_stat
.commit_step
= 0;
2636 if (!build_numeric_indices(&tch
, tmpfd
))
2638 logf("Failure to commit numeric indices");
2641 tc_stat
.commit_step
= 0;
2647 tc_stat
.commit_step
= 0;
2649 /* Update the master index headers. */
2650 if ( (masterfd
= open_master_fd(&tcmh
, true)) < 0)
2656 tcmh
.tch
.entry_count
+= tch
.entry_count
;
2657 tcmh
.tch
.datasize
= sizeof(struct master_header
)
2658 + sizeof(struct index_entry
) * tcmh
.tch
.entry_count
2663 lseek(masterfd
, 0, SEEK_SET
);
2664 ecwrite(masterfd
, &tcmh
, 1, master_header_ec
, tc_stat
.econ
);
2667 logf("tagcache committed");
2668 remove(TAGCACHE_FILE_TEMP
);
2669 tc_stat
.ready
= check_all_headers();
2670 tc_stat
.readyvalid
= true;
2672 if (local_allocation
)
2678 #ifdef HAVE_DIRCACHE
2679 /* Rebuild the dircache, if we stole the buffer. */
2680 if (dircache_buffer_stolen
)
2684 #ifdef HAVE_TC_RAMCACHE
2685 /* Reload tagcache. */
2686 if (tc_stat
.ramcache_allocated
> 0)
2687 tagcache_start_scan();
2695 static void allocate_tempbuf(void)
2697 /* Yeah, malloc would be really nice now :) */
2699 tempbuf_size
= 32*1024*1024;
2700 tempbuf
= malloc(tempbuf_size
);
2702 tempbuf
= (char *)(((long)audiobuf
& ~0x03) + 0x04);
2703 tempbuf_size
= (long)audiobufend
- (long)audiobuf
- 4;
2704 audiobuf
+= tempbuf_size
;
2708 static void free_tempbuf(void)
2710 if (tempbuf_size
== 0)
2716 audiobuf
-= tempbuf_size
;
2722 long tagcache_increase_serial(void)
2732 old
= current_tcmh
.serial
++;
2733 if (!update_master_header())
2739 long tagcache_get_serial(void)
2741 return current_tcmh
.serial
;
2744 long tagcache_get_commitid(void)
2746 return current_tcmh
.commitid
;
2749 static bool modify_numeric_entry(int masterfd
, int idx_id
, int tag
, long data
)
2751 struct index_entry idx
;
2756 if (!tagcache_is_numeric_tag(tag
))
2759 if (!get_index(masterfd
, idx_id
, &idx
, false))
2762 idx
.tag_seek
[tag
] = data
;
2763 idx
.flag
|= FLAG_DIRTYNUM
;
2765 return write_index(masterfd
, idx_id
, &idx
);
2768 bool tagcache_modify_numeric_entry(struct tagcache_search
*tcs
,
2771 struct master_header myhdr
;
2773 if (tcs
->masterfd
< 0)
2775 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, true)) < 0)
2779 return modify_numeric_entry(tcs
->masterfd
, tcs
->idx_id
, tag
, data
);
2782 static bool write_tag(int fd
, const char *tagstr
, const char *datastr
)
2787 snprintf(buf
, sizeof buf
, "%s=\"", tagstr
);
2788 for (i
= strlen(buf
); i
< (long)sizeof(buf
)-3; i
++)
2790 if (*datastr
== '\0')
2793 if (*datastr
== '"')
2800 buf
[i
] = *(datastr
++);
2803 strcpy(&buf
[i
], "\" ");
2805 write(fd
, buf
, i
+ 2);
2810 static bool read_tag(char *dest
, long size
,
2811 const char *src
, const char *tagstr
)
2814 char current_tag
[32];
2816 while (*src
!= '\0')
2818 /* Skip all whitespace */
2826 /* Read in tag name */
2827 while (*src
!= '=' && *src
!= ' ')
2829 current_tag
[pos
] = *src
;
2833 if (*src
== '\0' || pos
>= (long)sizeof(current_tag
))
2836 current_tag
[pos
] = '\0';
2838 /* Read in tag data */
2840 /* Find the start. */
2841 while (*src
!= '"' && *src
!= '\0')
2844 if (*src
== '\0' || *(++src
) == '\0')
2847 /* Read the data. */
2848 for (pos
= 0; pos
< size
; pos
++)
2853 if (*src
== '\\' && *(src
+1) == '"')
2875 if (!strcasecmp(tagstr
, current_tag
))
2882 static int parse_changelog_line(int line_n
, const char *buf
, void *parameters
)
2884 struct index_entry idx
;
2885 char tag_data
[TAG_MAXLEN
+32];
2887 long masterfd
= (long)parameters
;
2888 const int import_tags
[] = { tag_playcount
, tag_rating
, tag_playtime
, tag_lastplayed
,
2896 logf("%d/%s", line_n
, buf
);
2897 if (!read_tag(tag_data
, sizeof tag_data
, buf
, "filename"))
2899 logf("filename missing");
2904 idx_id
= find_index(tag_data
);
2907 logf("entry not found");
2911 if (!get_index(masterfd
, idx_id
, &idx
, false))
2913 logf("failed to retrieve index entry");
2917 /* Stop if tag has already been modified. */
2918 if (idx
.flag
& FLAG_DIRTYNUM
)
2921 logf("import: %s", tag_data
);
2923 idx
.flag
|= FLAG_DIRTYNUM
;
2924 for (i
= 0; i
< (long)(sizeof(import_tags
)/sizeof(import_tags
[0])); i
++)
2928 if (!read_tag(tag_data
, sizeof tag_data
, buf
,
2929 tagcache_tag_to_str(import_tags
[i
])))
2934 data
= atoi(tag_data
);
2938 idx
.tag_seek
[import_tags
[i
]] = data
;
2940 if (import_tags
[i
] == tag_lastplayed
&& data
> current_tcmh
.serial
)
2941 current_tcmh
.serial
= data
;
2942 else if (import_tags
[i
] == tag_commitid
&& data
>= current_tcmh
.commitid
)
2943 current_tcmh
.commitid
= data
+ 1;
2946 return write_index(masterfd
, idx_id
, &idx
) ? 0 : -5;
2950 bool tagcache_import_changelog(void)
2952 struct master_header myhdr
;
2953 struct tagcache_header tch
;
2964 clfd
= open(TAGCACHE_FILE_CHANGELOG
, O_RDONLY
);
2967 logf("failure to open changelog");
2971 if ( (masterfd
= open_master_fd(&myhdr
, true)) < 0)
2979 filenametag_fd
= open_tag_fd(&tch
, tag_filename
, false);
2981 fast_readline(clfd
, buf
, sizeof buf
, (long *)masterfd
,
2982 parse_changelog_line
);
2987 if (filenametag_fd
>= 0)
2988 close(filenametag_fd
);
2992 update_master_header();
2998 bool tagcache_create_changelog(struct tagcache_search
*tcs
)
3000 struct master_header myhdr
;
3001 struct index_entry idx
;
3002 char buf
[TAG_MAXLEN
+32];
3010 if (!tagcache_search(tcs
, tag_filename
))
3013 /* Initialize the changelog */
3014 clfd
= open(TAGCACHE_FILE_CHANGELOG
, O_WRONLY
| O_CREAT
| O_TRUNC
);
3017 logf("failure to open changelog");
3021 if (tcs
->masterfd
< 0)
3023 if ( (tcs
->masterfd
= open_master_fd(&myhdr
, false)) < 0)
3028 lseek(tcs
->masterfd
, 0, SEEK_SET
);
3029 ecread(tcs
->masterfd
, &myhdr
, 1, master_header_ec
, tc_stat
.econ
);
3032 write(clfd
, "## Changelog version 1\n", 23);
3034 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
3036 if (ecread(tcs
->masterfd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
3037 != sizeof(struct index_entry
))
3039 logf("read error #9");
3040 tagcache_search_finish(tcs
);
3045 /* Skip until the entry found has been modified. */
3046 if (! (idx
.flag
& FLAG_DIRTYNUM
) )
3049 /* Skip deleted entries too. */
3050 if (idx
.flag
& FLAG_DELETED
)
3053 /* Now retrieve all tags. */
3054 for (j
= 0; j
< TAG_COUNT
; j
++)
3056 if (tagcache_is_numeric_tag(j
))
3058 snprintf(temp
, sizeof temp
, "%d", idx
.tag_seek
[j
]);
3059 write_tag(clfd
, tagcache_tag_to_str(j
), temp
);
3064 tagcache_retrieve(tcs
, i
, tcs
->type
, buf
, sizeof buf
);
3065 write_tag(clfd
, tagcache_tag_to_str(j
), buf
);
3068 write(clfd
, "\n", 1);
3074 tagcache_search_finish(tcs
);
3079 static bool delete_entry(long idx_id
)
3083 struct index_entry idx
, myidx
;
3084 struct master_header myhdr
;
3085 char buf
[TAG_MAXLEN
+32];
3086 int in_use
[TAG_COUNT
];
3088 logf("delete_entry(): %ld", idx_id
);
3090 #ifdef HAVE_TC_RAMCACHE
3091 /* At first mark the entry removed from ram cache. */
3092 if (tc_stat
.ramcache
)
3093 hdr
->indices
[idx_id
].flag
|= FLAG_DELETED
;
3096 if ( (fd
= open_master_fd(&myhdr
, true) ) < 0)
3099 lseek(fd
, idx_id
* sizeof(struct index_entry
), SEEK_CUR
);
3100 if (ecread(fd
, &myidx
, 1, index_entry_ec
, tc_stat
.econ
)
3101 != sizeof(struct index_entry
))
3103 logf("delete_entry(): read error");
3108 myidx
.flag
|= FLAG_DELETED
;
3109 lseek(fd
, -sizeof(struct index_entry
), SEEK_CUR
);
3110 if (ecwrite(fd
, &myidx
, 1, index_entry_ec
, tc_stat
.econ
)
3111 != sizeof(struct index_entry
))
3113 logf("delete_entry(): write_error");
3118 /* Now check which tags are no longer in use (if any) */
3119 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3122 lseek(fd
, sizeof(struct master_header
), SEEK_SET
);
3123 for (i
= 0; i
< myhdr
.tch
.entry_count
; i
++)
3125 struct index_entry
*idxp
;
3127 #ifdef HAVE_TC_RAMCACHE
3128 /* Use RAM DB if available for greater speed */
3129 if (tc_stat
.ramcache
)
3130 idxp
= &hdr
->indices
[i
];
3134 if (ecread(fd
, &idx
, 1, index_entry_ec
, tc_stat
.econ
)
3135 != sizeof(struct index_entry
))
3137 logf("delete_entry(): read error #2");
3144 if (idxp
->flag
& FLAG_DELETED
)
3147 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3149 if (tagcache_is_numeric_tag(tag
))
3152 if (idxp
->tag_seek
[tag
] == myidx
.tag_seek
[tag
])
3159 /* Now delete all tags no longer in use. */
3160 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3162 if (tagcache_is_numeric_tag(tag
))
3167 logf("in use: %d/%d", tag
, in_use
[tag
]);
3171 #ifdef HAVE_TC_RAMCACHE
3172 /* Delete from ram. */
3173 if (tc_stat
.ramcache
&& tag
!= tag_filename
)
3175 struct tagfile_entry
*tagentry
= get_tag(&myidx
, tag
);
3176 tagentry
->tag_data
[0] = '\0';
3180 /* Open the index file, which contains the tag names. */
3181 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag
);
3182 fd
= open(buf
, O_RDWR
);
3186 logf("open failed");
3190 /* Skip the header block */
3191 lseek(fd
, myidx
.tag_seek
[tag
] + sizeof(struct tagfile_entry
), SEEK_SET
);
3193 /* Debug, print 10 first characters of the tag
3196 logf("TAG:%s", buf);
3197 lseek(fd, -10, SEEK_CUR);
3200 /* Write first data byte in tag as \0 */
3203 /* Now tag data has been removed */
3212 * Returns true if there is an event waiting in the queue
3213 * that requires the current operation to be aborted.
3215 static bool check_event_queue(void)
3219 queue_wait_w_tmo(&tagcache_queue
, &ev
, 0);
3224 case SYS_USB_CONNECTED
:
3225 /* Put the event back into the queue. */
3226 queue_post(&tagcache_queue
, ev
.id
, ev
.data
);
3234 #ifdef HAVE_TC_RAMCACHE
3235 static bool allocate_tagcache(void)
3237 struct master_header tcmh
;
3240 /* Load the header. */
3241 if ( (fd
= open_master_fd(&tcmh
, false)) < 0)
3250 * Now calculate the required cache size plus
3251 * some extra space for alignment fixes.
3253 tc_stat
.ramcache_allocated
= tcmh
.tch
.datasize
+ 128 + TAGCACHE_RESERVE
+
3254 sizeof(struct ramcache_header
) + TAG_COUNT
*sizeof(void *);
3255 hdr
= buffer_alloc(tc_stat
.ramcache_allocated
+ 128);
3256 memset(hdr
, 0, sizeof(struct ramcache_header
));
3257 memcpy(&hdr
->h
, &tcmh
, sizeof(struct master_header
));
3258 hdr
->indices
= (struct index_entry
*)(hdr
+ 1);
3259 logf("tagcache: %d bytes allocated.", tc_stat
.ramcache_allocated
);
3264 # ifdef HAVE_EEPROM_SETTINGS
3265 static bool tagcache_dumpload(void)
3267 struct statefile_header shdr
;
3272 fd
= open(TAGCACHE_STATEFILE
, O_RDONLY
);
3275 logf("no tagcache statedump");
3279 /* Check the statefile memory placement */
3280 hdr
= buffer_alloc(0);
3281 rc
= read(fd
, &shdr
, sizeof(struct statefile_header
));
3282 if (rc
!= sizeof(struct statefile_header
)
3283 /* || (long)hdr != (long)shdr.hdr */)
3285 logf("incorrect statefile");
3291 offpos
= (long)hdr
- (long)shdr
.hdr
;
3293 /* Lets allocate real memory and load it */
3294 hdr
= buffer_alloc(shdr
.tc_stat
.ramcache_allocated
);
3295 rc
= read(fd
, hdr
, shdr
.tc_stat
.ramcache_allocated
);
3298 if (rc
!= shdr
.tc_stat
.ramcache_allocated
)
3300 logf("read failure!");
3305 memcpy(&tc_stat
, &shdr
.tc_stat
, sizeof(struct tagcache_stat
));
3307 /* Now fix the pointers */
3308 hdr
->indices
= (struct index_entry
*)((long)hdr
->indices
+ offpos
);
3309 for (i
= 0; i
< TAG_COUNT
; i
++)
3310 hdr
->tags
[i
] += offpos
;
3315 static bool tagcache_dumpsave(void)
3317 struct statefile_header shdr
;
3320 if (!tc_stat
.ramcache
)
3323 fd
= open(TAGCACHE_STATEFILE
, O_WRONLY
| O_CREAT
| O_TRUNC
);
3326 logf("failed to create a statedump");
3330 /* Create the header */
3332 memcpy(&shdr
.tc_stat
, &tc_stat
, sizeof(struct tagcache_stat
));
3333 write(fd
, &shdr
, sizeof(struct statefile_header
));
3335 /* And dump the data too */
3336 write(fd
, hdr
, tc_stat
.ramcache_allocated
);
3343 static bool load_tagcache(void)
3345 struct tagcache_header
*tch
;
3346 long bytesleft
= tc_stat
.ramcache_allocated
;
3347 struct index_entry
*idx
;
3352 # ifdef HAVE_DIRCACHE
3353 while (dircache_is_initializing())
3357 logf("loading tagcache to ram...");
3359 fd
= open(TAGCACHE_FILE_MASTER
, O_RDONLY
);
3362 logf("tagcache open failed");
3366 if (ecread(fd
, &hdr
->h
, 1, master_header_ec
, tc_stat
.econ
)
3367 != sizeof(struct master_header
)
3368 || hdr
->h
.tch
.magic
!= TAGCACHE_MAGIC
)
3370 logf("incorrect header");
3376 /* Load the master index table. */
3377 for (i
= 0; i
< hdr
->h
.tch
.entry_count
; i
++)
3379 rc
= ecread(fd
, idx
, 1, index_entry_ec
, tc_stat
.econ
);
3380 if (rc
!= sizeof(struct index_entry
))
3382 logf("read error #10");
3387 bytesleft
-= sizeof(struct index_entry
);
3388 if (bytesleft
< 0 || ((long)idx
- (long)hdr
->indices
) >= tc_stat
.ramcache_allocated
)
3390 logf("too big tagcache.");
3400 /* Load the tags. */
3402 for (tag
= 0; tag
< TAG_COUNT
; tag
++)
3404 struct tagfile_entry
*fe
;
3405 char buf
[TAG_MAXLEN
+32];
3407 if (tagcache_is_numeric_tag(tag
))
3410 //p = ((void *)p+1);
3411 p
= (char *)((long)p
& ~0x03) + 0x04;
3414 /* Check the header. */
3415 tch
= (struct tagcache_header
*)p
;
3416 p
+= sizeof(struct tagcache_header
);
3418 if ( (fd
= open_tag_fd(tch
, tag
, false)) < 0)
3421 for (hdr
->entry_count
[tag
] = 0;
3422 hdr
->entry_count
[tag
] < tch
->entry_count
;
3423 hdr
->entry_count
[tag
]++)
3427 if (do_timed_yield())
3429 /* Abort if we got a critical event in queue */
3430 if (check_event_queue())
3434 fe
= (struct tagfile_entry
*)p
;
3435 pos
= lseek(fd
, 0, SEEK_CUR
);
3436 rc
= ecread(fd
, fe
, 1, tagfile_entry_ec
, tc_stat
.econ
);
3437 if (rc
!= sizeof(struct tagfile_entry
))
3439 /* End of lookup table. */
3440 logf("read error #11");
3445 /* We have a special handling for the filename tags. */
3446 if (tag
== tag_filename
)
3448 # ifdef HAVE_DIRCACHE
3449 const struct dircache_entry
*dc
;
3452 // FIXME: This is wrong!
3453 // idx = &hdr->indices[hdr->entry_count[i]];
3454 idx
= &hdr
->indices
[fe
->idx_id
];
3456 if (fe
->tag_length
>= (long)sizeof(buf
)-1)
3460 logf("TAG:%s", buf
);
3461 logf("too long filename");
3466 rc
= read(fd
, buf
, fe
->tag_length
);
3467 if (rc
!= fe
->tag_length
)
3469 logf("read error #12");
3474 /* Check if the entry has already been removed */
3475 if (idx
->flag
& FLAG_DELETED
)
3478 /* This flag must not be used yet. */
3479 if (idx
->flag
& FLAG_DIRCACHE
)
3481 logf("internal error!");
3486 if (idx
->tag_seek
[tag
] != pos
)
3488 logf("corrupt data structures!");
3493 # ifdef HAVE_DIRCACHE
3494 if (dircache_is_enabled())
3496 dc
= dircache_get_entry_ptr(buf
);
3499 logf("Entry no longer valid.");
3501 delete_entry(fe
->idx_id
);
3505 idx
->flag
|= FLAG_DIRCACHE
;
3506 FLAG_SET_ATTR(idx
->flag
, fe
->idx_id
);
3507 idx
->tag_seek
[tag_filename
] = (long)dc
;
3513 # if 0 /* Maybe we could enable this for flash players. Too slow otherwise. */
3514 /* Check if entry has been removed. */
3515 if (global_settings
.tagcache_autoupdate
)
3519 testfd
= open(buf
, O_RDONLY
);
3522 logf("Entry no longer valid.");
3524 delete_entry(fe
->idx_id
);
3535 bytesleft
-= sizeof(struct tagfile_entry
) + fe
->tag_length
;
3538 logf("too big tagcache #2");
3539 logf("tl: %d", fe
->tag_length
);
3540 logf("bl: %ld", bytesleft
);
3546 rc
= read(fd
, fe
->tag_data
, fe
->tag_length
);
3549 if (rc
!= fe
->tag_length
)
3551 logf("read error #13");
3552 logf("rc=0x%04x", rc
); // 0x431
3553 logf("len=0x%04x", fe
->tag_length
); // 0x4000
3554 logf("pos=0x%04lx", lseek(fd
, 0, SEEK_CUR
)); // 0x433
3555 logf("tag=0x%02x", tag
); // 0x00
3563 tc_stat
.ramcache_used
= tc_stat
.ramcache_allocated
- bytesleft
;
3564 logf("tagcache loaded into ram!");
3568 #endif /* HAVE_TC_RAMCACHE */
3570 static bool check_deleted_files(void)
3573 char buf
[TAG_MAXLEN
+32];
3574 struct tagfile_entry tfe
;
3576 logf("reverse scan...");
3577 snprintf(buf
, sizeof buf
, TAGCACHE_FILE_INDEX
, tag_filename
);
3578 fd
= open(buf
, O_RDONLY
);
3582 logf("%s open fail", buf
);
3586 lseek(fd
, sizeof(struct tagcache_header
), SEEK_SET
);
3587 while (ecread(fd
, &tfe
, 1, tagfile_entry_ec
, tc_stat
.econ
)
3588 == sizeof(struct tagfile_entry
)
3590 && !check_event_queue()
3594 if (tfe
.tag_length
>= (long)sizeof(buf
)-1)
3596 logf("too long tag");
3601 if (read(fd
, buf
, tfe
.tag_length
) != tfe
.tag_length
)
3603 logf("read error #14");
3608 /* Check if the file has already deleted from the db. */
3612 /* Now check if the file exists. */
3613 testfd
= open(buf
, O_RDONLY
);
3616 logf("Entry no longer valid.");
3617 logf("-> %s / %d", buf
, tfe
.tag_length
);
3618 delete_entry(tfe
.idx_id
);
3630 static bool check_dir(const char *dirname
)
3634 int success
= false;
3636 dir
= opendir_cached(dirname
);
3639 logf("tagcache: opendir_cached() failed");
3643 /* Recursively scan the dir. */
3647 while (!check_event_queue())
3650 struct dircache_entry
*entry
;
3652 entry
= readdir_cached(dir
);
3660 if (!strcmp((char *)entry
->d_name
, ".") ||
3661 !strcmp((char *)entry
->d_name
, ".."))
3666 len
= strlen(curpath
);
3667 snprintf(&curpath
[len
], curpath_size
- len
, "/%s",
3670 processed_dir_count
++;
3671 if (entry
->attribute
& ATTR_DIRECTORY
)
3674 #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
3675 add_tagcache(curpath
, dir
->internal_entry
);
3677 add_tagcache(curpath
);
3680 curpath
[len
] = '\0';
3683 closedir_cached(dir
);
3688 void build_tagcache(const char *path
)
3690 struct tagcache_header header
;
3695 total_entry_count
= 0;
3696 processed_dir_count
= 0;
3698 #ifdef HAVE_DIRCACHE
3699 while (dircache_is_initializing())
3703 logf("updating tagcache");
3705 cachefd
= open(TAGCACHE_FILE_TEMP
, O_RDONLY
);
3708 logf("skipping, cache already waiting for commit");
3713 cachefd
= open(TAGCACHE_FILE_TEMP
, O_RDWR
| O_CREAT
| O_TRUNC
);
3716 logf("master file open failed: %s", TAGCACHE_FILE_TEMP
);
3720 filenametag_fd
= open_tag_fd(&header
, tag_filename
, false);
3724 logf("Scanning files...");
3725 /* Scan for new files. */
3726 memset(&header
, 0, sizeof(struct tagcache_header
));
3727 write(cachefd
, &header
, sizeof(struct tagcache_header
));
3729 if (strcmp("/", path
) != 0)
3730 strcpy(curpath
, path
);
3731 ret
= check_dir(path
);
3733 /* Write the header. */
3734 header
.magic
= TAGCACHE_MAGIC
;
3735 header
.datasize
= data_size
;
3736 header
.entry_count
= total_entry_count
;
3737 lseek(cachefd
, 0, SEEK_SET
);
3738 write(cachefd
, &header
, sizeof(struct tagcache_header
));
3741 if (filenametag_fd
>= 0)
3743 close(filenametag_fd
);
3744 filenametag_fd
= -1;
3754 /* Commit changes to the database. */
3760 remove(TAGCACHE_FILE_TEMP
);
3761 logf("tagcache built!");
3767 #ifdef HAVE_TC_RAMCACHE
3770 /* Import runtime statistics if we just initialized the db. */
3771 if (hdr
->h
.serial
== 0)
3772 queue_post(&tagcache_queue
, Q_IMPORT_CHANGELOG
, 0);
3779 #ifdef HAVE_TC_RAMCACHE
3780 static void load_ramcache(void)
3787 /* At first we should load the cache (if exists). */
3788 tc_stat
.ramcache
= load_tagcache();
3790 if (!tc_stat
.ramcache
)
3792 /* If loading failed, it must indicate some problem with the db
3793 * so disable it entirely to prevent further issues. */
3794 tc_stat
.ready
= false;
3801 void tagcache_unload_ramcache(void)
3803 tc_stat
.ramcache
= false;
3804 /* Just to make sure there is no statefile present. */
3805 // remove(TAGCACHE_STATEFILE);
3810 static void tagcache_thread(void)
3813 bool check_done
= false;
3815 /* If the previous cache build/update was interrupted, commit
3816 * the changes first in foreground. */
3822 #ifdef HAVE_TC_RAMCACHE
3823 # ifdef HAVE_EEPROM_SETTINGS
3824 if (firmware_settings
.initialized
&& firmware_settings
.disk_clean
)
3825 check_done
= tagcache_dumpload();
3827 remove(TAGCACHE_STATEFILE
);
3830 /* Allocate space for the tagcache if found on disk. */
3831 if (global_settings
.tagcache_ram
&& !tc_stat
.ramcache
)
3832 allocate_tagcache();
3836 tc_stat
.initialized
= true;
3838 /* Don't delay bootup with the header check but do it on background. */
3840 tc_stat
.ready
= check_all_headers();
3841 tc_stat
.readyvalid
= true;
3845 queue_wait_w_tmo(&tagcache_queue
, &ev
, HZ
);
3849 case Q_IMPORT_CHANGELOG
:
3850 tagcache_import_changelog();
3855 build_tagcache("/");
3859 build_tagcache("/");
3860 #ifdef HAVE_TC_RAMCACHE
3863 check_deleted_files();
3869 if (check_done
|| !tc_stat
.ready
)
3872 #ifdef HAVE_TC_RAMCACHE
3873 if (!tc_stat
.ramcache
&& global_settings
.tagcache_ram
)
3876 if (tc_stat
.ramcache
&& global_settings
.tagcache_autoupdate
)
3877 build_tagcache("/");
3881 if (global_settings
.tagcache_autoupdate
)
3883 build_tagcache("/");
3884 /* Don't do auto removal without dircache (very slow). */
3885 #ifdef HAVE_DIRCACHE
3886 if (dircache_is_enabled())
3887 check_deleted_files();
3891 logf("tagcache check done");
3903 case SYS_USB_CONNECTED
:
3904 logf("USB: TagCache");
3905 usb_acknowledge(SYS_USB_CONNECTED_ACK
);
3906 usb_wait_for_disconnect(&tagcache_queue
);
3913 bool tagcache_prepare_shutdown(void)
3915 if (tagcache_get_commit_step() > 0)
3918 tagcache_stop_scan();
3919 while (read_lock
|| write_lock
)
3925 void tagcache_shutdown(void)
3927 #ifdef HAVE_EEPROM_SETTINGS
3928 if (tc_stat
.ramcache
)
3929 tagcache_dumpsave();
3933 static int get_progress(void)
3935 int total_count
= -1;
3937 #ifdef HAVE_DIRCACHE
3938 if (dircache_is_enabled())
3940 total_count
= dircache_get_entry_count();
3944 #ifdef HAVE_TC_RAMCACHE
3946 if (hdr
&& tc_stat
.ramcache
)
3947 total_count
= hdr
->h
.tch
.entry_count
;
3951 if (total_count
< 0)
3954 return processed_dir_count
* 100 / total_count
;
3957 struct tagcache_stat
* tagcache_get_stat(void)
3959 tc_stat
.progress
= get_progress();
3960 tc_stat
.processed_entries
= processed_dir_count
;
3965 void tagcache_start_scan(void)
3967 queue_post(&tagcache_queue
, Q_START_SCAN
, 0);
3970 bool tagcache_update(void)
3975 queue_post(&tagcache_queue
, Q_UPDATE
, 0);
3979 bool tagcache_rebuild()
3981 queue_post(&tagcache_queue
, Q_REBUILD
, 0);
3985 void tagcache_stop_scan(void)
3987 queue_post(&tagcache_queue
, Q_STOP_SCAN
, 0);
3990 #ifdef HAVE_TC_RAMCACHE
3991 bool tagcache_is_ramcache(void)
3993 return tc_stat
.ramcache
;
3997 #endif /* !__PCTOOL__ */
4000 void tagcache_init(void)
4002 memset(&tc_stat
, 0, sizeof(struct tagcache_stat
));
4003 memset(¤t_tcmh
, 0, sizeof(struct master_header
));
4004 filenametag_fd
= -1;
4005 write_lock
= read_lock
= 0;
4008 queue_init(&tagcache_queue
, true);
4009 create_thread(tagcache_thread
, tagcache_stack
,
4010 sizeof(tagcache_stack
), tagcache_thread_name
4011 IF_PRIO(, PRIORITY_BACKGROUND
)
4012 IF_COP(, CPU
, false));
4014 tc_stat
.initialized
= true;
4018 tc_stat
.ready
= check_all_headers();
4023 void tagcache_reverse_scan(void)
4025 logf("Checking for deleted files");
4026 check_deleted_files();
4030 bool tagcache_is_initialized(void)
4032 return tc_stat
.initialized
;
4034 bool tagcache_is_usable(void)
4036 return tc_stat
.initialized
&& tc_stat
.ready
;
4038 int tagcache_get_commit_step(void)
4040 return tc_stat
.commit_step
;
4042 int tagcache_get_max_commit_step(void)
4044 return (int)(sizeof(sorted_tags
)/sizeof(sorted_tags
[0]))+1;