#include "src/ctx.h" #include "serialize.h" static __thread int index_fd = -1; typedef struct { unsigned char uuid[16]; unsigned long ino; unsigned long size; unsigned int mime; int mtime; short base; short ext; } line_t; void skip_meta(FILE *file) { enum metakey key = getc(file); while (key != '\n') { if (IS_META_INT(key)) { fseek(file, sizeof(int), SEEK_CUR); } else if (IS_META_LONG(key)) { fseek(file, sizeof(long), SEEK_CUR); } else { while ((getc(file))) {} } key = getc(file); } } void write_index_descriptor(char *path, index_descriptor_t *desc) { cJSON *json = cJSON_CreateObject(); cJSON_AddStringToObject(json, "uuid", desc->uuid); cJSON_AddStringToObject(json, "version", desc->version); cJSON_AddStringToObject(json, "root", desc->root); cJSON_AddStringToObject(json, "name", desc->name); cJSON_AddStringToObject(json, "rewrite_url", desc->rewrite_url); cJSON_AddNumberToObject(json, "timestamp", (double) desc->timestamp); int fd = open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); if (fd == -1) { perror(path); } char *str = cJSON_Print(json); write(fd, str, strlen(str)); free(str); close(fd); cJSON_Delete(json); } index_descriptor_t read_index_descriptor(char *path) { struct stat info; stat(path, &info); int fd = open(path, O_RDONLY); if (fd == -1) { fprintf(stderr, "Invalid/corrupt index (Could not find descriptor)\n"); exit(1); } char *buf = malloc(info.st_size + 1); read(fd, buf, info.st_size); *(buf + info.st_size) = '\0'; close(fd); cJSON *json = cJSON_Parse(buf); index_descriptor_t descriptor; descriptor.timestamp = (long) cJSON_GetObjectItem(json, "timestamp")->valuedouble; strcpy(descriptor.root, cJSON_GetObjectItem(json, "root")->valuestring); strcpy(descriptor.name, cJSON_GetObjectItem(json, "name")->valuestring); strcpy(descriptor.rewrite_url, cJSON_GetObjectItem(json, "rewrite_url")->valuestring); descriptor.root_len = (short) strlen(descriptor.root); strcpy(descriptor.version, cJSON_GetObjectItem(json, "version")->valuestring); strcpy(descriptor.uuid, cJSON_GetObjectItem(json, "uuid")->valuestring); cJSON_Delete(json); free(buf); return descriptor; } char *get_meta_key_text(enum metakey meta_key) { switch (meta_key) { case MetaContent: return "content"; case MetaWidth: return "width"; case MetaHeight: return "height"; case MetaMediaDuration: return "duration"; case MetaMediaAudioCodec: return "audioc"; case MetaMediaVideoCodec: return "videoc"; case MetaMediaBitrate: return "bitrate"; case MetaArtist: return "artist"; case MetaAlbum: return "album"; case MetaAlbumArtist: return "album_artist"; case MetaGenre: return "genre"; case MetaTitle: return "title"; case MetaFontName: return "font_name"; default: return NULL; } } void write_document(document_t *doc) { if (index_fd == -1) { char dstfile[PATH_MAX]; pthread_t self = pthread_self(); snprintf(dstfile, PATH_MAX, "%s_index_%lu", ScanCtx.index.path, self); index_fd = open(dstfile, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR); if (index_fd == -1) { perror("open"); } } dyn_buffer_t buf = dyn_buffer_create(); // Ignore root directory in the file path doc->ext = doc->ext - ScanCtx.index.desc.root_len; doc->base = doc->base - ScanCtx.index.desc.root_len; doc->filepath += ScanCtx.index.desc.root_len; dyn_buffer_write(&buf, doc, sizeof(line_t)); dyn_buffer_write_str(&buf, doc->filepath); meta_line_t *meta = doc->meta_head; while (meta != NULL) { dyn_buffer_write_char(&buf, meta->key); if (IS_META_INT(meta->key)) { dyn_buffer_write_int(&buf, meta->intval); } else if (IS_META_LONG(meta->key)) { dyn_buffer_write_long(&buf, meta->longval); } else { dyn_buffer_write_str(&buf, meta->strval); } meta_line_t *tmp = meta; meta = meta->next; free(tmp); } dyn_buffer_write_char(&buf, '\n'); int res = write(index_fd, buf.buf, buf.cur); if (res == -1) { perror("write"); } ScanCtx.stat_index_size += buf.cur; dyn_buffer_destroy(&buf); } void thread_cleanup() { close(index_fd); } void read_index(const char *path, const char index_id[UUID_STR_LEN], index_func func) { line_t line; dyn_buffer_t buf = dyn_buffer_create(); FILE *file = fopen(path, "rb"); while (1) { buf.cur = 0; fread((void *) &line, 1, sizeof(line_t), file); if (feof(file)) { break; } cJSON *document = cJSON_CreateObject(); cJSON_AddStringToObject(document, "index", index_id); char uuid_str[UUID_STR_LEN]; uuid_unparse(line.uuid, uuid_str); cJSON_AddStringToObject(document, "mime", mime_get_mime_text(line.mime)); cJSON_AddNumberToObject(document, "size", (double) line.size); cJSON_AddNumberToObject(document, "mtime", line.mtime); int c; while ((c = getc(file)) != 0) { dyn_buffer_write_char(&buf, (char) c); } dyn_buffer_write_char(&buf, '\0'); cJSON_AddStringToObject(document, "extension", buf.buf + line.ext); if (*(buf.buf + line.ext - 1) == '.') { *(buf.buf + line.ext - 1) = '\0'; } else { *(buf.buf + line.ext) = '\0'; } cJSON_AddStringToObject(document, "name", buf.buf + line.base); if (line.base > 0) { *(buf.buf + line.base - 1) = '\0'; cJSON_AddStringToObject(document, "path", buf.buf); } else { cJSON_AddStringToObject(document, "path", ""); } enum metakey key = getc(file); while (key != '\n') { switch (key) { case MetaWidth: case MetaHeight: { int value; fread(&value, sizeof(int), 1, file); cJSON_AddNumberToObject(document, get_meta_key_text(key), value); break; } case MetaMediaDuration: case MetaMediaBitrate: { long value; fread(&value, sizeof(long), 1, file); cJSON_AddNumberToObject(document, get_meta_key_text(key), value); break; } case MetaMediaAudioCodec: case MetaMediaVideoCodec: { int value; fread(&value, sizeof(int), 1, file); const AVCodecDescriptor *desc = avcodec_descriptor_get(value); if (desc != NULL) { cJSON_AddStringToObject(document, get_meta_key_text(key), desc->name); } break; } case MetaContent: case MetaArtist: case MetaAlbum: case MetaAlbumArtist: case MetaGenre: case MetaFontName: case MetaTitle: { buf.cur = 0; while ((c = getc(file)) != 0) { if (SHOULD_KEEP_CHAR(c) || c == ' ') { dyn_buffer_write_char(&buf, (char) c); } } dyn_buffer_write_char(&buf, '\0'); cJSON_AddStringToObject(document, get_meta_key_text(key), buf.buf); break; } default: fprintf(stderr, "Invalid meta key (corrupt index): %x\n", key); break; } key = getc(file); } func(document, uuid_str); cJSON_Delete(document); } dyn_buffer_destroy(&buf); fclose(file); } void incremental_read(GHashTable *table, const char *filepath) { FILE *file = fopen(filepath, "rb"); line_t line; while (1) { fread((void *) &line, 1, sizeof(line_t), file); if (feof(file)) { break; } incremental_put(table, line.ino, line.mtime); while ((getc(file))) {} skip_meta(file); } fclose(file); } /** * Copy items from an index that are in the copy_table. Also copies from * the store. */ void incremental_copy(store_t *store, store_t *dst_store, const char *filepath, const char *dst_filepath, GHashTable *copy_table) { FILE *file = fopen(filepath, "rb"); FILE *dst_file = fopen(dst_filepath, "ab"); line_t line; while (1) { fread((void *) &line, 1, sizeof(line_t), file); if (feof(file)) { break; } if (incremental_get(copy_table, line.ino)) { fwrite(&line, sizeof(line), 1, dst_file); size_t buf_len; char *buf = store_read(store, (char *) line.uuid, 16, &buf_len); store_write(dst_store, (char *) line.uuid, 16, buf, buf_len); free(buf); char c; while ((c = (char) getc(file))) { fwrite(&c, sizeof(c), 1, dst_file); } fwrite("\0", sizeof(c), 1, dst_file); enum metakey key; while (1) { key = getc(file); if (key == '\n') { break; } fwrite(&key, sizeof(char), 1, dst_file); if (IS_META_INT(key)) { int val; fread(&val, sizeof(val), 1, file); fwrite(&val, sizeof(val), 1, dst_file); } else if (IS_META_LONG(key)) { long val; fread(&val, sizeof(val), 1, file); fwrite(&val, sizeof(val), 1, dst_file); } else { while ((c = (char) getc(file))) { fwrite(&c, sizeof(c), 1, dst_file); } fwrite("\0", sizeof(c), 1, dst_file); } } } else { skip_meta(file); } } fclose(file); }