diff options
Diffstat (limited to 'src/server/utils.c')
-rw-r--r-- | src/server/utils.c | 766 |
1 files changed, 414 insertions, 352 deletions
diff --git a/src/server/utils.c b/src/server/utils.c index 3fe1900..d6e7a61 100644 --- a/src/server/utils.c +++ b/src/server/utils.c @@ -31,6 +31,12 @@ #include "utils.h" #include "memlog.h" +static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *port); +static char is_valid_namespace(char *namespace); +static char is_valid_imagename(char *namespace); +static void strtolower(char *string); +static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers); + /** * Parse IPv4 or IPv6 address in string representation to a suitable format usable by the BSD socket library * @string eg. "1.2.3.4" or "2a01::10:5", optially with port appended, eg "1.2.3.4:6666" or "2a01::10:5:6666" @@ -65,19 +71,22 @@ static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *po char *portpos = NULL, *ptr = string; while (*ptr) { - if (*ptr == ':') portpos = ptr; + if (*ptr == ':') + portpos = ptr; ++ptr; } - if (portpos == NULL) return 0; // No port in string + if (portpos == NULL) + return 0; // No port in string // Consider IP being surrounded by [ ] - if (*string == '[' && *(portpos-1) == ']') + if (*string == '[' && *(portpos - 1) == ']') { ++string; - *(portpos-1) = '\0'; + *(portpos - 1) = '\0'; } *portpos++ = '\0'; int p = atoi(portpos); - if (p < 1 || p > 65535) return 0; // Invalid port + if (p < 1 || p > 65535) + return 0; // Invalid port *port = htons((uint16_t)p); // Try IPv4 with port @@ -101,30 +110,33 @@ static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *po static char is_valid_namespace(char *namespace) { - if (*namespace == '\0' || *namespace == '/') return 0; // Invalid: Length = 0 or starting with a slash + if (*namespace == '\0' || *namespace == '/') + return 0; // Invalid: Length = 0 or starting with a slash while (*namespace) { - if (*namespace != '/' && *namespace != '-' - && (*namespace < 'a' || *namespace > 'z') - && (*namespace < 'A' || *namespace > 'Z')) return 0; + if (*namespace != '/' && *namespace != '-' && (*namespace < 'a' || *namespace > 'z') + && (*namespace < 'A' || *namespace > 'Z')) + return 0; ++namespace; } - if (*(namespace - 1) == '/') return 0; // Invalid: Ends in a slash + if (*(namespace - 1) == '/') + return 0; // Invalid: Ends in a slash return 1; } static char is_valid_imagename(char *namespace) { - if (*namespace == '\0' || *namespace == ' ') return 0; // Invalid: Length = 0 or starting with a space + if (*namespace == '\0' || *namespace == ' ') + return 0; // Invalid: Length = 0 or starting with a space while (*namespace) { // Check for invalid chars - if (*namespace != '.' && *namespace != '-' && *namespace != ' ' - && *namespace != '(' && *namespace != ')' - && (*namespace < 'a' || *namespace > 'z') - && (*namespace < 'A' || *namespace > 'Z')) return 0; + if (*namespace != '.' && *namespace != '-' && *namespace != ' ' && *namespace != '(' && *namespace != ')' + && (*namespace < 'a' || *namespace > 'z') && (*namespace < 'A' || *namespace > 'Z')) + return 0; ++namespace; } - if (*(namespace - 1) == ' ') return 0; // Invalid: Ends in a space + if (*(namespace - 1) == ' ') + return 0; // Invalid: Ends in a space return 1; } @@ -132,380 +144,430 @@ static void strtolower(char *string) { while (*string) { - if (*string >= 'A' && *string <= 'Z') *string += 32; + if (*string >= 'A' && *string <= 'Z') + *string += 32; ++string; } } -void dnbd3_load_config(char *file) +void dnbd3_load_config() { - int fd; - gint i, j, k; - GKeyFile* gkf; - - gkf = g_key_file_new(); - if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) - { - printf("ERROR: Config file not found: %s\n", file); - exit(EXIT_FAILURE); - } - - char *namespace = g_key_file_get_string(gkf, "settings", "default_namespace", NULL); - if (namespace && !is_valid_namespace(namespace)) - { - memlogf("[ERROR] Ignoring default namespace: '%s' is not a valid namespace", namespace); - g_free(namespace); - namespace = NULL; - } - - gchar **groups = NULL; - gsize section_count; - groups = g_key_file_get_groups(gkf, §ion_count); - - for (i = 0; i < section_count; i++) - { - // Special group - if (strcmp(groups[i], "settings") == 0 || strcmp(groups[i], "trusted") == 0) - { - continue; - } - - // An actual image definition - - if (!is_valid_imagename(groups[i])) - { - memlogf("[ERROR] Invalid image name: '%s'", groups[i]); - continue; - } - - int rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL); - if (rid <= 0) - { - memlogf("[ERROR] Invalid rid '%d' for image '%s'", rid, groups[i]); - continue; - } - - if (strchr(groups[i], '.') == NULL && namespace == NULL) - { - memlogf("[ERROR] Image '%s' has local name and no default namespace is defined; entry ignored.", groups[i]); - continue; - } - - dnbd3_image_t *image = g_new0(dnbd3_image_t, 1); - if (image == NULL) - { - memlogf("[ERROR] Could not allocate dnbd3_image_t while reading config"); - continue; - } - - if (strchr(groups[i], '/') == NULL) - { // Local image, build global name - image->name = calloc(strlen(namespace) + strlen(groups[i]) + 2, sizeof(char)); - sprintf(image->name, "%s/%s", namespace, groups[i]); - } - else - { - image->name = strdup(groups[i]); - } - - if (dnbd3_get_image(image->name, rid, 0)) - { - memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image->name, rid); - free(image->name); - g_free(image); - continue; - } - - image->low_name = strdup(image->name); - strtolower(image->low_name); - - image->rid = rid; - image->file = g_key_file_get_string(gkf, groups[i], "file", NULL); - char relayed = image->file == NULL || *image->file == '\0'; - if (relayed && image->file) - { - g_free(image->file); - image->file = NULL; - } - - if (relayed) // Image is relayed (this server acts as proxy) - { - if (strchr(groups[i], '.') == NULL) - { - memlogf("[ERROR] Relayed image without global name in config: '%s'", groups[i]); - g_free(image); - continue; - } - image->cache_file = g_key_file_get_string(gkf, groups[i], "cache", NULL); - if (image->cache_file && *image->cache_file == '\0') g_free(image->cache_file); - } - else // Image is a local one, open file to get size - { - fd = open(image->file, O_RDONLY); - if (fd > 0) { - image->filesize = lseek(fd, 0, SEEK_END); - if (image->filesize & 4095) { - memlogf("[WARNING] Size of image '%s' is not a multiple of 4096. Last incomplete block will be ignored!", image->file); - image->filesize &= ~(uint64_t)4095; - } - close(fd); - image->working = 1; - } else { - memlogf("[ERROR] Image file not found: '%s'", image->file); - } - } - - // A list of servers that are known to also host or relay this image - gsize num_servers; - gchar **servers = g_key_file_get_string_list(gkf, groups[i], "servers", &num_servers, NULL); - if (servers) for (k = 0, j = 0; j < MIN(num_servers, NUMBER_SERVERS); ++j) - { - if (parse_address(servers[j], &(image->servers[k].hostaddrtype), image->servers[k].hostaddr, &(image->servers[k].port))) - { - ++k; continue; - } - image->servers[k].hostaddrtype = 0; - } - g_strfreev(servers); - - if (image->cache_file) - { - // Determine size of cached image - fd = open(image->cache_file, O_RDONLY); - if (fd > 0) - { - image->filesize = lseek(fd, 0, SEEK_END); - close(fd); - } - if (image->filesize & 4095) - { // Cache files should always be trincated to 4kib boundaries already - memlogf("[WARNING] Size of cache file '%s' is not a multiple of 4096. Something's fishy!", image->cache_file); - image->filesize = 0; - } - else if (image->filesize > 0) - { - const size_t map_len_bytes = (image->filesize + (1 << 15) - 1) >> 15; - image->cache_map = calloc(map_len_bytes, sizeof(uint8_t)); - // read cache map from file - // one byte in the map covers 8 4kib blocks, so 32kib per byte - // "+ (1 << 15) - 1" is required to account for the last bit of - // the image that is smaller than 32kib - // this would be the case whenever the image file size is not a - // multiple of 32kib (= the number of blocks is not dividable by 8) - // ie: if the image is 49152 bytes and you do 49152 >> 15 you get 1, - // but you actually need 2 bytes to have a complete cache map - char tmp[strlen(image->cache_file)+4]; - strcpy(tmp, image->cache_file); - strcat(tmp, ".map"); - fd = open(tmp, O_RDONLY); - if (fd > 0) - { - read(fd, image->cache_map, map_len_bytes * sizeof(uint8_t)); - close(fd); - // If the whole image is cached, mark it as working right away without waiting for an upstream server - image->working = 1; - for (j = 0; j < map_len_bytes - 1; ++j) - { - if (image->cache_map[j] != 0xFF) - { - image->working = 0; - break; - } - } - const int blocks_in_last_byte = (image->filesize >> 12) & 7; - uint8_t last_byte = 0; - if (blocks_in_last_byte == 0) - last_byte = 0xFF; - else - for (j = 0; j < k; ++j) last_byte = (last_byte << 1) | 1; - if ((image->cache_map[map_len_bytes-1] & last_byte) != last_byte) - image->working = 0; - else - memlogf("[INFO] Publishing relayed image '%s' because the local cache copy is complete", image->name); - } + gint i; + GKeyFile* gkf; - /* - // TODO: Do this as soon as a connection to a upstream server is established - // open cache file - fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 1) - memlogf("ERROR: Could't create cache file '%s'", _images[i].cache_file); + if (_local_namespace != NULL || _dnbd3_images != NULL) + { + printf("dnbd3_load_config() called more than once\n\n"); + exit(EXIT_FAILURE); + } - if (_images[i].filesize != lseek(fd, 0, SEEK_END)) - fallocate(fd, 0, 0, _images[i].filesize); + gkf = g_key_file_new(); + if (!g_key_file_load_from_file(gkf, _config_file_name, G_KEY_FILE_NONE, NULL)) + { + printf("ERROR: Config file not found: %s\n", _config_file_name); + exit(EXIT_FAILURE); + } - close(fd); - */ - } - } // end cache_file handling - pthread_spin_lock(&_spinlock); - _dnbd3_images = g_slist_append(_dnbd3_images, image); - pthread_spin_unlock(&_spinlock); - // DONE IMAGE - } - - g_free(namespace); - g_strfreev(groups); - g_key_file_free(gkf); -} + _local_namespace = g_key_file_get_string(gkf, "settings", "default_namespace", NULL); + if (_local_namespace && !is_valid_namespace(_local_namespace)) + { + memlogf("[ERROR] Ignoring default namespace: '%s' is not a valid namespace", _local_namespace); + g_free(_local_namespace); + _local_namespace = NULL; + } -int dnbd3_add_image(dnbd3_image_t *image, char *file) -{ - return ERROR_IMAGE_ALREADY_EXISTS; // TODO: Make it work with image names - /* - FILE* f = fopen(image->file,"r"); - if (f == NULL) + gchar **groups = NULL; + gsize section_count; + groups = g_key_file_get_groups(gkf, §ion_count); + + for (i = 0; i < section_count; i++) { - printf("ERROR: Image file not found: %s\n", image->file); - return ERROR_FILE_NOT_FOUND; + // Special group + if (strcmp(groups[i], "settings") == 0 || strcmp(groups[i], "trusted") == 0) + { + continue; + } + + // An actual image definition + + int rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL); + if (rid <= 0) + { + memlogf("[ERROR] Invalid rid '%d' for image '%s'", rid, groups[i]); + continue; + } + + char *image_file = g_key_file_get_string(gkf, groups[i], "file", NULL); + char *cache_file = g_key_file_get_string(gkf, groups[i], "cache", NULL); + gsize num_servers; + gchar **servers = g_key_file_get_string_list(gkf, groups[i], "servers", &num_servers, NULL); + + pthread_spin_lock(&_spinlock); + dnbd3_image_t *image = prepare_image(groups[i], rid, image_file, cache_file, servers, num_servers); + if (image) + { + _dnbd3_images = g_slist_prepend(_dnbd3_images, image); + } + pthread_spin_unlock(&_spinlock); + + g_free(image_file); + g_free(cache_file); + g_strfreev(servers); } - fclose (f); - dnbd3_image_t* tmp = dnbd3_get_image(image->vid, image->rid); + g_strfreev(groups); + g_key_file_free(gkf); +} + +int dnbd3_add_image(dnbd3_image_t *image) +{ + // Lock here to prevent concurrent add calls to mess rids up. Cannot happen currently + // as IPC clients are not threaded and they're the only place where this is called, + // but better be safe for the future... + pthread_spin_lock(&_spinlock); if (image->rid == 0) - { - if(tmp) - image->rid = tmp->rid +1; + { // TODO: globalize image->name somewhere for this call + const dnbd3_image_t *latest = dnbd3_get_image(image->name, image->rid, 0); + if (latest) + image->rid = latest->rid + 1; else image->rid = 1; - } else if (tmp) + } + + dnbd3_image_t *newimage = prepare_image(image->name, image->rid, image->file, image->cache_file, NULL, 0); + if (newimage) + { + _dnbd3_images = g_slist_prepend(_dnbd3_images, image); + } + else { - printf("ERROR: Image already exists (%d,%d)\n", image->vid, image->rid); - return ERROR_IMAGE_ALREADY_EXISTS; + pthread_spin_unlock(&_spinlock); + return ERROR_SEE_LOG; } - GKeyFile* gkf; - gkf = g_key_file_new(); - if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) - { - printf("ERROR: Config file not found: %s\n", file); - exit(EXIT_FAILURE); - } - - g_key_file_set_integer(gkf, image->group, "vid", image->vid); - g_key_file_set_integer(gkf, image->group, "rid", image->rid); - g_key_file_set_string(gkf, image->group, "file", image->file); - g_key_file_set_string(gkf, image->group, "servers", image->serverss); - g_key_file_set_string(gkf, image->group, "cache", image->cache_file); - - gchar* data = g_key_file_to_data(gkf, NULL, NULL); - - f = fopen(file,"w"); - if (f) - { - fputs((char*) data, f); - fclose(f); - g_free(data); - g_key_file_free(gkf); - return 0; - } - else - { - g_free(data); - g_key_file_free(gkf); - printf("ERROR: Config file is not writable: %s\n", file); - return ERROR_CONFIG_FILE_PERMISSIONS; - } - */ + // Adding image was successful, write config file + GKeyFile* gkf; + gkf = g_key_file_new(); + if (!g_key_file_load_from_file(gkf, _config_file_name, G_KEY_FILE_NONE, NULL)) + { + printf("ERROR: Config file not found: %s\n", _config_file_name); + exit(EXIT_FAILURE); + } + + g_key_file_set_integer(gkf, image->name, "rid", image->rid); + g_key_file_set_string(gkf, image->name, "file", image->file); + //g_key_file_set_string(gkf, image->name, "servers", image->serverss); // TODO: Save servers as string + g_key_file_set_string(gkf, image->name, "cache", image->cache_file); + + gchar* data = g_key_file_to_data(gkf, NULL, NULL); + + int f = fopen(_config_file_name, "w"); + if (f >= 0) + { + fputs((char*) data, f); + fclose(f); + pthread_spin_unlock(&_spinlock); + g_free(data); + g_key_file_free(gkf); + memlogf("[INFO] Added new image '%s' (rid %d)", newimage->name, newimage->rid); + return 0; + } + pthread_spin_unlock(&_spinlock); + g_free(data); + g_key_file_free(gkf); + memlogf("[ERROR] Image added, but config file is not writable (%s)", _config_file_name); + return ERROR_SEE_LOG; } int dnbd3_del_image(dnbd3_image_t *image, char *file) { return ERROR_IMAGE_NOT_FOUND; // TODO: Make it work with image names /* - if (image->rid == 0) - { - printf("ERROR: Delete with rid=0 is not allowed\n"); - return ERROR_RID; - } - - dnbd3_image_t* tmp = dnbd3_get_image(image->vid, image->rid); - if (!tmp) - { - printf("ERROR: Image not found: (%d,%d)\n", image->vid, image->rid); - return ERROR_IMAGE_NOT_FOUND; - } - - GSList *iterator = NULL; - for (iterator = _dnbd3_clients; iterator; iterator = iterator->next) - { - dnbd3_client_t *client = iterator->data; - if (tmp == client->image) - { - printf("ERROR: Delete is not allowed, image is in use (%d,%d)\n", tmp->vid, tmp->rid); - return ERROR_IMAGE_IN_USE; - } - } - - GKeyFile* gkf; - gkf = g_key_file_new(); - if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) - { - printf("ERROR: Config file not found: %s\n", file); - exit(EXIT_FAILURE); - } - - g_key_file_remove_group(gkf, tmp->group, NULL); - gchar* data = g_key_file_to_data(gkf, NULL, NULL); - - FILE* f = fopen(file,"w"); - if (f) - { - fputs((char*) data, f); - fclose(f); - g_free(data); - g_key_file_free(gkf); - // TODO: unlink image file - return 0; - } - else - { - g_free(data); - g_key_file_free(gkf); - printf("ERROR: Config file is not writable: %s\n", file); - return ERROR_CONFIG_FILE_PERMISSIONS; - } - */ + if (image->rid == 0) + { + printf("ERROR: Delete with rid=0 is not allowed\n"); + return ERROR_RID; + } + + dnbd3_image_t* tmp = dnbd3_get_image(image->vid, image->rid); + if (!tmp) + { + printf("ERROR: Image not found: (%d,%d)\n", image->vid, image->rid); + return ERROR_IMAGE_NOT_FOUND; + } + + GSList *iterator = NULL; + for (iterator = _dnbd3_clients; iterator; iterator = iterator->next) + { + dnbd3_client_t *client = iterator->data; + if (tmp == client->image) + { + printf("ERROR: Delete is not allowed, image is in use (%d,%d)\n", tmp->vid, tmp->rid); + return ERROR_IMAGE_IN_USE; + } + } + + GKeyFile* gkf; + gkf = g_key_file_new(); + if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) + { + printf("ERROR: Config file not found: %s\n", file); + exit(EXIT_FAILURE); + } + + g_key_file_remove_group(gkf, tmp->group, NULL); + gchar* data = g_key_file_to_data(gkf, NULL, NULL); + + FILE* f = fopen(file,"w"); + if (f) + { + fputs((char*) data, f); + fclose(f); + g_free(data); + g_key_file_free(gkf); + // TODO: unlink image file + return 0; + } + else + { + g_free(data); + g_key_file_free(gkf); + printf("ERROR: Config file is not writable: %s\n", file); + return ERROR_CONFIG_FILE_PERMISSIONS; + } + */ } dnbd3_image_t* dnbd3_get_image(char *name_orig, int rid, const char do_lock) { - dnbd3_image_t *result = NULL, *image; + dnbd3_image_t *result = NULL, *image; GSList *iterator; char name[strlen(name_orig) + 1]; strcpy(name, name_orig); strtolower(name); - if (do_lock) pthread_spin_lock(&_spinlock); - for (iterator = _dnbd3_images; iterator; iterator = iterator->next) - { - image = iterator->data; - if (rid != 0) // rid was specified - { - if (image->rid == rid && strcmp(name, image->low_name) == 0) - { - result = image; - break; - } - } - else // search max. rid available - { - if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid)) - { - result = image; - } - } - } - if (do_lock) pthread_spin_unlock(&_spinlock); - return result; + if (do_lock) + pthread_spin_lock(&_spinlock); + for (iterator = _dnbd3_images; iterator; iterator = iterator->next) + { + image = iterator->data; + if (rid != 0) // rid was specified + { + if (image->rid == rid && strcmp(name, image->low_name) == 0) + { + result = image; + break; + } + } + else // search max. rid available + { + if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid)) + { + result = image; + } + } + } + if (do_lock) + pthread_spin_unlock(&_spinlock); + return result; } void dnbd3_handle_sigpipe(int signum) { - memlogf("ERROR: SIGPIPE received!\n"); + memlogf("ERROR: SIGPIPE received!\n"); } void dnbd3_handle_sigterm(int signum) { - memlogf("INFO: SIGTERM or SIGINT received!\n"); - dnbd3_cleanup(); + memlogf("INFO: SIGTERM or SIGINT received!\n"); + dnbd3_cleanup(); +} + +/** + * Prepare image to be added to image list. Returns a pointer to a newly allocated image struct + * on success, NULL otherwise. + * Note: This function calls dnbd3_get_image without locking, so make sure you lock + * before calling this function while the server is active. + */ +static dnbd3_image_t *prepare_image(char *image_name, int rid, char *image_file, char *cache_file, gchar **servers, gsize num_servers) +{ + int j, k; + if (image_name == NULL) + { + memlogf("[ERROR] Null Image-Name"); + return NULL; + } + if (!is_valid_imagename(image_name)) + { + memlogf("[ERROR] Invalid image name: '%s'", image_name); + return NULL; + } + + if (strchr(image_name, '.') == NULL && _local_namespace == NULL) + { + memlogf("[ERROR] Image '%s' has local name and no default namespace is defined; entry ignored.", image_name); + return NULL; + } + + // Allocate image struct and zero it out by using g_new0 + dnbd3_image_t *image = g_new0(dnbd3_image_t, 1); + if (image == NULL) + { + memlogf("[ERROR] Could not allocate dnbd3_image_t while reading config"); + return NULL; + } + + if (strchr(image_name, '/') == NULL) + { // Local image, build global name + image->name = calloc(strlen(_local_namespace) + strlen(image_name) + 2, sizeof(char)); + sprintf(image->name, "%s/%s", _local_namespace, image_name); + } + else + { + image->name = strdup(image_name); + } + + if (dnbd3_get_image(image->name, rid, 0)) + { + memlogf("[ERROR] Duplicate image in config: '%s' rid:%d", image->name, rid); + goto error; + } + + image->low_name = strdup(image->name); + strtolower(image->low_name); + + image->rid = rid; + const char relayed = (image_file == NULL || image_file == '\0'); + + if (relayed) // Image is relayed (this server acts as proxy) + { + if (strchr(image_name, '/') == NULL) + { + memlogf("[ERROR] Relayed image without global name in config: '%s'", image_name); + goto error; + } + if (cache_file && *cache_file) + image->cache_file = strdup(cache_file); + } + else // Image is a local one, open file to get size + { + image->file = strdup(image_file); + int fd = open(image->file, O_RDONLY); + if (fd < 0) + { + memlogf("[ERROR] Image file not found: '%s'", image->file); + goto error; + } + const off_t size = lseek(fd, 0, SEEK_END); + if (size <= 0) + { + memlogf("[ERROR] File '%s' of image '%s' has size '%lld'. Image ignored.", image->file, image->name, (long long)size); + goto error; + } + image->filesize = (uint64_t)size; + if (image->filesize & 4095) + { + memlogf("[WARNING] Size of image '%s' is not a multiple of 4096. Last incomplete block will be ignored!", + image->file); + image->filesize &= ~(uint64_t)4095; + } + close(fd); + image->working = 1; + } + + // A list of servers that are known to also host or relay this image + if (servers) + for (k = 0, j = 0; j < MIN(num_servers, NUMBER_SERVERS); ++j) + { + if (parse_address(servers[j], &(image->servers[k].hostaddrtype), image->servers[k].hostaddr, + &(image->servers[k].port))) + { + ++k; + continue; + } + image->servers[k].hostaddrtype = 0; + } + + if (image->cache_file) + { + // Determine size of cached image + int fd = open(image->cache_file, O_RDONLY); + if (fd >= 0) + { + const off_t size = lseek(fd, 0, SEEK_END); + if (size > 0) + image->filesize = (uint64_t)size; + close(fd); + } + if (image->filesize & 4095) + { // Cache files should always be truncated to 4kib boundaries already + memlogf("[WARNING] Size of cache file '%s' is not a multiple of 4096. Something's fishy!", image->cache_file); + image->filesize = 0; + } + else if (image->filesize > 0) + { + const size_t map_len_bytes = (image->filesize + (1 << 15) - 1) >> 15; + image->cache_map = calloc(map_len_bytes, sizeof(uint8_t)); + // read cache map from file + // one byte in the map covers 8 4kib blocks, so 32kib per byte + // "+ (1 << 15) - 1" is required to account for the last bit of + // the image that is smaller than 32kib + // this would be the case whenever the image file size is not a + // multiple of 32kib (= the number of blocks is not dividable by 8) + // ie: if the image is 49152 bytes and you do 49152 >> 15 you get 1, + // but you actually need 2 bytes to have a complete cache map + char tmp[strlen(image->cache_file) + 5]; + strcpy(tmp, image->cache_file); + strcat(tmp, ".map"); + fd = open(tmp, O_RDONLY); // TODO: Check if map file has expected size + if (fd >= 0) + { + read(fd, image->cache_map, map_len_bytes * sizeof(uint8_t)); + close(fd); + // If the whole image is cached, mark it as working right away without waiting for an upstream server + image->working = 1; + for (j = 0; j < map_len_bytes - 1; ++j) + { + if (image->cache_map[j] != 0xFF) + { + image->working = 0; + break; + } + } + const int blocks_in_last_byte = (image->filesize >> 12) & 7; + uint8_t last_byte = 0; + if (blocks_in_last_byte == 0) + last_byte = 0xFF; + else + for (j = 0; j < blocks_in_last_byte; ++j) + last_byte = (last_byte << 1) | 1; + if ((image->cache_map[map_len_bytes - 1] & last_byte) != last_byte) + image->working = 0; + else + memlogf("[INFO] Instantly publishing relayed image '%s' because the local cache copy is complete", image->name); + } + + /* + // TODO: Do this as soon as a connection to a upstream server is established + // open cache file + fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 1) + memlogf("ERROR: Could't create cache file '%s'", _images[i].cache_file); + + if (_images[i].filesize != lseek(fd, 0, SEEK_END)) + fallocate(fd, 0, 0, _images[i].filesize); + + close(fd); + */ + } + } // end cache_file handling + return image; +error: + // Free stuff. Some pointers might be zero, but calling free() on those is safe. + free(image->cache_map); + free(image->name); + free(image->low_name); + free(image->file); + free(image->cache_file); + g_free(image); + return NULL; } |