summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/tagcache.c275
-rw-r--r--apps/tagcache.h12
2 files changed, 238 insertions, 49 deletions
diff --git a/apps/tagcache.c b/apps/tagcache.c
index 1f4521168e..05e224543c 100644
--- a/apps/tagcache.c
+++ b/apps/tagcache.c
@@ -2028,7 +2028,7 @@ inline static int tempbuf_find_location(int id)
2028 2028
2029static bool build_numeric_indices(struct tagcache_header *h, int tmpfd) 2029static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2030{ 2030{
2031 struct master_header tcmh; 2031 struct master_header tcmh;
2032 struct index_entry idx; 2032 struct index_entry idx;
2033 int masterfd; 2033 int masterfd;
2034 int masterfd_pos; 2034 int masterfd_pos;
@@ -2036,6 +2036,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2036 int max_entries; 2036 int max_entries;
2037 int entries_processed = 0; 2037 int entries_processed = 0;
2038 int i, j; 2038 int i, j;
2039 char buf[TAG_MAXLEN];
2039 2040
2040 max_entries = tempbuf_size / sizeof(struct temp_file_entry) - 1; 2041 max_entries = tempbuf_size / sizeof(struct temp_file_entry) - 1;
2041 2042
@@ -2061,8 +2062,11 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2061 /* Read in as many entries as possible. */ 2062 /* Read in as many entries as possible. */
2062 for (i = 0; i < count; i++) 2063 for (i = 0; i < count; i++)
2063 { 2064 {
2065 struct temp_file_entry *tfe = &entrybuf[i];
2066 int datastart;
2067
2064 /* Read in numeric data. */ 2068 /* Read in numeric data. */
2065 if (read(tmpfd, &entrybuf[i], sizeof(struct temp_file_entry)) != 2069 if (read(tmpfd, tfe, sizeof(struct temp_file_entry)) !=
2066 sizeof(struct temp_file_entry)) 2070 sizeof(struct temp_file_entry))
2067 { 2071 {
2068 logf("read fail #1"); 2072 logf("read fail #1");
@@ -2070,10 +2074,142 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2070 return false; 2074 return false;
2071 } 2075 }
2072 2076
2073 /* Skip string data. */ 2077 datastart = lseek(tmpfd, 0, SEEK_CUR);
2074 lseek(tmpfd, entrybuf[i].data_length, SEEK_CUR); 2078
2079 /**
2080 * Read string data from the following tags:
2081 * - tag_filename
2082 * - tag_artist
2083 * - tag_album
2084 * - tag_title
2085 *
2086 * A crc32 hash is calculated from the read data
2087 * and stored back to the data offset field kept in memory.
2088 */
2089#define tmpdb_read_string_tag(tag) \
2090 lseek(tmpfd, tfe->tag_offset[tag], SEEK_CUR); \
2091 if ((unsigned long)tfe->tag_length[tag] > sizeof buf) \
2092 { \
2093 logf("read fail: buffer overflow"); \
2094 close(masterfd); \
2095 return false; \
2096 } \
2097 \
2098 if (read(tmpfd, buf, tfe->tag_length[tag]) != \
2099 tfe->tag_length[tag]) \
2100 { \
2101 logf("read fail #2"); \
2102 close(masterfd); \
2103 return false; \
2104 } \
2105 \
2106 tfe->tag_offset[tag] = crc_32(buf, strlen(buf), 0xffffffff); \
2107 lseek(tmpfd, datastart, SEEK_SET)
2108
2109 tmpdb_read_string_tag(tag_filename);
2110 tmpdb_read_string_tag(tag_artist);
2111 tmpdb_read_string_tag(tag_album);
2112 tmpdb_read_string_tag(tag_title);
2113
2114 /* Seek to the end of the string data. */
2115 lseek(tmpfd, tfe->data_length, SEEK_CUR);
2075 } 2116 }
2076 2117
2118 /* Backup the master index position. */
2119 masterfd_pos = lseek(masterfd, 0, SEEK_CUR);
2120 lseek(masterfd, sizeof(struct master_header), SEEK_SET);
2121
2122 /* Check if we can resurrect some deleted runtime statistics data. */
2123 for (i = 0; i < tcmh.tch.entry_count; i++)
2124 {
2125 /* Read the index entry. */
2126 if (ecread(masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
2127 != sizeof(struct index_entry))
2128 {
2129 logf("read fail #3");
2130 close(masterfd);
2131 return false;
2132 }
2133
2134 /**
2135 * Skip unless the entry is marked as being deleted
2136 * or the data has already been resurrected.
2137 */
2138 if (!(idx.flag & FLAG_DELETED) || idx.flag & FLAG_RESURRECTED)
2139 continue;
2140
2141 /* Now try to match the entry. */
2142 /**
2143 * To succesfully match a song, the following conditions
2144 * must apply:
2145 *
2146 * For numeric fields: tag_length
2147 * - Full identical match is required
2148 *
2149 * If tag_filename matches, no further checking necessary.
2150 *
2151 * For string hashes: tag_artist, tag_album, tag_title
2152 * - Two of these must match
2153 */
2154 for (j = 0; j < count; j++)
2155 {
2156 struct temp_file_entry *tfe = &entrybuf[j];
2157
2158 /* Try to match numeric fields first. */
2159 if (tfe->tag_offset[tag_length] != idx.tag_seek[tag_length])
2160 continue;
2161
2162 /* Now it's time to do the hash matching. */
2163 if (tfe->tag_offset[tag_filename] != idx.tag_seek[tag_filename])
2164 {
2165 int match_count = 0;
2166
2167 /* No filename match, check if we can match two other tags. */
2168#define tmpdb_match(tag) \
2169 if (tfe->tag_offset[tag] == idx.tag_seek[tag]) \
2170 match_count++
2171
2172 tmpdb_match(tag_artist);
2173 tmpdb_match(tag_album);
2174 tmpdb_match(tag_title);
2175
2176 if (match_count < 2)
2177 {
2178 /* Still no match found, give up. */
2179 continue;
2180 }
2181 }
2182
2183 /* A match found, now copy & resurrect the statistical data. */
2184#define tmpdb_copy_tag(tag) \
2185 tfe->tag_offset[tag] = idx.tag_seek[tag]
2186
2187 tmpdb_copy_tag(tag_playcount);
2188 tmpdb_copy_tag(tag_rating);
2189 tmpdb_copy_tag(tag_playtime);
2190 tmpdb_copy_tag(tag_lastplayed);
2191 tmpdb_copy_tag(tag_commitid);
2192
2193 /* Avoid processing this entry again. */
2194 idx.flag |= FLAG_RESURRECTED;
2195
2196 lseek(masterfd, -sizeof(struct index_entry), SEEK_CUR);
2197 if (ecwrite(masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
2198 != sizeof(struct index_entry))
2199 {
2200 logf("masterfd writeback fail #1");
2201 close(masterfd);
2202 return false;
2203 }
2204
2205 logf("Entry resurrected");
2206 }
2207 }
2208
2209
2210 /* Restore the master index position. */
2211 lseek(masterfd, masterfd_pos, SEEK_SET);
2212
2077 /* Commit the data to the index. */ 2213 /* Commit the data to the index. */
2078 for (i = 0; i < count; i++) 2214 for (i = 0; i < count; i++)
2079 { 2215 {
@@ -2082,7 +2218,7 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2082 if (ecread(masterfd, &idx, 1, index_entry_ec, tc_stat.econ) 2218 if (ecread(masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
2083 != sizeof(struct index_entry)) 2219 != sizeof(struct index_entry))
2084 { 2220 {
2085 logf("read fail #2"); 2221 logf("read fail #3");
2086 close(masterfd); 2222 close(masterfd);
2087 return false; 2223 return false;
2088 } 2224 }
@@ -2096,7 +2232,12 @@ static bool build_numeric_indices(struct tagcache_header *h, int tmpfd)
2096 } 2232 }
2097 idx.flag = entrybuf[i].flag; 2233 idx.flag = entrybuf[i].flag;
2098 2234
2099 if (tc_stat.ready && current_tcmh.commitid > 0) 2235 if (idx.tag_seek[tag_commitid])
2236 {
2237 /* Data has been resurrected. */
2238 idx.flag |= FLAG_DIRTYNUM;
2239 }
2240 else if (tc_stat.ready && current_tcmh.commitid > 0)
2100 { 2241 {
2101 idx.tag_seek[tag_commitid] = current_tcmh.commitid; 2242 idx.tag_seek[tag_commitid] = current_tcmh.commitid;
2102 idx.flag |= FLAG_DIRTYNUM; 2243 idx.flag |= FLAG_DIRTYNUM;
@@ -2452,7 +2593,7 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
2452 if (idxbuf[j].flag & FLAG_DELETED) 2593 if (idxbuf[j].flag & FLAG_DELETED)
2453 { 2594 {
2454 /* We can just ignore deleted entries. */ 2595 /* We can just ignore deleted entries. */
2455 idxbuf[j].tag_seek[index_type] = 0; 2596 // idxbuf[j].tag_seek[index_type] = 0;
2456 continue; 2597 continue;
2457 } 2598 }
2458 2599
@@ -2462,7 +2603,8 @@ static int build_index(int index_type, struct tagcache_header *h, int tmpfd)
2462 2603
2463 if (idxbuf[j].tag_seek[index_type] < 0) 2604 if (idxbuf[j].tag_seek[index_type] < 0)
2464 { 2605 {
2465 logf("update error: %d/%ld", i+j, tcmh.tch.entry_count); 2606 logf("update error: %d/%d/%ld",
2607 idxbuf[j].flag, i+j, tcmh.tch.entry_count);
2466 error = true; 2608 error = true;
2467 goto error_exit; 2609 goto error_exit;
2468 } 2610 }
@@ -3289,7 +3431,7 @@ bool tagcache_create_changelog(struct tagcache_search *tcs)
3289static bool delete_entry(long idx_id) 3431static bool delete_entry(long idx_id)
3290{ 3432{
3291 int fd = -1; 3433 int fd = -1;
3292 /*int dbdel_fd = -1;*/ 3434 int masterfd = -1;
3293 int tag, i; 3435 int tag, i;
3294 struct index_entry idx, myidx; 3436 struct index_entry idx, myidx;
3295 struct master_header myhdr; 3437 struct master_header myhdr;
@@ -3304,42 +3446,34 @@ static bool delete_entry(long idx_id)
3304 hdr->indices[idx_id].flag |= FLAG_DELETED; 3446 hdr->indices[idx_id].flag |= FLAG_DELETED;
3305#endif 3447#endif
3306 3448
3307 if ( (fd = open_master_fd(&myhdr, true) ) < 0) 3449 if ( (masterfd = open_master_fd(&myhdr, true) ) < 0)
3308 return false; 3450 return false;
3309 3451
3310 /* 3452 lseek(masterfd, idx_id * sizeof(struct index_entry), SEEK_CUR);
3311 TODO: Implement soon. 3453 if (ecread(masterfd, &myidx, 1, index_entry_ec, tc_stat.econ)
3312 dbdel_fd = open(TAGCACHE_FILE_DELETED, O_RDWR | O_APPEND | O_CREAT);
3313 if (dbdel_fd < 0)
3314 {
3315 logf("delete_entry(): DBDEL open failed");
3316 goto cleanup;
3317 }
3318 close(dbdel_fd);
3319 dbdel_fd = -1;
3320 */
3321 lseek(fd, idx_id * sizeof(struct index_entry), SEEK_CUR);
3322 if (ecread(fd, &myidx, 1, index_entry_ec, tc_stat.econ)
3323 != sizeof(struct index_entry)) 3454 != sizeof(struct index_entry))
3324 { 3455 {
3325 logf("delete_entry(): read error"); 3456 logf("delete_entry(): read error");
3326 goto cleanup; 3457 goto cleanup;
3327 } 3458 }
3328 3459
3329 myidx.flag |= FLAG_DELETED; 3460 if (myidx.flag & FLAG_DELETED)
3330 lseek(fd, -sizeof(struct index_entry), SEEK_CUR);
3331 if (ecwrite(fd, &myidx, 1, index_entry_ec, tc_stat.econ)
3332 != sizeof(struct index_entry))
3333 { 3461 {
3334 logf("delete_entry(): write_error"); 3462 logf("delete_entry(): already deleted!");
3335 goto cleanup; 3463 goto cleanup;
3336 } 3464 }
3337 3465
3466 myidx.flag |= FLAG_DELETED;
3467#ifdef HAVE_TC_RAMCACHE
3468 if (tc_stat.ramcache)
3469 hdr->indices[idx_id].flag |= FLAG_DELETED;
3470#endif
3471
3338 /* Now check which tags are no longer in use (if any) */ 3472 /* Now check which tags are no longer in use (if any) */
3339 for (tag = 0; tag < TAG_COUNT; tag++) 3473 for (tag = 0; tag < TAG_COUNT; tag++)
3340 in_use[tag] = 0; 3474 in_use[tag] = 0;
3341 3475
3342 lseek(fd, sizeof(struct master_header), SEEK_SET); 3476 lseek(masterfd, sizeof(struct master_header), SEEK_SET);
3343 for (i = 0; i < myhdr.tch.entry_count; i++) 3477 for (i = 0; i < myhdr.tch.entry_count; i++)
3344 { 3478 {
3345 struct index_entry *idxp; 3479 struct index_entry *idxp;
@@ -3351,7 +3485,7 @@ static bool delete_entry(long idx_id)
3351 else 3485 else
3352#endif 3486#endif
3353 { 3487 {
3354 if (ecread(fd, &idx, 1, index_entry_ec, tc_stat.econ) 3488 if (ecread(masterfd, &idx, 1, index_entry_ec, tc_stat.econ)
3355 != sizeof(struct index_entry)) 3489 != sizeof(struct index_entry))
3356 { 3490 {
3357 logf("delete_entry(): read error #2"); 3491 logf("delete_entry(): read error #2");
@@ -3373,18 +3507,65 @@ static bool delete_entry(long idx_id)
3373 } 3507 }
3374 } 3508 }
3375 3509
3376 close(fd);
3377 fd = -1;
3378
3379 /* Now delete all tags no longer in use. */ 3510 /* Now delete all tags no longer in use. */
3380 for (tag = 0; tag < TAG_COUNT; tag++) 3511 for (tag = 0; tag < TAG_COUNT; tag++)
3381 { 3512 {
3513 struct tagcache_header tch;
3514 int oldseek = myidx.tag_seek[tag];
3515
3382 if (tagcache_is_numeric_tag(tag)) 3516 if (tagcache_is_numeric_tag(tag))
3383 continue; 3517 continue;
3384 3518
3519 /**
3520 * Replace tag seek with a hash value of the field string data.
3521 * That way runtime statistics of moved or altered files can be
3522 * resurrected.
3523 */
3524#ifdef HAVE_TC_RAMCACHE
3525 if (tc_stat.ramcache && tag != tag_filename)
3526 {
3527 struct tagfile_entry *tfe;
3528 long *seek = &hdr->indices[idx_id].tag_seek[tag];
3529
3530 tfe = (struct tagfile_entry *)&hdr->tags[tag][*seek];
3531 *seek = crc_32(tfe->tag_data, strlen(tfe->tag_data), 0xffffffff);
3532 myidx.tag_seek[tag] = *seek;
3533 }
3534 else
3535#endif
3536 {
3537 struct tagfile_entry tfe;
3538
3539 /* Open the index file, which contains the tag names. */
3540 if ((fd = open_tag_fd(&tch, tag, true)) < 0)
3541 goto cleanup;
3542
3543 /* Skip the header block */
3544 lseek(fd, myidx.tag_seek[tag], SEEK_SET);
3545 if (ecread(fd, &tfe, 1, tagfile_entry_ec, tc_stat.econ)
3546 != sizeof(struct tagfile_entry))
3547 {
3548 logf("delete_entry(): read error #3");
3549 goto cleanup;
3550 }
3551
3552 if (read(fd, buf, tfe.tag_length) != tfe.tag_length)
3553 {
3554 logf("delete_entry(): read error #4");
3555 goto cleanup;
3556 }
3557
3558 myidx.tag_seek[tag] = crc_32(buf, strlen(buf), 0xffffffff);
3559 }
3560
3385 if (in_use[tag]) 3561 if (in_use[tag])
3386 { 3562 {
3387 logf("in use: %d/%d", tag, in_use[tag]); 3563 logf("in use: %d/%d", tag, in_use[tag]);
3564 if (fd >= 0)
3565 {
3566 close(fd);
3567 fd = -1;
3568 }
3388 continue; 3569 continue;
3389 } 3570 }
3390 3571
@@ -3398,17 +3579,14 @@ static bool delete_entry(long idx_id)
3398#endif 3579#endif
3399 3580
3400 /* Open the index file, which contains the tag names. */ 3581 /* Open the index file, which contains the tag names. */
3401 snprintf(buf, sizeof buf, TAGCACHE_FILE_INDEX, tag);
3402 fd = open(buf, O_RDWR);
3403
3404 if (fd < 0) 3582 if (fd < 0)
3405 { 3583 {
3406 logf("open failed"); 3584 if ((fd = open_tag_fd(&tch, tag, true)) < 0)
3407 goto cleanup; 3585 goto cleanup;
3408 } 3586 }
3409 3587
3410 /* Skip the header block */ 3588 /* Skip the header block */
3411 lseek(fd, myidx.tag_seek[tag] + sizeof(struct tagfile_entry), SEEK_SET); 3589 lseek(fd, oldseek + sizeof(struct tagfile_entry), SEEK_SET);
3412 3590
3413 /* Debug, print 10 first characters of the tag 3591 /* Debug, print 10 first characters of the tag
3414 read(fd, buf, 10); 3592 read(fd, buf, 10);
@@ -3422,16 +3600,29 @@ static bool delete_entry(long idx_id)
3422 3600
3423 /* Now tag data has been removed */ 3601 /* Now tag data has been removed */
3424 close(fd); 3602 close(fd);
3603 fd = -1;
3604 }
3605
3606 /* Write index entry back into master index. */
3607 lseek(masterfd, sizeof(struct master_header) +
3608 (idx_id * sizeof(struct index_entry)), SEEK_SET);
3609 if (ecwrite(masterfd, &myidx, 1, index_entry_ec, tc_stat.econ)
3610 != sizeof(struct index_entry))
3611 {
3612 logf("delete_entry(): write_error");
3613 goto cleanup;
3425 } 3614 }
3426 3615
3616 close(masterfd);
3617
3427 return true; 3618 return true;
3428 3619
3429 cleanup: 3620 cleanup:
3430 if (fd >= 0) 3621 if (fd >= 0)
3431 close(fd); 3622 close(fd);
3432/* if (dbdel_fd >= 0) 3623 if (masterfd >= 0)
3433 close(dbdel_fd); 3624 close(masterfd);
3434 */ 3625
3435 return false; 3626 return false;
3436} 3627}
3437 3628
diff --git a/apps/tagcache.h b/apps/tagcache.h
index 879cf66fbd..9aaa17c9c8 100644
--- a/apps/tagcache.h
+++ b/apps/tagcache.h
@@ -76,9 +76,6 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
76/* Temporary database containing new tags to be committed to the main db. */ 76/* Temporary database containing new tags to be committed to the main db. */
77#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd" 77#define TAGCACHE_FILE_TEMP ROCKBOX_DIR "/database_tmp.tcd"
78 78
79/* Database containing deleted entries with runtime statistics. */
80#define TAGCACHE_FILE_DELETED ROCKBOX_DIR "/database_del.tcd"
81
82/* The main database master index and numeric data. */ 79/* The main database master index and numeric data. */
83#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd" 80#define TAGCACHE_FILE_MASTER ROCKBOX_DIR "/database_idx.tcd"
84 81
@@ -92,10 +89,11 @@ enum tag_type { tag_artist = 0, tag_album, tag_genre, tag_title,
92#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd" 89#define TAGCACHE_STATEFILE ROCKBOX_DIR "/database_state.tcd"
93 90
94/* Flags */ 91/* Flags */
95#define FLAG_DELETED 0x0001 /* Entry has been removed from db */ 92#define FLAG_DELETED 0x0001 /* Entry has been removed from db */
96#define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */ 93#define FLAG_DIRCACHE 0x0002 /* Filename is a dircache pointer */
97#define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */ 94#define FLAG_DIRTYNUM 0x0004 /* Numeric data has been modified */
98#define FLAG_TRKNUMGEN 0x0008 /* Track number has been generated */ 95#define FLAG_TRKNUMGEN 0x0008 /* Track number has been generated */
96#define FLAG_RESURRECTED 0x0010 /* Statistics data has been resurrected */
99#define FLAG_GET_ATTR(flag) ((flag >> 16) & 0x0000ffff) 97#define FLAG_GET_ATTR(flag) ((flag >> 16) & 0x0000ffff)
100#define FLAG_SET_ATTR(flag,attr) flag = (flag & 0x0000ffff) | (attr << 16) 98#define FLAG_SET_ATTR(flag,attr) flag = (flag & 0x0000ffff) | (attr << 16)
101 99