summaryrefslogtreecommitdiffstats
path: root/src/server/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/utils.c')
-rw-r--r--src/server/utils.c420
1 files changed, 320 insertions, 100 deletions
diff --git a/src/server/utils.c b/src/server/utils.c
index 0be1569..15e51f8 100644
--- a/src/server/utils.c
+++ b/src/server/utils.c
@@ -23,14 +23,124 @@
#include <sys/stat.h>
#include <pthread.h>
#include <string.h>
+#include <glib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include "server.h"
#include "utils.h"
+#include "memlog.h"
+
+/**
+ * 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"
+ * @af will contain either AF_INET or AF_INET6
+ * @addr will contain the address in network representation
+ * @port will contain the port in network representation, defaulting to #define PORT if none was given
+ * returns 1 on success, 0 in failure. contents of af, addr and port are undefined in the latter case
+ */
+static char parse_address(char *string, uint8_t *af, uint8_t *addr, uint16_t *port)
+{
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ // Try IPv4 without port
+ if (1 == inet_pton(AF_INET, string, &v4))
+ {
+ *af = AF_INET;
+ memcpy(addr, &v4, 4);
+ *port = htons(PORT);
+ return 1;
+ }
+ // Try IPv6 without port
+ if (1 == inet_pton(AF_INET6, string, &v6))
+ {
+ *af = AF_INET6;
+ memcpy(addr, &v6, 16);
+ *port = htons(PORT);
+ return 1;
+ }
+
+ // Scan for port
+ char *portpos = NULL, *ptr = string;
+ while (*ptr)
+ {
+ if (*ptr == ':') portpos = ptr;
+ ++ptr;
+ }
+ if (portpos == NULL) return 0; // No port in string
+ // Consider IP being surrounded by [ ]
+ if (*string == '[' && *(portpos-1) == ']')
+ {
+ ++string;
+ *(portpos-1) = '\0';
+ }
+ *portpos++ = '\0';
+ int p = atoi(portpos);
+ if (p < 1 || p > 65535) return 0; // Invalid port
+ *port = htons((uint16_t)p);
+
+ // Try IPv4 with port
+ if (1 == inet_pton(AF_INET, string, &v4))
+ {
+ *af = AF_INET;
+ memcpy(addr, &v4, 4);
+ return 1;
+ }
+ // Try IPv6 with port
+ if (1 == inet_pton(AF_INET6, string, &v6))
+ {
+ *af = AF_INET6;
+ memcpy(addr, &v6, 16);
+ return 1;
+ }
+
+ // FAIL
+ return 0;
+}
+
+static char is_valid_namespace(char *namespace)
+{
+ 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;
+ ++namespace;
+ }
+ 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
+ while (*namespace)
+ { // Check for invalid chars
+ 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
+ return 1;
+}
+
+static void strtolower(char *string)
+{
+ while (*string)
+ {
+ if (*string >= 'A' && *string <= 'Z') *string += 32;
+ ++string;
+ }
+}
void dnbd3_load_config(char *file)
{
int fd;
- gint i;
+ gint i, j, k;
GKeyFile* gkf;
gkf = g_key_file_new();
@@ -40,111 +150,209 @@ void dnbd3_load_config(char *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;
- groups = g_key_file_get_groups(gkf, &_num_images);
- _images = calloc(_num_images, sizeof(dnbd3_image_t));
+ gsize section_count;
+ groups = g_key_file_get_groups(gkf, &section_count);
- for (i = 0; i < _num_images; i++)
+ for (i = 0; i < section_count; i++)
{
- _images[i].group = malloc(strlen(groups[i]));
- strcpy(_images[i].group, groups[i]);
- _images[i].file = g_key_file_get_string(gkf, groups[i], "file", NULL);
- _images[i].servers = g_key_file_get_string_list(gkf, groups[i], "servers", &_images[i].num_servers, NULL);
- _images[i].serverss = g_key_file_get_string(gkf, groups[i], "servers", NULL);
- _images[i].vid = g_key_file_get_integer(gkf, groups[i], "vid", NULL);
- _images[i].rid = g_key_file_get_integer(gkf, groups[i], "rid", NULL);
- _images[i].cache_file = g_key_file_get_string(gkf, groups[i], "cache", NULL);
- _images[i].atime = 0;
-
- if (_images[i].num_servers > NUMBER_SERVERS)
- printf("WARN: Max allowed servers %i\n", NUMBER_SERVERS);
-
- fd = open(_images[i].file, O_RDONLY);
- if (fd > 0)
- _images[i].filesize = lseek(fd, 0, SEEK_END);
+ // 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
- printf("ERROR: Image file not found: %s\n", _images[i].file);
-
- close(fd);
-
- if (_images[i].cache_file)
{
- // read cache map from file
- _images[i].cache_map = calloc(_images[i].filesize >> 15, sizeof(char));
- memset(_images[i].cache_map, 0, (_images[i].filesize >> 15) * sizeof(char));
- char tmp[strlen(_images[i].cache_file)+4];
- strcpy(tmp, _images[i].cache_file);
- strcat(tmp, ".map");
- fd = open(tmp, O_RDONLY);
- if (fd > 0)
- read(fd, _images[i].cache_map, (_images[i].filesize >> 15) * sizeof(char));
- close(fd);
-
- // open cache file
- fd = open(_images[i].cache_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (fd < 1)
- printf("ERROR: Could't create cache file\n");
-
- if (_images[i].filesize != lseek(fd, 0, SEEK_END))
- fallocate(fd, 0, 0, _images[i].filesize);
-
- close(fd);
+ image->name = strdup(groups[i]);
}
- }
- g_strfreev(groups);
- g_key_file_free(gkf);
-}
-
-void dnbd3_reload_config(char* config_file_name)
-{
- int i, fd;
- pthread_spin_lock(&_spinlock);
- GSList *iterator = NULL;
- for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
- {
- dnbd3_client_t *client = iterator->data;
- pthread_spin_lock(&client->spinlock);
- client->image = NULL;
- }
-
- for (i = 0; i < _num_images; i++)
- {
- // save cache maps
- if (_images[i].cache_file)
+ 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
{
- char tmp[strlen(_images[i].cache_file)+4];
- strcpy(tmp, _images[i].cache_file);
- strcat(tmp, ".map");
- fd = open(tmp, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
-
- if (fd > 0)
- write(fd, _images[i].cache_map, (_images[i].filesize >> 15) * sizeof(char));
+ 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);
+ }
+ }
- close(fd);
+ // 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].addrtype), image->servers[k].ipaddr, &(image->servers[k].port)))
+ {
+ ++k; continue;
+ }
+ image->servers[k].addrtype = 0;
}
+ g_strfreev(servers);
- free(_images[i].group);
- free(_images[i].file);
- free(_images[i].servers);
- free(_images[i].serverss);
- free(_images[i].cache_file);
- free(_images[i].cache_map);
+ 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);
+ }
+
+ /*
+ // 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
+ pthread_spin_lock(&_spinlock);
+ _dnbd3_images = g_slist_append(_dnbd3_images, image);
+ pthread_spin_unlock(&_spinlock);
+ // DONE IMAGE
}
- _num_images = 0;
- free(_images);
- dnbd3_load_config(config_file_name);
- for (iterator = _dnbd3_clients; iterator; iterator = iterator->next)
- {
- dnbd3_client_t *client = iterator->data;
- pthread_spin_unlock(&client->spinlock);
- }
- pthread_spin_unlock(&_spinlock);
+ g_free(namespace);
+ g_strfreev(groups);
+ g_key_file_free(gkf);
}
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)
{
@@ -198,10 +406,13 @@ int dnbd3_add_image(dnbd3_image_t *image, char *file)
printf("ERROR: Config file is not writable: %s\n", file);
return ERROR_CONFIG_FILE_PERMISSIONS;
}
+ */
}
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");
@@ -254,38 +465,47 @@ int dnbd3_del_image(dnbd3_image_t *image, char *file)
printf("ERROR: Config file is not writable: %s\n", file);
return ERROR_CONFIG_FILE_PERMISSIONS;
}
+ */
}
-dnbd3_image_t* dnbd3_get_image(int vid, int rid)
+dnbd3_image_t* dnbd3_get_image(char *name_orig, int rid, const char do_lock)
{
- int i, max = 0;
- dnbd3_image_t *result = NULL;
- for (i = 0; i < _num_images; ++i)
+ 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 (_images[i].vid == vid && _images[i].rid == rid)
- result = &_images[i];
+ if (image->rid == rid && strcmp(name, image->low_name) == 0)
+ {
+ result = image;
+ break;
+ }
}
else // search max. rid available
{
- if (_images[i].vid == vid && _images[i].rid > max)
+ if (strcmp(name, image->low_name) == 0 && (result == NULL || result->rid < image->rid))
{
- result = &_images[i];
- max = _images[i].rid;
+ result = image;
}
}
}
+ if (do_lock) pthread_spin_unlock(&_spinlock);
return result;
}
void dnbd3_handle_sigpipe(int signum)
{
- printf("ERROR: SIGPIPE received!\n");
+ memlogf("ERROR: SIGPIPE received!\n");
}
void dnbd3_handle_sigterm(int signum)
{
- printf("INFO: SIGTERM or SIGINT received!\n");
+ memlogf("INFO: SIGTERM or SIGINT received!\n");
dnbd3_cleanup();
}