diff options
Diffstat (limited to 'apps/tagcache.c')
-rw-r--r-- | apps/tagcache.c | 275 |
1 files changed, 233 insertions, 42 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 | ||
2029 | static bool build_numeric_indices(struct tagcache_header *h, int tmpfd) | 2029 | static 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) | |||
3289 | static bool delete_entry(long idx_id) | 3431 | static 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 | ||