diff --git a/.gitignore b/.gitignore index 421f80b..0604f1e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ doc # Editor related *.kate-swp .vscode +*.c~ +*.h~ diff --git a/src/README.md b/src/README.md index 1b3dd2d..d7e2de6 100644 --- a/src/README.md +++ b/src/README.md @@ -3,4 +3,4 @@ - Static global variables: lower case letters - Function names: TypeName_verb or verb_noun - Type names: camel case with the first letter capitalised, e.g. CamelCase -- Indentation style: ``indent -linux $SOURCE_FILES`` +- Indentation style: ``indent -kr -nut *.c *.h`` diff --git a/src/cache.c b/src/cache.c index 2ca7ae7..fbdf92c 100644 --- a/src/cache.c +++ b/src/cache.c @@ -37,109 +37,107 @@ static char *DATA_DIR; */ static char *CacheSystem_calc_dir(const char *url) { - char *xdg_cache_home = getenv("XDG_CACHE_HOME"); - if (!xdg_cache_home) { - char *home = getenv("HOME"); - char *xdg_cache_home_default = "/.cache"; - xdg_cache_home = path_append(home, xdg_cache_home_default); - } - if (mkdir - (xdg_cache_home, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - char *cache_dir_root = path_append(xdg_cache_home, "/httpdirfs/"); - if (mkdir - (cache_dir_root, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } + char *xdg_cache_home = getenv("XDG_CACHE_HOME"); + if (!xdg_cache_home) { + char *home = getenv("HOME"); + char *xdg_cache_home_default = "/.cache"; + xdg_cache_home = path_append(home, xdg_cache_home_default); + } + if (mkdir + (xdg_cache_home, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + char *cache_dir_root = path_append(xdg_cache_home, "/httpdirfs/"); + if (mkdir + (cache_dir_root, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } - char *fn = path_append(cache_dir_root, "/CACHEDIR.TAG"); - FILE *fp = fopen(fn, "w"); - if (fp) { - fprintf(fp, "Signature: 8a477f597d28d172789f06886806bc55\n\ + char *fn = path_append(cache_dir_root, "/CACHEDIR.TAG"); + FILE *fp = fopen(fn, "w"); + if (fp) { + fprintf(fp, "Signature: 8a477f597d28d172789f06886806bc55\n\ # This file is a cache directory tag created by httpdirfs.\n\ # For information about cache directory tags, see:\n\ # http://www.brynosaurus.com/cachedir/\n"); - } else { - lprintf(fatal, "fopen(%s): %s", fn, strerror(errno)); - } - if (ferror(fp)) { - lprintf(fatal, "fwrite(): encountered error!\n"); - } - if (fclose(fp)) { - lprintf(fatal, "fclose(%s): %s\n", fn, strerror(errno)); - } - CURL *c = curl_easy_init(); - char *escaped_url = curl_easy_escape(c, url, 0); - char *full_path = path_append(cache_dir_root, escaped_url); - if (mkdir(full_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(fn); - FREE(cache_dir_root); - curl_free(escaped_url); - curl_easy_cleanup(c); - return full_path; + } else { + lprintf(fatal, "fopen(%s): %s", fn, strerror(errno)); + } + if (ferror(fp)) { + lprintf(fatal, "fwrite(): encountered error!\n"); + } + if (fclose(fp)) { + lprintf(fatal, "fclose(%s): %s\n", fn, strerror(errno)); + } + CURL *c = curl_easy_init(); + char *escaped_url = curl_easy_escape(c, url, 0); + char *full_path = path_append(cache_dir_root, escaped_url); + if (mkdir(full_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + FREE(fn); + FREE(cache_dir_root); + curl_free(escaped_url); + curl_easy_cleanup(c); + return full_path; } void CacheSystem_init(const char *path, int url_supplied) { - if (pthread_mutex_init(&cf_lock, NULL)) { - lprintf(fatal, "cf_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf_lock, NULL)) { + lprintf(fatal, "cf_lock initialisation failed!\n"); + } - if (url_supplied) { - path = CacheSystem_calc_dir(path); - } + if (url_supplied) { + path = CacheSystem_calc_dir(path); + } - lprintf(debug, "directory: %s\n", path); + lprintf(debug, "directory: %s\n", path); - META_DIR = path_append(path, "meta/"); - DATA_DIR = path_append(path, "data/"); + META_DIR = path_append(path, "meta/"); + DATA_DIR = path_append(path, "data/"); + /* + * Check if directories exist, if not, create them + */ + if (mkdir(META_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + + if (mkdir(DATA_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + + if (CONFIG.mode == SONIC) { + char *sonic_path; /* - * Check if directories exist, if not, create them + * Create "rest" sub-directory for META_DIR */ - if (mkdir(META_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + sonic_path = path_append(META_DIR, "rest/"); + if (mkdir + (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } + FREE(sonic_path); - if (mkdir(DATA_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + /* + * Create "rest" sub-directory for DATA_DIR + */ + sonic_path = path_append(DATA_DIR, "rest/"); + if (mkdir + (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } + FREE(sonic_path); + } - if (CONFIG.mode == SONIC) { - char *sonic_path; - /* - * Create "rest" sub-directory for META_DIR - */ - sonic_path = path_append(META_DIR, "rest/"); - if (mkdir - (sonic_path, - S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(sonic_path); - - /* - * Create "rest" sub-directory for DATA_DIR - */ - sonic_path = path_append(DATA_DIR, "rest/"); - if (mkdir - (sonic_path, - S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(sonic_path); - } - - CACHE_SYSTEM_INIT = 1; + CACHE_SYSTEM_INIT = 1; } /** @@ -148,89 +146,89 @@ void CacheSystem_init(const char *path, int url_supplied) */ static int Meta_read(Cache * cf) { - FILE *fp = cf->mfp; - rewind(fp); + FILE *fp = cf->mfp; + rewind(fp); - int nmemb = 0; + int nmemb = 0; - if (!fp) { - /* - * The metadata file does not exist - */ - lprintf(error, "fopen(): %s\n", strerror(errno)); - return EIO; + if (!fp) { + /* + * The metadata file does not exist + */ + lprintf(error, "fopen(): %s\n", strerror(errno)); + return EIO; + } + + fread(&cf->time, sizeof(long), 1, fp); + fread(&cf->content_length, sizeof(off_t), 1, fp); + fread(&cf->blksz, sizeof(int), 1, fp); + fread(&cf->segbc, sizeof(long), 1, fp); + + /* + * Error checking for fread + */ + if (ferror(fp)) { + lprintf(error, "error reading core metadata!\n"); + return EIO; + } + + if (!cf->blksz) { + lprintf(error, + "corrupt metadata: %s, blksz: %d", cf->path, cf->blksz); + return EBADMSG; + } + + if (cf->blksz != CONFIG.data_blksz) { + lprintf(warning, "Warning: cf->blksz != CONFIG.data_blksz\n"); + } + + if (cf->segbc > CONFIG.max_segbc) { + lprintf(error, "Error: segbc: %ld\n", cf->segbc); + return EFBIG; + } + + if (cf->segbc > 0) { + /* + * Allocate memory for all segments, and read them in + */ + cf->seg = CALLOC(cf->segbc, sizeof(Seg)); + nmemb = fread(cf->seg, sizeof(Seg), cf->segbc, fp); + + /* + * We shouldn't have gone past the end of the file + */ + if (feof(fp)) { + /* + * reached EOF + */ + lprintf(error, "attempted to read past the end of the \ +file!\n"); + return EBADMSG; } - fread(&cf->time, sizeof(long), 1, fp); - fread(&cf->content_length, sizeof(off_t), 1, fp); - fread(&cf->blksz, sizeof(int), 1, fp); - fread(&cf->segbc, sizeof(long), 1, fp); - /* * Error checking for fread */ if (ferror(fp)) { - lprintf(error, "error reading core metadata!\n"); - return EIO; + lprintf(error, "error reading bitmap!\n"); + return EIO; } - if (!cf->blksz) { - lprintf(error, - "corrupt metadata: %s, blksz: %d", cf->path, cf->blksz); - return EBADMSG; + /* + * Check for inconsistent metadata file + */ + if (nmemb != cf->segbc) { + lprintf(error, "corrupted metadata!\n"); + return EBADMSG; } + } else { + /* + * Allocate one single segment for empty file to prevent segfault + */ + cf->seg = CALLOC(1, sizeof(Seg)); + } - if (cf->blksz != CONFIG.data_blksz) { - lprintf(warning, "Warning: cf->blksz != CONFIG.data_blksz\n"); - } - - if (cf->segbc > CONFIG.max_segbc) { - lprintf(error, "Error: segbc: %ld\n", cf->segbc); - return EFBIG; - } - - if (cf->segbc > 0) { - /* - * Allocate memory for all segments, and read them in - */ - cf->seg = CALLOC(cf->segbc, sizeof(Seg)); - nmemb = fread(cf->seg, sizeof(Seg), cf->segbc, fp); - - /* - * We shouldn't have gone past the end of the file - */ - if (feof(fp)) { - /* - * reached EOF - */ - lprintf(error, "attempted to read past the end of the \ -file!\n"); - return EBADMSG; - } - - /* - * Error checking for fread - */ - if (ferror(fp)) { - lprintf(error, "error reading bitmap!\n"); - return EIO; - } - - /* - * Check for inconsistent metadata file - */ - if (nmemb != cf->segbc) { - lprintf(error, "corrupted metadata!\n"); - return EBADMSG; - } - } else { - /* - * Allocate one single segment for empty file to prevent segfault - */ - cf->seg = CALLOC(1, sizeof(Seg)); - } - - return 0; + return 0; } /** @@ -241,43 +239,42 @@ file!\n"); */ static int Meta_write(Cache * cf) { - FILE *fp = cf->mfp; - rewind(fp); - - if (!fp) { - /* - * Cannot create the metadata file - */ - lprintf(error, "fopen(): %s\n", strerror(errno)); - return -1; - } + FILE *fp = cf->mfp; + rewind(fp); + if (!fp) { /* - * These things really should not be zero!!! + * Cannot create the metadata file */ - if (!cf->content_length || !cf->blksz || !cf->segbc) { - lprintf(error, - "Warning: content_length: %ld, blksz: %d, segbc: \ + lprintf(error, "fopen(): %s\n", strerror(errno)); + return -1; + } + + /* + * These things really should not be zero!!! + */ + if (!cf->content_length || !cf->blksz || !cf->segbc) { + lprintf(error, "Warning: content_length: %ld, blksz: %d, segbc: \ %ld\n", cf->content_length, cf->blksz, cf->segbc); - } + } - fwrite(&cf->time, sizeof(long), 1, fp); - fwrite(&cf->content_length, sizeof(off_t), 1, fp); - fwrite(&cf->blksz, sizeof(int), 1, fp); - fwrite(&cf->segbc, sizeof(long), 1, fp); - if (cf->segbc > 0) { - fwrite(cf->seg, sizeof(Seg), cf->segbc, fp); - } + fwrite(&cf->time, sizeof(long), 1, fp); + fwrite(&cf->content_length, sizeof(off_t), 1, fp); + fwrite(&cf->blksz, sizeof(int), 1, fp); + fwrite(&cf->segbc, sizeof(long), 1, fp); + if (cf->segbc > 0) { + fwrite(cf->seg, sizeof(Seg), cf->segbc, fp); + } - /* - * Error checking for fwrite - */ - if (ferror(fp)) { - lprintf(error, "fwrite(): encountered error!\n"); - return -1; - } + /* + * Error checking for fwrite + */ + if (ferror(fp)) { + lprintf(error, "fwrite(): encountered error!\n"); + return -1; + } - return 0; + return 0; } /** @@ -287,22 +284,22 @@ static int Meta_write(Cache * cf) */ static void Data_create(Cache * cf) { - int fd; - int mode; + int fd; + int mode; - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - char *datafn = path_append(DATA_DIR, cf->path); - fd = open(datafn, O_WRONLY | O_CREAT, mode); - FREE(datafn); - if (fd == -1) { - lprintf(fatal, "open(): %s\n", strerror(errno)); - } - if (ftruncate(fd, cf->content_length)) { - lprintf(warning, "ftruncate(): %s\n", strerror(errno)); - } - if (close(fd)) { - lprintf(fatal, "close:(): %s\n", strerror(errno)); - } + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + char *datafn = path_append(DATA_DIR, cf->path); + fd = open(datafn, O_WRONLY | O_CREAT, mode); + FREE(datafn); + if (fd == -1) { + lprintf(fatal, "open(): %s\n", strerror(errno)); + } + if (ftruncate(fd, cf->content_length)) { + lprintf(warning, "ftruncate(): %s\n", strerror(errno)); + } + if (close(fd)) { + lprintf(fatal, "close:(): %s\n", strerror(errno)); + } } /** @@ -311,15 +308,15 @@ static void Data_create(Cache * cf) */ static long Data_size(const char *fn) { - char *datafn = path_append(DATA_DIR, fn); - struct stat st; - int s = stat(datafn, &st); - FREE(datafn); - if (!s) { - return st.st_size; - } - lprintf(error, "stat(): %s\n", strerror(errno)); - return -1; + char *datafn = path_append(DATA_DIR, fn); + struct stat st; + int s = stat(datafn, &st); + FREE(datafn); + if (!s) { + return st.st_size; + } + lprintf(error, "stat(): %s\n", strerror(errno)); + return -1; } /** @@ -334,65 +331,63 @@ static long Data_size(const char *fn) */ static long Data_read(Cache * cf, uint8_t * buf, off_t len, off_t offset) { - if (len == 0) { - lprintf(error, "requested to read 0 byte!\n"); - return -EINVAL; - } + if (len == 0) { + lprintf(error, "requested to read 0 byte!\n"); + return -EINVAL; + } - lprintf(cache_lock_debug, - "thread %x: locking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->seek_lock); + lprintf(cache_lock_debug, + "thread %x: locking seek_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->seek_lock); - long byte_read = 0; + long byte_read = 0; + /* + * Seek to the right location + */ + if (fseeko(cf->dfp, offset, SEEK_SET)) { /* - * Seek to the right location + * fseeko failed */ - if (fseeko(cf->dfp, offset, SEEK_SET)) { - /* - * fseeko failed - */ - lprintf(error, "fseeko(): %s\n", strerror(errno)); - byte_read = -EIO; - goto end; + lprintf(error, "fseeko(): %s\n", strerror(errno)); + byte_read = -EIO; + goto end; + } + + /* + * Calculate how much to read + */ + if (offset + len > cf->content_length) { + len -= offset + len - cf->content_length; + if (len < 0) { + goto end; } + } - /* - * Calculate how much to read - */ - if (offset + len > cf->content_length) { - len -= offset + len - cf->content_length; - if (len < 0) { - goto end; - } + byte_read = fread(buf, sizeof(uint8_t), len, cf->dfp); + if (byte_read != len) { + lprintf(debug, + "fread(): requested %ld, returned %ld!\n", len, byte_read); + if (feof(cf->dfp)) { + /* + * reached EOF + */ + lprintf(error, "fread(): reached the end of the file!\n"); } - - byte_read = fread(buf, sizeof(uint8_t), len, cf->dfp); - if (byte_read != len) { - lprintf(debug, - "fread(): requested %ld, returned %ld!\n", - len, byte_read); - if (feof(cf->dfp)) { - /* - * reached EOF - */ - lprintf(error, - "fread(): reached the end of the file!\n"); - } - if (ferror(cf->dfp)) { - /* - * filesystem error - */ - lprintf(error, "fread(): encountered error!\n"); - } + if (ferror(cf->dfp)) { + /* + * filesystem error + */ + lprintf(error, "fread(): encountered error!\n"); } + } - end: + end: - lprintf(cache_lock_debug, - "thread %x: unlocking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); - return byte_read; + lprintf(cache_lock_debug, + "thread %x: unlocking seek_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); + return byte_read; } /** @@ -405,71 +400,72 @@ static long Data_read(Cache * cf, uint8_t * buf, off_t len, off_t offset) * - -1 when the data file does not exist * - otherwise, the number of bytes written. */ -static long Data_write(Cache * cf, const uint8_t * buf, off_t len, off_t offset) +static long Data_write(Cache * cf, const uint8_t * buf, off_t len, + off_t offset) { - if (len == 0) { - /* - * We should permit empty files - */ - return 0; - } + if (len == 0) { + /* + * We should permit empty files + */ + return 0; + } - lprintf(cache_lock_debug, - "thread %x: locking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->seek_lock); + lprintf(cache_lock_debug, + "thread %x: locking seek_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->seek_lock); - long byte_written = 0; + long byte_written = 0; - if (fseeko(cf->dfp, offset, SEEK_SET)) { - /* - * fseeko failed - */ - lprintf(error, "fseeko(): %s\n", strerror(errno)); - byte_written = -EIO; - goto end; - } + if (fseeko(cf->dfp, offset, SEEK_SET)) { + /* + * fseeko failed + */ + lprintf(error, "fseeko(): %s\n", strerror(errno)); + byte_written = -EIO; + goto end; + } - byte_written = fwrite(buf, sizeof(uint8_t), len, cf->dfp); + byte_written = fwrite(buf, sizeof(uint8_t), len, cf->dfp); - if (byte_written != len) { - lprintf(error, - "fwrite(): requested %ld, returned %ld!\n", - len, byte_written); - } + if (byte_written != len) { + lprintf(error, + "fwrite(): requested %ld, returned %ld!\n", + len, byte_written); + } - if (ferror(cf->dfp)) { - /* - * filesystem error - */ - lprintf(error, "fwrite(): encountered error!\n"); - } + if (ferror(cf->dfp)) { + /* + * filesystem error + */ + lprintf(error, "fwrite(): encountered error!\n"); + } - end: - lprintf(cache_lock_debug, - "thread %x: unlocking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); - return byte_written; + end: + lprintf(cache_lock_debug, + "thread %x: unlocking seek_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); + return byte_written; } int CacheDir_create(const char *dirn) { - char *metadirn = path_append(META_DIR, dirn); - char *datadirn = path_append(DATA_DIR, dirn); - int i; + char *metadirn = path_append(META_DIR, dirn); + char *datadirn = path_append(DATA_DIR, dirn); + int i; - i = -mkdir(metadirn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if (i && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } + i = -mkdir(metadirn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (i && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } - i |= -mkdir(datadirn, - S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) << 1; - if (i && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(datadirn); - FREE(metadirn); - return -i; + i |= -mkdir(datadirn, + S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) << 1; + if (i && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + FREE(datadirn); + FREE(metadirn); + return -i; } /** @@ -477,30 +473,30 @@ int CacheDir_create(const char *dirn) */ static Cache *Cache_alloc() { - Cache *cf = CALLOC(1, sizeof(Cache)); + Cache *cf = CALLOC(1, sizeof(Cache)); - if (pthread_mutex_init(&cf->seek_lock, NULL)) { - lprintf(fatal, "seek_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf->seek_lock, NULL)) { + lprintf(fatal, "seek_lock initialisation failed!\n"); + } - if (pthread_mutex_init(&cf->w_lock, NULL)) { - lprintf(fatal, "w_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf->w_lock, NULL)) { + lprintf(fatal, "w_lock initialisation failed!\n"); + } - if (pthread_mutexattr_init(&cf->bgt_lock_attr)) { - lprintf(fatal, "bgt_lock_attr initialisation failed!\n"); - } + if (pthread_mutexattr_init(&cf->bgt_lock_attr)) { + lprintf(fatal, "bgt_lock_attr initialisation failed!\n"); + } - if (pthread_mutexattr_setpshared(&cf->bgt_lock_attr, - PTHREAD_PROCESS_SHARED)) { - lprintf(fatal, "could not set bgt_lock_attr!\n"); - } + if (pthread_mutexattr_setpshared(&cf->bgt_lock_attr, + PTHREAD_PROCESS_SHARED)) { + lprintf(fatal, "could not set bgt_lock_attr!\n"); + } - if (pthread_mutex_init(&cf->bgt_lock, &cf->bgt_lock_attr)) { - lprintf(fatal, "bgt_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf->bgt_lock, &cf->bgt_lock_attr)) { + lprintf(fatal, "bgt_lock initialisation failed!\n"); + } - return cf; + return cf; } /** @@ -508,35 +504,35 @@ static Cache *Cache_alloc() */ static void Cache_free(Cache * cf) { - if (pthread_mutex_destroy(&cf->seek_lock)) { - lprintf(fatal, "could not destroy seek_lock!\n"); - } + if (pthread_mutex_destroy(&cf->seek_lock)) { + lprintf(fatal, "could not destroy seek_lock!\n"); + } - if (pthread_mutex_destroy(&cf->w_lock)) { - lprintf(fatal, "could not destroy w_lock!\n"); - } + if (pthread_mutex_destroy(&cf->w_lock)) { + lprintf(fatal, "could not destroy w_lock!\n"); + } - if (pthread_mutex_destroy(&cf->bgt_lock)) { - lprintf(fatal, "could not destroy bgt_lock!\n"); - } + if (pthread_mutex_destroy(&cf->bgt_lock)) { + lprintf(fatal, "could not destroy bgt_lock!\n"); + } - if (pthread_mutexattr_destroy(&cf->bgt_lock_attr)) { - lprintf(fatal, "could not destroy bgt_lock_attr!\n"); - } + if (pthread_mutexattr_destroy(&cf->bgt_lock_attr)) { + lprintf(fatal, "could not destroy bgt_lock_attr!\n"); + } - if (cf->path) { - FREE(cf->path); - } + if (cf->path) { + FREE(cf->path); + } - if (cf->seg) { - FREE(cf->seg); - } + if (cf->seg) { + FREE(cf->seg); + } - if (cf->fs_path) { - FREE(cf->fs_path); - } + if (cf->fs_path) { + FREE(cf->fs_path); + } - FREE(cf); + FREE(cf); } /** @@ -551,34 +547,32 @@ static void Cache_free(Cache * cf) */ static int Cache_exist(const char *fn) { - char *metafn = path_append(META_DIR, fn); - char *datafn = path_append(DATA_DIR, fn); - /* - * access() returns 0 on success - */ - int no_meta = access(metafn, F_OK); - int no_data = access(datafn, F_OK); + char *metafn = path_append(META_DIR, fn); + char *datafn = path_append(DATA_DIR, fn); + /* + * access() returns 0 on success + */ + int no_meta = access(metafn, F_OK); + int no_data = access(datafn, F_OK); - if (no_meta ^ no_data) { - if (no_meta) { - lprintf(warning, "Cache file partially missing.\n"); - if (unlink(datafn)) { - lprintf(error, "unlink(): %s\n", - strerror(errno)); - } - } - if (no_data) { - if (unlink(metafn)) { - lprintf(error, "unlink(): %s\n", - strerror(errno)); - } - } + if (no_meta ^ no_data) { + if (no_meta) { + lprintf(warning, "Cache file partially missing.\n"); + if (unlink(datafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); + } } + if (no_data) { + if (unlink(metafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); + } + } + } - FREE(metafn); - FREE(datafn); + FREE(metafn); + FREE(datafn); - return no_meta | no_data; + return no_meta | no_data; } /** @@ -586,26 +580,26 @@ static int Cache_exist(const char *fn) */ void Cache_delete(const char *fn) { - if (CONFIG.mode == SONIC) { - Link *link = path_to_Link(fn); - fn = link->sonic_id; - } + if (CONFIG.mode == SONIC) { + Link *link = path_to_Link(fn); + fn = link->sonic_id; + } - char *metafn = path_append(META_DIR, fn); - char *datafn = path_append(DATA_DIR, fn); - if (!access(metafn, F_OK)) { - if (unlink(metafn)) { - lprintf(error, "unlink(): %s\n", strerror(errno)); - } + char *metafn = path_append(META_DIR, fn); + char *datafn = path_append(DATA_DIR, fn); + if (!access(metafn, F_OK)) { + if (unlink(metafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); } + } - if (!access(datafn, F_OK)) { - if (unlink(datafn)) { - lprintf(error, "unlink(): %s\n", strerror(errno)); - } + if (!access(datafn, F_OK)) { + if (unlink(datafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); } - FREE(metafn); - FREE(datafn); + } + FREE(metafn); + FREE(datafn); } /** @@ -616,17 +610,17 @@ void Cache_delete(const char *fn) */ static int Data_open(Cache * cf) { - char *datafn = path_append(DATA_DIR, cf->path); - cf->dfp = fopen(datafn, "r+"); - FREE(datafn); - if (!cf->dfp) { - /* - * Failed to open the data file - */ - lprintf(error, "fopen(%s): %s\n", datafn, strerror(errno)); - return -1; - } - return 0; + char *datafn = path_append(DATA_DIR, cf->path); + cf->dfp = fopen(datafn, "r+"); + FREE(datafn); + if (!cf->dfp) { + /* + * Failed to open the data file + */ + lprintf(error, "fopen(%s): %s\n", datafn, strerror(errno)); + return -1; + } + return 0; } /** @@ -637,18 +631,18 @@ static int Data_open(Cache * cf) */ static int Meta_open(Cache * cf) { - char *metafn = path_append(META_DIR, cf->path); - cf->mfp = fopen(metafn, "r+"); - if (!cf->mfp) { - /* - * Failed to open the data file - */ - lprintf(error, "fopen(%s): %s\n", metafn, strerror(errno)); - FREE(metafn); - return -1; - } + char *metafn = path_append(META_DIR, cf->path); + cf->mfp = fopen(metafn, "r+"); + if (!cf->mfp) { + /* + * Failed to open the data file + */ + lprintf(error, "fopen(%s): %s\n", metafn, strerror(errno)); FREE(metafn); - return 0; + return -1; + } + FREE(metafn); + return 0; } /** @@ -657,259 +651,257 @@ static int Meta_open(Cache * cf) */ static void Meta_create(Cache * cf) { - char *metafn = path_append(META_DIR, cf->path); - cf->mfp = fopen(metafn, "w"); - if (!cf->mfp) { - /* - * Failed to open the data file - */ - lprintf(fatal, "fopen(%s): %s\n", metafn, strerror(errno)); - } - FREE(metafn); + char *metafn = path_append(META_DIR, cf->path); + cf->mfp = fopen(metafn, "w"); + if (!cf->mfp) { + /* + * Failed to open the data file + */ + lprintf(fatal, "fopen(%s): %s\n", metafn, strerror(errno)); + } + FREE(metafn); } int Cache_create(const char *path) { - Link *this_link = path_to_Link(path); + Link *this_link = path_to_Link(path); - char *fn = ""; - if (CONFIG.mode == NORMAL) { - fn = curl_easy_unescape(NULL, - this_link->f_url + ROOT_LINK_OFFSET, 0, - NULL); - } else if (CONFIG.mode == SONIC) { - fn = this_link->sonic_id; - } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); - } - lprintf(debug, "Creating cache files for %s.\n", fn); + char *fn = ""; + if (CONFIG.mode == NORMAL) { + fn = curl_easy_unescape(NULL, + this_link->f_url + ROOT_LINK_OFFSET, 0, + NULL); + } else if (CONFIG.mode == SONIC) { + fn = this_link->sonic_id; + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } + lprintf(debug, "Creating cache files for %s.\n", fn); - Cache *cf = Cache_alloc(); - cf->path = strndup(fn, MAX_PATH_LEN); - cf->time = this_link->time; - cf->content_length = this_link->content_length; - cf->blksz = CONFIG.data_blksz; - cf->segbc = (cf->content_length / cf->blksz) + 1; - cf->seg = CALLOC(cf->segbc, sizeof(Seg)); + Cache *cf = Cache_alloc(); + cf->path = strndup(fn, MAX_PATH_LEN); + cf->time = this_link->time; + cf->content_length = this_link->content_length; + cf->blksz = CONFIG.data_blksz; + cf->segbc = (cf->content_length / cf->blksz) + 1; + cf->seg = CALLOC(cf->segbc, sizeof(Seg)); - Meta_create(cf); + Meta_create(cf); - if (fclose(cf->mfp)) { - lprintf(error, - "cannot close metadata after creation: %s.\n", - strerror(errno)); - } - - if (Meta_open(cf)) { - Cache_free(cf); - lprintf(error, "cannot open metadata file, %s.\n", fn); - } - - if (Meta_write(cf)) { - lprintf(error, "Meta_write() failed!\n"); - } - - if (fclose(cf->mfp)) { - lprintf(error, - "cannot close metadata after write, %s.\n", - strerror(errno)); - } - - Data_create(cf); + if (fclose(cf->mfp)) { + lprintf(error, + "cannot close metadata after creation: %s.\n", + strerror(errno)); + } + if (Meta_open(cf)) { Cache_free(cf); + lprintf(error, "cannot open metadata file, %s.\n", fn); + } - int res = Cache_exist(fn); + if (Meta_write(cf)) { + lprintf(error, "Meta_write() failed!\n"); + } - if (CONFIG.mode == NORMAL) { - curl_free(fn); - } + if (fclose(cf->mfp)) { + lprintf(error, + "cannot close metadata after write, %s.\n", + strerror(errno)); + } - return res; + Data_create(cf); + + Cache_free(cf); + + int res = Cache_exist(fn); + + if (CONFIG.mode == NORMAL) { + curl_free(fn); + } + + return res; } Cache *Cache_open(const char *fn) { + /* + * Obtain the link structure memory pointer + */ + Link *link = path_to_Link(fn); + if (!link) { /* - * Obtain the link structure memory pointer + * There is no associated link to the path */ - Link *link = path_to_Link(fn); - if (!link) { - /* - * There is no associated link to the path - */ - return NULL; - } + return NULL; + } - lprintf(cache_lock_debug, - "thread %x: locking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf_lock); + lprintf(cache_lock_debug, + "thread %x: locking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf_lock); - if (link->cache_opened) { - link->cache_opened++; - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return link->cache_ptr; - } - - /* - * Check if both metadata and data file exist - */ - if (CONFIG.mode == NORMAL) { - if (Cache_exist(fn)) { - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", - pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - } else if (CONFIG.mode == SONIC) { - if (Cache_exist(link->sonic_id)) { - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", - pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); - } - - /* - * Create the cache in-memory data structure - */ - Cache *cf = Cache_alloc(); - - /* - * Fill in the fs_path - */ - cf->fs_path = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - strncpy(cf->fs_path, fn, MAX_PATH_LEN); - - /* - * Set the path for the local cache file, if we are in sonic mode - */ - if (CONFIG.mode == SONIC) { - fn = link->sonic_id; - } - - cf->path = strndup(fn, MAX_PATH_LEN); - - /* - * Associate the cache structure with a link - */ - cf->link = link; - - if (Meta_open(cf)) { - Cache_free(cf); - lprintf(error, "cannot open metadata file %s.\n", fn); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - /* - * Corrupt metadata - */ - if (Meta_read(cf)) { - Cache_free(cf); - lprintf(error, "metadata error: %s.\n", fn); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - /* - * Inconsistency between metadata and data file, note that on disk file - * size might be bigger than content_length, due to on-disk filesystem - * allocation policy. - */ - if (cf->content_length > Data_size(fn)) { - lprintf(error, "metadata inconsistency %s, \ -cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length, Data_size(fn)); - Cache_free(cf); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - /* - * Check if the cache files are not outdated - */ - if (cf->time != cf->link->time) { - lprintf(warning, "outdated cache file: %s.\n", fn); - Cache_free(cf); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - if (Data_open(cf)) { - Cache_free(cf); - lprintf(error, "cannot open data file %s.\n", fn); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - cf->link->cache_opened = 1; - /* - * Yup, we just created a circular loop. ;) - */ - cf->link->cache_ptr = cf; + if (link->cache_opened) { + link->cache_opened++; lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); PTHREAD_MUTEX_UNLOCK(&cf_lock); - return cf; + return link->cache_ptr; + } + + /* + * Check if both metadata and data file exist + */ + if (CONFIG.mode == NORMAL) { + if (Cache_exist(fn)) { + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + } else if (CONFIG.mode == SONIC) { + if (Cache_exist(link->sonic_id)) { + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } + + /* + * Create the cache in-memory data structure + */ + Cache *cf = Cache_alloc(); + + /* + * Fill in the fs_path + */ + cf->fs_path = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + strncpy(cf->fs_path, fn, MAX_PATH_LEN); + + /* + * Set the path for the local cache file, if we are in sonic mode + */ + if (CONFIG.mode == SONIC) { + fn = link->sonic_id; + } + + cf->path = strndup(fn, MAX_PATH_LEN); + + /* + * Associate the cache structure with a link + */ + cf->link = link; + + if (Meta_open(cf)) { + Cache_free(cf); + lprintf(error, "cannot open metadata file %s.\n", fn); + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + /* + * Corrupt metadata + */ + if (Meta_read(cf)) { + Cache_free(cf); + lprintf(error, "metadata error: %s.\n", fn); + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + /* + * Inconsistency between metadata and data file, note that on disk file + * size might be bigger than content_length, due to on-disk filesystem + * allocation policy. + */ + if (cf->content_length > Data_size(fn)) { + lprintf(error, "metadata inconsistency %s, \ +cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length, Data_size(fn)); + Cache_free(cf); + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + /* + * Check if the cache files are not outdated + */ + if (cf->time != cf->link->time) { + lprintf(warning, "outdated cache file: %s.\n", fn); + Cache_free(cf); + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + if (Data_open(cf)) { + Cache_free(cf); + lprintf(error, "cannot open data file %s.\n", fn); + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + cf->link->cache_opened = 1; + /* + * Yup, we just created a circular loop. ;) + */ + cf->link->cache_ptr = cf; + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return cf; } void Cache_close(Cache * cf) { - lprintf(cache_lock_debug, - "thread %x: locking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf_lock); + lprintf(cache_lock_debug, + "thread %x: locking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf_lock); - cf->link->cache_opened--; + cf->link->cache_opened--; - if (cf->link->cache_opened > 0) { - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return; - } - - if (Meta_write(cf)) { - lprintf(error, "Meta_write() error."); - } - - if (fclose(cf->mfp)) { - lprintf(error, "cannot close metadata: %s.\n", strerror(errno)); - } - - if (fclose(cf->dfp)) { - lprintf(error, "cannot close data file %s.\n", strerror(errno)); - } + if (cf->link->cache_opened > 0) { lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); PTHREAD_MUTEX_UNLOCK(&cf_lock); - return Cache_free(cf); + return; + } + + if (Meta_write(cf)) { + lprintf(error, "Meta_write() error."); + } + + if (fclose(cf->mfp)) { + lprintf(error, "cannot close metadata: %s.\n", strerror(errno)); + } + + if (fclose(cf->dfp)) { + lprintf(error, "cannot close data file %s.\n", strerror(errno)); + } + + lprintf(cache_lock_debug, + "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return Cache_free(cf); } /** @@ -918,8 +910,8 @@ void Cache_close(Cache * cf) */ static int Seg_exist(Cache * cf, off_t offset) { - off_t byte = offset / cf->blksz; - return cf->seg[byte]; + off_t byte = offset / cf->blksz; + return cf->seg[byte]; } /** @@ -931,8 +923,8 @@ static int Seg_exist(Cache * cf, off_t offset) */ static void Seg_set(Cache * cf, off_t offset, int i) { - off_t byte = offset / cf->blksz; - cf->seg[byte] = i; + off_t byte = offset / cf->blksz; + cf->seg[byte] = i; } /** @@ -943,147 +935,144 @@ static void Seg_set(Cache * cf, off_t offset, int i) */ static void *Cache_bgdl(void *arg) { - Cache *cf = (Cache *) arg; + Cache *cf = (Cache *) arg; - lprintf(cache_lock_debug, "thread %x: locking w_lock;\n", - pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->w_lock); + lprintf(cache_lock_debug, "thread %x: locking w_lock;\n", + pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->w_lock); - uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); - lprintf(debug, "thread %x spawned.\n ", pthread_self()); - long recv = path_download(cf->fs_path, (char *)recv_buf, cf->blksz, - cf->next_dl_offset); - if (recv < 0) { - lprintf(error, "thread %x received %ld bytes, \ + uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); + lprintf(debug, "thread %x spawned.\n ", pthread_self()); + long recv = path_download(cf->fs_path, (char *) recv_buf, cf->blksz, + cf->next_dl_offset); + if (recv < 0) { + lprintf(error, "thread %x received %ld bytes, \ which does't make sense\n", pthread_self(), recv); - } + } - if ((recv == cf->blksz) || - (cf->next_dl_offset == - (cf->content_length / cf->blksz * cf->blksz))) { - Data_write(cf, recv_buf, recv, cf->next_dl_offset); - Seg_set(cf, cf->next_dl_offset, 1); - } else { - lprintf(error, "received %ld rather than %ld, possible network \ + if ((recv == cf->blksz) || + (cf->next_dl_offset == + (cf->content_length / cf->blksz * cf->blksz))) { + Data_write(cf, recv_buf, recv, cf->next_dl_offset); + Seg_set(cf, cf->next_dl_offset, 1); + } else { + lprintf(error, "received %ld rather than %ld, possible network \ error.\n", recv, cf->blksz); - } + } - FREE(recv_buf); + FREE(recv_buf); - lprintf(cache_lock_debug, - "thread %x: unlocking bgt_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->bgt_lock); + lprintf(cache_lock_debug, + "thread %x: unlocking bgt_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->bgt_lock); - lprintf(cache_lock_debug, - "thread %x: unlocking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->w_lock); + lprintf(cache_lock_debug, + "thread %x: unlocking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - pthread_detach(pthread_self()); - pthread_exit(NULL); + pthread_detach(pthread_self()); + pthread_exit(NULL); } long Cache_read(Cache * cf, char *const output_buf, const off_t len, const off_t offset_start) { - long send; + long send; - /* - * The offset of the segment to be downloaded - */ - off_t dl_offset = (offset_start + len) / cf->blksz * cf->blksz; + /* + * The offset of the segment to be downloaded + */ + off_t dl_offset = (offset_start + len) / cf->blksz * cf->blksz; - /* - * ------------------ Check if the segment already exists - * --------------- - */ - if (Seg_exist(cf, dl_offset)) { - send = Data_read(cf, (uint8_t *) output_buf, len, offset_start); - goto bgdl; - } else { - /* - * Wait for any other download thread to finish - */ - - lprintf(cache_lock_debug, - "thread %ld: locking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->w_lock); - - if (Seg_exist(cf, dl_offset)) { - /* - * The segment now exists - it was downloaded by another - * download thread. Send it off and unlock the I/O - */ - send = - Data_read(cf, (uint8_t *) output_buf, len, - offset_start); - - lprintf(cache_lock_debug, - "thread %x: unlocking w_lock;\n", - pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - - goto bgdl; - } - } - - /* - * ------------------------Download the segment - * ------------------------- - */ - - uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); - lprintf(debug, "thread %x: spawned.\n ", pthread_self()); - long recv = path_download(cf->fs_path, (char *)recv_buf, cf->blksz, - dl_offset); - if (recv < 0) { - lprintf(error, "thread %x received %ld bytes, \ -which does't make sense\n", pthread_self(), recv); - } - /* - * check if we have received enough data, write it to the disk - * - * Condition 1: received the exact amount as the segment size. - * Condition 2: offset is the last segment - */ - if ((recv == cf->blksz) || - (dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { - Data_write(cf, recv_buf, recv, dl_offset); - Seg_set(cf, dl_offset, 1); - } else { - lprintf(error, "received %ld rather than %ld, possible network \ -error.\n", recv, cf->blksz); - } - FREE(recv_buf); + /* + * ------------------ Check if the segment already exists + * --------------- + */ + if (Seg_exist(cf, dl_offset)) { send = Data_read(cf, (uint8_t *) output_buf, len, offset_start); + goto bgdl; + } else { + /* + * Wait for any other download thread to finish + */ lprintf(cache_lock_debug, - "thread %x: unlocking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->w_lock); + "thread %ld: locking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->w_lock); + if (Seg_exist(cf, dl_offset)) { + /* + * The segment now exists - it was downloaded by another + * download thread. Send it off and unlock the I/O + */ + send = + Data_read(cf, (uint8_t *) output_buf, len, offset_start); + + lprintf(cache_lock_debug, + "thread %x: unlocking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->w_lock); + + goto bgdl; + } + } + + /* + * ------------------------Download the segment + * ------------------------- + */ + + uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); + lprintf(debug, "thread %x: spawned.\n ", pthread_self()); + long recv = path_download(cf->fs_path, (char *) recv_buf, cf->blksz, + dl_offset); + if (recv < 0) { + lprintf(error, "thread %x received %ld bytes, \ +which does't make sense\n", pthread_self(), recv); + } + /* + * check if we have received enough data, write it to the disk + * + * Condition 1: received the exact amount as the segment size. + * Condition 2: offset is the last segment + */ + if ((recv == cf->blksz) || + (dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { + Data_write(cf, recv_buf, recv, dl_offset); + Seg_set(cf, dl_offset, 1); + } else { + lprintf(error, "received %ld rather than %ld, possible network \ +error.\n", recv, cf->blksz); + } + FREE(recv_buf); + send = Data_read(cf, (uint8_t *) output_buf, len, offset_start); + + lprintf(cache_lock_debug, + "thread %x: unlocking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->w_lock); + + /* + * ----------- Download the next segment in background ----------------- + */ + bgdl: + { + } + off_t next_dl_offset = round_div(offset_start, cf->blksz) * cf->blksz; + if ((next_dl_offset > dl_offset) && !Seg_exist(cf, next_dl_offset) + && next_dl_offset < cf->content_length) { /* - * ----------- Download the next segment in background ----------------- + * Stop the spawning of multiple background pthreads */ - bgdl: - { - } - off_t next_dl_offset = round_div(offset_start, cf->blksz) * cf->blksz; - if ((next_dl_offset > dl_offset) && !Seg_exist(cf, next_dl_offset) - && next_dl_offset < cf->content_length) { - /* - * Stop the spawning of multiple background pthreads - */ - if (!pthread_mutex_trylock(&cf->bgt_lock)) { - lprintf(cache_lock_debug, - "thread %x: trylocked bgt_lock;\n", - pthread_self()); - cf->next_dl_offset = next_dl_offset; - if (pthread_create(&cf->bgt, NULL, Cache_bgdl, cf)) { - lprintf(error, - "Error creating background download thread\n"); - } - } + if (!pthread_mutex_trylock(&cf->bgt_lock)) { + lprintf(cache_lock_debug, + "thread %x: trylocked bgt_lock;\n", pthread_self()); + cf->next_dl_offset = next_dl_offset; + if (pthread_create(&cf->bgt, NULL, Cache_bgdl, cf)) { + lprintf(error, + "Error creating background download thread\n"); + } } + } - return send; + return send; } diff --git a/src/cache.h b/src/cache.h index 0aeef47..d470409 100644 --- a/src/cache.h +++ b/src/cache.h @@ -28,44 +28,44 @@ typedef uint8_t Seg; */ struct Cache { /** \brief the FILE pointer for the data file*/ - FILE *dfp; + FILE *dfp; /** \brief the FILE pointer for the metadata */ - FILE *mfp; + FILE *mfp; /** \brief the path to the local cache file */ - char *path; + char *path; /** \brief the Link associated with this cache data set */ - Link *link; + Link *link; /** \brief the modified time of the file */ - long time; + long time; /** \brief the size of the file */ - off_t content_length; + off_t content_length; /** \brief the block size of the data file */ - int blksz; + int blksz; /** \brief segment array byte count */ - long segbc; + long segbc; /** \brief the detail of each segment */ - Seg *seg; + Seg *seg; /** \brief mutex lock for seek operation */ - pthread_mutex_t seek_lock; + pthread_mutex_t seek_lock; /** \brief mutex lock for write operation */ - pthread_mutex_t w_lock; + pthread_mutex_t w_lock; /** \brief background download pthread */ - pthread_t bgt; + pthread_t bgt; /** * \brief mutex lock for the background download thread * \note This lock is locked by the foreground thread, but unlocked by the * background thread! */ - pthread_mutex_t bgt_lock; + pthread_mutex_t bgt_lock; /** \brief mutex attributes for bgt_lock */ - pthread_mutexattr_t bgt_lock_attr; + pthread_mutexattr_t bgt_lock_attr; /** \brief the offset of the next segment to be downloaded in background*/ - off_t next_dl_offset; + off_t next_dl_offset; /** \brief the FUSE filesystem path to the remote file*/ - char *fs_path; + char *fs_path; }; /** diff --git a/src/config.c b/src/config.c index 26ed1b6..24f4a59 100644 --- a/src/config.c +++ b/src/config.c @@ -28,46 +28,46 @@ ConfigStruct CONFIG; */ void Config_init(void) { - CONFIG.mode = NORMAL; + CONFIG.mode = NORMAL; - CONFIG.log_type = log_level_init(); + CONFIG.log_type = log_level_init(); /*---------------- Network related --------------*/ - CONFIG.http_username = NULL; + CONFIG.http_username = NULL; - CONFIG.http_password = NULL; + CONFIG.http_password = NULL; - CONFIG.proxy = NULL; + CONFIG.proxy = NULL; - CONFIG.proxy_username = NULL; + CONFIG.proxy_username = NULL; - CONFIG.proxy_password = NULL; + CONFIG.proxy_password = NULL; - CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; + CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; - CONFIG.user_agent = DEFAULT_USER_AGENT; + CONFIG.user_agent = DEFAULT_USER_AGENT; - CONFIG.http_wait_sec = DEFAULT_HTTP_WAIT_SEC; + CONFIG.http_wait_sec = DEFAULT_HTTP_WAIT_SEC; - CONFIG.no_range_check = 0; + CONFIG.no_range_check = 0; - CONFIG.insecure_tls = 0; + CONFIG.insecure_tls = 0; /*--------------- Cache related ---------------*/ - CONFIG.cache_enabled = 0; + CONFIG.cache_enabled = 0; - CONFIG.cache_dir = NULL; + CONFIG.cache_dir = NULL; - CONFIG.data_blksz = DEFAULT_DATA_BLKSZ; + CONFIG.data_blksz = DEFAULT_DATA_BLKSZ; - CONFIG.max_segbc = DEFAULT_MAX_SEGBC; + CONFIG.max_segbc = DEFAULT_MAX_SEGBC; /*-------------- Sonic related -------------*/ - CONFIG.sonic_username = NULL; + CONFIG.sonic_username = NULL; - CONFIG.sonic_password = NULL; + CONFIG.sonic_password = NULL; - CONFIG.sonic_id3 = 0; + CONFIG.sonic_id3 = 0; - CONFIG.sonic_insecure = 0; + CONFIG.sonic_insecure = 0; } diff --git a/src/config.h b/src/config.h index 97b3f2d..e4731ad 100644 --- a/src/config.h +++ b/src/config.h @@ -27,9 +27,9 @@ * \brief Operation modes */ typedef enum { - NORMAL = 1, - SONIC = 2, - SINGLE_FILE = 3, + NORMAL = 1, + SONIC = 2, + SINGLE_FILE = 3, } OperationMode; /** @@ -39,48 +39,48 @@ typedef enum { */ typedef struct { /** \brief Operation Mode */ - OperationMode mode; + OperationMode mode; /** \brief Current log level */ - int log_type; + int log_type; /*---------------- Network related --------------*/ /** \brief HTTP username */ - char *http_username; + char *http_username; /** \brief HTTP password */ - char *http_password; + char *http_password; /** \brief HTTP proxy URL */ - char *proxy; + char *proxy; /** \brief HTTP proxy username */ - char *proxy_username; + char *proxy_username; /** \brief HTTP proxy password */ - char *proxy_password; + char *proxy_password; /** \brief HTTP maximum connection count */ - long max_conns; + long max_conns; /** \brief HTTP user agent*/ - char *user_agent; + char *user_agent; /** \brief The waiting time after getting HTTP 429 (too many requests) */ - int http_wait_sec; + int http_wait_sec; /** \brief Disable check for the server's support of HTTP range request */ - int no_range_check; + int no_range_check; /** \brief Disable TLS certificate verification */ - int insecure_tls; + int insecure_tls; /*--------------- Cache related ---------------*/ /** \brief Whether cache mode is enabled */ - int cache_enabled; + int cache_enabled; /** \brief The cache location*/ - char *cache_dir; + char *cache_dir; /** \brief The size of each download segment for cache mode */ - int data_blksz; + int data_blksz; /** \brief The maximum segment count for a single cache file */ - int max_segbc; + int max_segbc; /*-------------- Sonic related -------------*/ /** \brief The Sonic server username */ - char *sonic_username; + char *sonic_username; /** \brief The Sonic server password */ - char *sonic_password; + char *sonic_password; /** \brief Whether we are using sonic mode ID3 extension */ - int sonic_id3; + int sonic_id3; /** \brief Whether we use the legacy sonic authentication mode */ - int sonic_insecure; + int sonic_insecure; } ConfigStruct; /** diff --git a/src/fuse_local.c b/src/fuse_local.c index 4b91651..0698390 100644 --- a/src/fuse_local.c +++ b/src/fuse_local.c @@ -14,61 +14,61 @@ static void *fs_init(struct fuse_conn_info *conn) { - (void)conn; - return NULL; + (void) conn; + return NULL; } /** \brief release an opened file */ static int fs_release(const char *path, struct fuse_file_info *fi) { - (void)path; - if (CACHE_SYSTEM_INIT) { - Cache_close((Cache *) fi->fh); - } - return 0; + (void) path; + if (CACHE_SYSTEM_INIT) { + Cache_close((Cache *) fi->fh); + } + return 0; } /** \brief return the attributes for a single file indicated by path */ static int fs_getattr(const char *path, struct stat *stbuf) { - int res = 0; - memset(stbuf, 0, sizeof(struct stat)); + int res = 0; + memset(stbuf, 0, sizeof(struct stat)); - if (!strcmp(path, "/")) { - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 1; - } else { - Link *link = path_to_Link(path); - if (!link) { - return -ENOENT; - } - struct timespec spec; - spec.tv_sec = link->time; -#if defined(__APPLE__) && defined(__MACH__) - stbuf->st_mtimespec = spec; -#else - stbuf->st_mtim = spec; -#endif - switch (link->type) { - case LINK_DIR: - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 1; - break; - case LINK_FILE: - stbuf->st_mode = S_IFREG | 0444; - stbuf->st_nlink = 1; - stbuf->st_size = link->content_length; - stbuf->st_blksize = 128 * 1024; - stbuf->st_blocks = (link->content_length) / 512; - break; - default: - return -ENOENT; - } + if (!strcmp(path, "/")) { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 1; + } else { + Link *link = path_to_Link(path); + if (!link) { + return -ENOENT; } - stbuf->st_uid = getuid(); - stbuf->st_gid = getgid(); + struct timespec spec; + spec.tv_sec = link->time; +#if defined(__APPLE__) && defined(__MACH__) + stbuf->st_mtimespec = spec; +#else + stbuf->st_mtim = spec; +#endif + switch (link->type) { + case LINK_DIR: + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 1; + break; + case LINK_FILE: + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = link->content_length; + stbuf->st_blksize = 128 * 1024; + stbuf->st_blocks = (link->content_length) / 512; + break; + default: + return -ENOENT; + } + } + stbuf->st_uid = getuid(); + stbuf->st_gid = getgid(); - return res; + return res; } /** \brief read a file */ @@ -76,44 +76,44 @@ static int fs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { - long received; - if (CACHE_SYSTEM_INIT) { - received = Cache_read((Cache *) fi->fh, buf, size, offset); - } else { - received = path_download(path, buf, size, offset); - } - return received; + long received; + if (CACHE_SYSTEM_INIT) { + received = Cache_read((Cache *) fi->fh, buf, size, offset); + } else { + received = path_download(path, buf, size, offset); + } + return received; } /** \brief open a file indicated by the path */ static int fs_open(const char *path, struct fuse_file_info *fi) { - Link *link = path_to_Link(path); - if (!link) { + Link *link = path_to_Link(path); + if (!link) { + return -ENOENT; + } + if ((fi->flags & 3) != O_RDONLY) { + return -EACCES; + } + if (CACHE_SYSTEM_INIT) { + fi->fh = (uint64_t) Cache_open(path); + if (!fi->fh) { + /* + * The link clearly exists, the cache cannot be opened, attempt + * cache creation + */ + Cache_delete(path); + Cache_create(path); + fi->fh = (uint64_t) Cache_open(path); + /* + * The cache definitely cannot be opened for some reason. + */ + if (!fi->fh) { return -ENOENT; + } } - if ((fi->flags & 3) != O_RDONLY) { - return -EACCES; - } - if (CACHE_SYSTEM_INIT) { - fi->fh = (uint64_t) Cache_open(path); - if (!fi->fh) { - /* - * The link clearly exists, the cache cannot be opened, attempt - * cache creation - */ - Cache_delete(path); - Cache_create(path); - fi->fh = (uint64_t) Cache_open(path); - /* - * The cache definitely cannot be opened for some reason. - */ - if (!fi->fh) { - return -ENOENT; - } - } - } - return 0; + } + return 0; } /** @@ -131,45 +131,45 @@ static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add, off_t offset, struct fuse_file_info *fi) { - (void)offset; - (void)fi; + (void) offset; + (void) fi; - LinkTable *linktbl; + LinkTable *linktbl; - if (!strcmp(path, "/")) { - linktbl = ROOT_LINK_TBL; - } else { - linktbl = path_to_Link_LinkTable_new(path); - if (!linktbl) { - return -ENOENT; - } + if (!strcmp(path, "/")) { + linktbl = ROOT_LINK_TBL; + } else { + linktbl = path_to_Link_LinkTable_new(path); + if (!linktbl) { + return -ENOENT; } + } - /* - * start adding the links - */ - dir_add(buf, ".", NULL, 0); - dir_add(buf, "..", NULL, 0); - for (int i = 1; i < linktbl->num; i++) { - Link *link = linktbl->links[i]; - if (link->type != LINK_INVALID) { - dir_add(buf, link->linkname, NULL, 0); - } + /* + * start adding the links + */ + dir_add(buf, ".", NULL, 0); + dir_add(buf, "..", NULL, 0); + for (int i = 1; i < linktbl->num; i++) { + Link *link = linktbl->links[i]; + if (link->type != LINK_INVALID) { + dir_add(buf, link->linkname, NULL, 0); } + } - return 0; + return 0; } static struct fuse_operations fs_oper = { - .getattr = fs_getattr, - .readdir = fs_readdir, - .open = fs_open, - .read = fs_read, - .init = fs_init, - .release = fs_release + .getattr = fs_getattr, + .readdir = fs_readdir, + .open = fs_open, + .read = fs_read, + .init = fs_init, + .release = fs_release }; int fuse_local_init(int argc, char **argv) { - return fuse_main(argc, argv, &fs_oper, NULL); + return fuse_main(argc, argv, &fs_oper, NULL); } diff --git a/src/link.c b/src/link.c index 148f6bb..f7f013b 100644 --- a/src/link.c +++ b/src/link.c @@ -34,73 +34,74 @@ static pthread_mutex_t link_lock; */ static LinkTable *single_LinkTable_new(const char *fn) { - char *ptr = strrchr(fn, '/'); - int dir_len = ptr - fn; - char *dir_name = CALLOC(dir_len + 1, sizeof(char)); - free(dir_name); - return NULL; + char *ptr = strrchr(fn, '/'); + int dir_len = ptr - fn; + char *dir_name = CALLOC(dir_len + 1, sizeof(char)); + free(dir_name); + return NULL; } LinkTable *LinkSystem_init(const char *raw_url) { - if (pthread_mutex_init(&link_lock, NULL)) { - lprintf(error, "link_lock initialisation failed!\n"); - } - /* - * Remove excess '/' if it is there - */ - char *url = strdup(raw_url); - int url_len = strnlen(url, MAX_PATH_LEN) - 1; - if (url[url_len] == '/') { - url[url_len] = '\0'; - } - /* - * --------- Set the length of the root link ----------- - */ - /* - * This is where the '/' should be - */ - ROOT_LINK_OFFSET = strnlen(url, MAX_PATH_LEN); + if (pthread_mutex_init(&link_lock, NULL)) { + lprintf(error, "link_lock initialisation failed!\n"); + } + /* + * Remove excess '/' if it is there + */ + char *url = strdup(raw_url); + int url_len = strnlen(url, MAX_PATH_LEN) - 1; + if (url[url_len] == '/') { + url[url_len] = '\0'; + } + /* + * --------- Set the length of the root link ----------- + */ + /* + * This is where the '/' should be + */ + ROOT_LINK_OFFSET = strnlen(url, MAX_PATH_LEN); - /* - * --------------------- Enable cache system -------------------- - */ - if (CONFIG.cache_enabled) { - if (CONFIG.cache_dir) { - CacheSystem_init(CONFIG.cache_dir, 0); - } else { - CacheSystem_init(url, 1); - } - } - - /* - * ----------- Create the root link table -------------- - */ - if (CONFIG.mode == NORMAL) { - ROOT_LINK_TBL = LinkTable_new(url); - } else if (CONFIG.mode == SONIC) { - sonic_config_init(url, CONFIG.sonic_username, - CONFIG.sonic_password); - if (!CONFIG.sonic_id3) { - ROOT_LINK_TBL = sonic_LinkTable_new_index("0"); - } else { - ROOT_LINK_TBL = sonic_LinkTable_new_id3(0, "0"); - } + /* + * --------------------- Enable cache system -------------------- + */ + if (CONFIG.cache_enabled) { + if (CONFIG.cache_dir) { + CacheSystem_init(CONFIG.cache_dir, 0); } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); + CacheSystem_init(url, 1); } - FREE(url); - return ROOT_LINK_TBL; + } + + /* + * ----------- Create the root link table -------------- + */ + if (CONFIG.mode == NORMAL) { + ROOT_LINK_TBL = LinkTable_new(url); + } else if (CONFIG.mode == SONIC) { + sonic_config_init(url, CONFIG.sonic_username, + CONFIG.sonic_password); + if (!CONFIG.sonic_id3) { + ROOT_LINK_TBL = sonic_LinkTable_new_index("0"); + } else { + ROOT_LINK_TBL = sonic_LinkTable_new_id3(0, "0"); + } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } + FREE(url); + return ROOT_LINK_TBL; } void LinkTable_add(LinkTable * linktbl, Link * link) { - linktbl->num++; - linktbl->links = realloc(linktbl->links, linktbl->num * sizeof(Link *)); - if (!linktbl->links) { - lprintf(fatal, "realloc() failure!\n"); - } - linktbl->links[linktbl->num - 1] = link; + linktbl->num++; + linktbl->links = + realloc(linktbl->links, linktbl->num * sizeof(Link *)); + if (!linktbl->links) { + lprintf(fatal, "realloc() failure!\n"); + } + linktbl->links[linktbl->num - 1] = link; } /** @@ -108,48 +109,48 @@ void LinkTable_add(LinkTable * linktbl, Link * link) */ static Link *Link_new(const char *linkname, LinkType type) { - Link *link = CALLOC(1, sizeof(Link)); + Link *link = CALLOC(1, sizeof(Link)); - strncpy(link->linkname, linkname, MAX_FILENAME_LEN); - link->type = type; + strncpy(link->linkname, linkname, MAX_FILENAME_LEN); + link->type = type; - /* - * remove the '/' from linkname if it exists - */ - char *c = - &(link->linkname[strnlen(link->linkname, MAX_FILENAME_LEN) - 1]); - if (*c == '/') { - *c = '\0'; - } + /* + * remove the '/' from linkname if it exists + */ + char *c = + &(link->linkname[strnlen(link->linkname, MAX_FILENAME_LEN) - 1]); + if (*c == '/') { + *c = '\0'; + } - return link; + return link; } static LinkType linkname_to_LinkType(const char *linkname) { - /* - * The link name has to start with alphanumerical character - */ - if (!isalnum(linkname[0]) && (linkname[0] != '%')) { - return LINK_INVALID; - } + /* + * The link name has to start with alphanumerical character + */ + if (!isalnum(linkname[0]) && (linkname[0] != '%')) { + return LINK_INVALID; + } - /* - * Check for stray '/' - Linkname should not have '/' - */ - char *slash = strchr(linkname, '/'); - if (slash) { - int linkname_len = strnlen(linkname, MAX_FILENAME_LEN) - 1; - if (slash - linkname != linkname_len) { - return LINK_INVALID; - } + /* + * Check for stray '/' - Linkname should not have '/' + */ + char *slash = strchr(linkname, '/'); + if (slash) { + int linkname_len = strnlen(linkname, MAX_FILENAME_LEN) - 1; + if (slash - linkname != linkname_len) { + return LINK_INVALID; } + } - if (linkname[strnlen(linkname, MAX_FILENAME_LEN) - 1] == '/') { - return LINK_DIR; - } + if (linkname[strnlen(linkname, MAX_FILENAME_LEN) - 1] == '/') { + return LINK_DIR; + } - return LINK_UNINITIALISED_FILE; + return LINK_UNINITIALISED_FILE; } /** @@ -157,24 +158,22 @@ static LinkType linkname_to_LinkType(const char *linkname) */ static int linknames_equal(char *linkname, const char *linkname_new) { - if (!strncmp(linkname, linkname_new, MAX_FILENAME_LEN)) { - return 1; - } + if (!strncmp(linkname, linkname_new, MAX_FILENAME_LEN)) { + return 1; + } - /* - * check if the link names differ by a single '/' - */ - if (!strncmp - (linkname, linkname_new, strnlen(linkname, MAX_FILENAME_LEN))) { - size_t linkname_new_len = - strnlen(linkname_new, MAX_FILENAME_LEN); - if ((linkname_new_len - strnlen(linkname, MAX_FILENAME_LEN) == - 1) - && (linkname_new[linkname_new_len - 1] == '/')) { - return 1; - } + /* + * check if the link names differ by a single '/' + */ + if (!strncmp + (linkname, linkname_new, strnlen(linkname, MAX_FILENAME_LEN))) { + size_t linkname_new_len = strnlen(linkname_new, MAX_FILENAME_LEN); + if ((linkname_new_len - strnlen(linkname, MAX_FILENAME_LEN) == 1) + && (linkname_new[linkname_new_len - 1] == '/')) { + return 1; } - return 0; + } + return 0; } /** @@ -183,135 +182,136 @@ static int linknames_equal(char *linkname, const char *linkname_new) */ static void HTML_to_LinkTable(GumboNode * node, LinkTable * linktbl) { - if (node->type != GUMBO_NODE_ELEMENT) { - return; - } - GumboAttribute *href; - if (node->v.element.tag == GUMBO_TAG_A && - (href = gumbo_get_attribute(&node->v.element.attributes, "href"))) { - /* - * if it is valid, copy the link onto the heap - */ - LinkType type = linkname_to_LinkType(href->value); - /* - * We also check if the link being added is the same as the last link. - * This is to prevent duplicated link, if an Apache server has the - * IconsAreLinks option. - */ - size_t comp_len = strnlen(href->value, MAX_FILENAME_LEN); - if (type == LINK_DIR) { - comp_len--; - } - if (((type == LINK_DIR) || (type == LINK_UNINITIALISED_FILE)) && - !linknames_equal(linktbl->links[linktbl->num - 1]->linkname, - href->value)) { - LinkTable_add(linktbl, Link_new(href->value, type)); - } - } - /* - * Note the recursive call, lol. - */ - GumboVector *children = &node->v.element.children; - for (size_t i = 0; i < children->length; ++i) { - HTML_to_LinkTable((GumboNode *) children->data[i], linktbl); - } + if (node->type != GUMBO_NODE_ELEMENT) { return; + } + GumboAttribute *href; + if (node->v.element.tag == GUMBO_TAG_A && + (href = + gumbo_get_attribute(&node->v.element.attributes, "href"))) { + /* + * if it is valid, copy the link onto the heap + */ + LinkType type = linkname_to_LinkType(href->value); + /* + * We also check if the link being added is the same as the last link. + * This is to prevent duplicated link, if an Apache server has the + * IconsAreLinks option. + */ + size_t comp_len = strnlen(href->value, MAX_FILENAME_LEN); + if (type == LINK_DIR) { + comp_len--; + } + if (((type == LINK_DIR) || (type == LINK_UNINITIALISED_FILE)) && + !linknames_equal(linktbl->links[linktbl->num - 1]->linkname, + href->value)) { + LinkTable_add(linktbl, Link_new(href->value, type)); + } + } + /* + * Note the recursive call, lol. + */ + GumboVector *children = &node->v.element.children; + for (size_t i = 0; i < children->length; ++i) { + HTML_to_LinkTable((GumboNode *) children->data[i], linktbl); + } + return; } static CURL *Link_to_curl(Link * link) { - CURL *curl = curl_easy_init(); - if (!curl) { - lprintf(fatal, "curl_easy_init() failed!\n"); - } - /* - * set up some basic curl stuff - */ - curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - /* - * for following directories without the '/' - */ - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); - curl_easy_setopt(curl, CURLOPT_URL, link->f_url); - curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); - curl_easy_setopt(curl, CURLOPT_SHARE, CURL_SHARE); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback); - if (CONFIG.insecure_tls) { - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - } - // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + CURL *curl = curl_easy_init(); + if (!curl) { + lprintf(fatal, "curl_easy_init() failed!\n"); + } + /* + * set up some basic curl stuff + */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + /* + * for following directories without the '/' + */ + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); + curl_easy_setopt(curl, CURLOPT_URL, link->f_url); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); + curl_easy_setopt(curl, CURLOPT_SHARE, CURL_SHARE); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback); + if (CONFIG.insecure_tls) { + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + } + // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (CONFIG.http_username) { - curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); - } + if (CONFIG.http_username) { + curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); + } - if (CONFIG.http_password) { - curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); - } + if (CONFIG.http_password) { + curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); + } - if (CONFIG.proxy) { - curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); - } + if (CONFIG.proxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); + } - if (CONFIG.proxy_username) { - curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, - CONFIG.proxy_username); - } + if (CONFIG.proxy_username) { + curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, + CONFIG.proxy_username); + } - if (CONFIG.proxy_password) { - curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, - CONFIG.proxy_password); - } + if (CONFIG.proxy_password) { + curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, + CONFIG.proxy_password); + } - return curl; + return curl; } static void Link_req_file_stat(Link * this_link) { - CURL *curl = Link_to_curl(this_link); - curl_easy_setopt(curl, CURLOPT_NOBODY, 1); - curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + CURL *curl = Link_to_curl(this_link); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1); + curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); - /* - * We need to put the variable on the heap, because otherwise the - * variable gets popped from the stack as the function returns. - * - * It gets freed in curl_multi_perform_once(); - */ - TransferStruct *transfer = CALLOC(1, sizeof(TransferStruct)); + /* + * We need to put the variable on the heap, because otherwise the + * variable gets popped from the stack as the function returns. + * + * It gets freed in curl_multi_perform_once(); + */ + TransferStruct *transfer = CALLOC(1, sizeof(TransferStruct)); - transfer->link = this_link; - transfer->type = FILESTAT; - curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); + transfer->link = this_link; + transfer->type = FILESTAT; + curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); - transfer_nonblocking(curl); + transfer_nonblocking(curl); } void Link_set_file_stat(Link * this_link, CURL * curl) { - long http_resp; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (http_resp == HTTP_OK) { - double cl = 0; - curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl); - curl_easy_getinfo(curl, CURLINFO_FILETIME, &(this_link->time)); - if (cl == -1) { - this_link->type = LINK_INVALID; - } else { - this_link->type = LINK_FILE; - this_link->content_length = cl; - } + long http_resp; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (http_resp == HTTP_OK) { + double cl = 0; + curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl); + curl_easy_getinfo(curl, CURLINFO_FILETIME, &(this_link->time)); + if (cl == -1) { + this_link->type = LINK_INVALID; } else { - lprintf(warning, "HTTP %ld", http_resp); - if (HTTP_temp_failure(http_resp)) { - lprintf(warning, ", retrying later.\n"); - } else { - this_link->type = LINK_INVALID; - lprintf(warning, ".\n"); - } + this_link->type = LINK_FILE; + this_link->content_length = cl; } + } else { + lprintf(warning, "HTTP %ld", http_resp); + if (HTTP_temp_failure(http_resp)) { + lprintf(warning, ", retrying later.\n"); + } else { + this_link->type = LINK_INVALID; + lprintf(warning, ".\n"); + } + } } /** @@ -321,61 +321,60 @@ void Link_set_file_stat(Link * this_link, CURL * curl) */ static void LinkTable_uninitialised_fill(LinkTable * linktbl) { - int u; - char s[STATUS_LEN]; - lprintf(debug, "LinkTable_uninitialised_fill(): ... "); - do { - u = 0; - for (int i = 0; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - if (this_link->type == LINK_UNINITIALISED_FILE) { - Link_req_file_stat(linktbl->links[i]); - u++; - } - } - /* - * Block until the gaps are filled - */ - int n = curl_multi_perform_once(); - int i = 0; - int j = 0; - while ((i = curl_multi_perform_once())) { - if (CONFIG.log_type & debug) { - if (j) { - erase_string(stderr, STATUS_LEN, s); - } - snprintf(s, STATUS_LEN, "%d / %d", n - i, n); - fprintf(stderr, "%s", s); - j++; - } - } + int u; + char s[STATUS_LEN]; + lprintf(debug, "LinkTable_uninitialised_fill(): ... "); + do { + u = 0; + for (int i = 0; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + if (this_link->type == LINK_UNINITIALISED_FILE) { + Link_req_file_stat(linktbl->links[i]); + u++; + } } - while (u); - if (CONFIG.log_type & debug) { - erase_string(stderr, STATUS_LEN, s); - fprintf(stderr, "Done!\n"); + /* + * Block until the gaps are filled + */ + int n = curl_multi_perform_once(); + int i = 0; + int j = 0; + while ((i = curl_multi_perform_once())) { + if (CONFIG.log_type & debug) { + if (j) { + erase_string(stderr, STATUS_LEN, s); + } + snprintf(s, STATUS_LEN, "%d / %d", n - i, n); + fprintf(stderr, "%s", s); + j++; + } } + } + while (u); + if (CONFIG.log_type & debug) { + erase_string(stderr, STATUS_LEN, s); + fprintf(stderr, "Done!\n"); + } } static void LinkTable_fill(LinkTable * linktbl) { - Link *head_link = linktbl->links[0]; - for (int i = 1; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - char *url; - url = path_append(head_link->f_url, this_link->linkname); - strncpy(this_link->f_url, url, MAX_PATH_LEN); - FREE(url); - char *unescaped_linkname; - CURL *c = curl_easy_init(); - unescaped_linkname = curl_easy_unescape(c, this_link->linkname, - 0, NULL); - strncpy(this_link->linkname, unescaped_linkname, - MAX_FILENAME_LEN); - curl_free(unescaped_linkname); - curl_easy_cleanup(c); - } - LinkTable_uninitialised_fill(linktbl); + Link *head_link = linktbl->links[0]; + for (int i = 1; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + char *url; + url = path_append(head_link->f_url, this_link->linkname); + strncpy(this_link->f_url, url, MAX_PATH_LEN); + FREE(url); + char *unescaped_linkname; + CURL *c = curl_easy_init(); + unescaped_linkname = curl_easy_unescape(c, this_link->linkname, + 0, NULL); + strncpy(this_link->linkname, unescaped_linkname, MAX_FILENAME_LEN); + curl_free(unescaped_linkname); + curl_easy_cleanup(c); + } + LinkTable_uninitialised_fill(linktbl); } /** @@ -383,490 +382,479 @@ static void LinkTable_fill(LinkTable * linktbl) */ static void LinkTable_invalid_reset(LinkTable * linktbl) { - int j = 0; - for (int i = 0; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - if (this_link->type == LINK_INVALID) { - this_link->type = LINK_UNINITIALISED_FILE; - j++; - } + int j = 0; + for (int i = 0; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + if (this_link->type == LINK_INVALID) { + this_link->type = LINK_UNINITIALISED_FILE; + j++; } - lprintf(debug, "%d invalid links\n", j); + } + lprintf(debug, "%d invalid links\n", j); } void LinkTable_free(LinkTable * linktbl) { - for (int i = 0; i < linktbl->num; i++) { - FREE(linktbl->links[i]); - } - FREE(linktbl->links); - FREE(linktbl); + for (int i = 0; i < linktbl->num; i++) { + FREE(linktbl->links[i]); + } + FREE(linktbl->links); + FREE(linktbl); } void LinkTable_print(LinkTable * linktbl) { - if (CONFIG.log_type & info) { - int j = 0; - lprintf(info, "--------------------------------------------\n"); - lprintf(info, " LinkTable %p for %s\n", linktbl, - linktbl->links[0]->f_url); - lprintf(info, "--------------------------------------------\n"); - for (int i = 0; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - lprintf(info, "%d %c %lu %s %s\n", - i, - this_link->type, - this_link->content_length, - this_link->linkname, this_link->f_url); - if ((this_link->type != LINK_FILE) - && (this_link->type != LINK_DIR) - && (this_link->type != LINK_HEAD)) { - j++; - } - } - lprintf(info, "--------------------------------------------\n"); - lprintf(info, "LinkTable_print(): Invalid link count: %d\n", j); - lprintf(info, "--------------------------------------------\n"); + if (CONFIG.log_type & info) { + int j = 0; + lprintf(info, "--------------------------------------------\n"); + lprintf(info, " LinkTable %p for %s\n", linktbl, + linktbl->links[0]->f_url); + lprintf(info, "--------------------------------------------\n"); + for (int i = 0; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + lprintf(info, "%d %c %lu %s %s\n", + i, + this_link->type, + this_link->content_length, + this_link->linkname, this_link->f_url); + if ((this_link->type != LINK_FILE) + && (this_link->type != LINK_DIR) + && (this_link->type != LINK_HEAD)) { + j++; + } } + lprintf(info, "--------------------------------------------\n"); + lprintf(info, "LinkTable_print(): Invalid link count: %d\n", j); + lprintf(info, "--------------------------------------------\n"); + } } DataStruct Link_to_DataStruct(Link * head_link) { - char *url = head_link->f_url; - CURL *curl = Link_to_curl(head_link); + char *url = head_link->f_url; + CURL *curl = Link_to_curl(head_link); - DataStruct buf; - buf.size = 0; - buf.data = NULL; + DataStruct buf; + buf.size = 0; + buf.data = NULL; - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buf); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &buf); - /* - * If we get temporary HTTP failure, wait for 5 seconds before retry - */ - long http_resp = 0; - do { - transfer_blocking(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (HTTP_temp_failure(http_resp)) { - lprintf(warning, - "URL: %s, HTTP %ld, retrying later.\n", - url, http_resp); - sleep(CONFIG.http_wait_sec); - } else if (http_resp != HTTP_OK) { - lprintf(warning, - "cannot retrieve URL: %s, HTTP %ld\n", - url, http_resp); - buf.size = 0; - curl_easy_cleanup(curl); - return buf; - } + /* + * If we get temporary HTTP failure, wait for 5 seconds before retry + */ + long http_resp = 0; + do { + transfer_blocking(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (HTTP_temp_failure(http_resp)) { + lprintf(warning, + "URL: %s, HTTP %ld, retrying later.\n", + url, http_resp); + sleep(CONFIG.http_wait_sec); + } else if (http_resp != HTTP_OK) { + lprintf(warning, + "cannot retrieve URL: %s, HTTP %ld\n", url, http_resp); + buf.size = 0; + curl_easy_cleanup(curl); + return buf; } - while (HTTP_temp_failure(http_resp)); + } + while (HTTP_temp_failure(http_resp)); - curl_easy_getinfo(curl, CURLINFO_FILETIME, &(head_link->time)); - curl_easy_cleanup(curl); - return buf; + curl_easy_getinfo(curl, CURLINFO_FILETIME, &(head_link->time)); + curl_easy_cleanup(curl); + return buf; } LinkTable *LinkTable_alloc(const char *url) { - LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); - /* - * populate the base URL - */ - Link *head_link = Link_new("/", LINK_HEAD); - LinkTable_add(linktbl, head_link); - strncpy(head_link->f_url, url, MAX_PATH_LEN); - assert(linktbl->num == 1); - return linktbl; + /* + * populate the base URL + */ + Link *head_link = Link_new("/", LINK_HEAD); + LinkTable_add(linktbl, head_link); + strncpy(head_link->f_url, url, MAX_PATH_LEN); + assert(linktbl->num == 1); + return linktbl; } LinkTable *LinkTable_new(const char *url) { - LinkTable *linktbl = LinkTable_alloc(url); + LinkTable *linktbl = LinkTable_alloc(url); - /* - * start downloading the base URL - */ - DataStruct buf = Link_to_DataStruct(linktbl->links[0]); - if (buf.size == 0) { + /* + * start downloading the base URL + */ + DataStruct buf = Link_to_DataStruct(linktbl->links[0]); + if (buf.size == 0) { + LinkTable_free(linktbl); + return NULL; + } + + /* + * Otherwise parsed the received data + */ + GumboOutput *output = gumbo_parse(buf.data); + HTML_to_LinkTable(output->root, linktbl); + gumbo_destroy_output(&kGumboDefaultOptions, output); + FREE(buf.data); + + int skip_fill = 0; + char *unescaped_path; + CURL *c = curl_easy_init(); + unescaped_path = + curl_easy_unescape(c, url + ROOT_LINK_OFFSET, 0, NULL); + if (CACHE_SYSTEM_INIT) { + CacheDir_create(unescaped_path); + LinkTable *disk_linktbl; + disk_linktbl = LinkTable_disk_open(unescaped_path); + if (disk_linktbl) { + /* + * Check if we need to update the link table + */ + lprintf(debug, + "disk_linktbl->num: %d, linktbl->num: %d\n", + disk_linktbl->num, linktbl->num); + if (disk_linktbl->num == linktbl->num) { LinkTable_free(linktbl); - return NULL; + linktbl = disk_linktbl; + skip_fill = 1; + } else { + LinkTable_free(disk_linktbl); + } } + } + if (!skip_fill) { /* - * Otherwise parsed the received data + * Fill in the link table */ - GumboOutput *output = gumbo_parse(buf.data); - HTML_to_LinkTable(output->root, linktbl); - gumbo_destroy_output(&kGumboDefaultOptions, output); - FREE(buf.data); - - int skip_fill = 0; - char *unescaped_path; - CURL *c = curl_easy_init(); - unescaped_path = curl_easy_unescape(c, url + ROOT_LINK_OFFSET, 0, NULL); - if (CACHE_SYSTEM_INIT) { - CacheDir_create(unescaped_path); - LinkTable *disk_linktbl; - disk_linktbl = LinkTable_disk_open(unescaped_path); - if (disk_linktbl) { - /* - * Check if we need to update the link table - */ - lprintf(debug, - "disk_linktbl->num: %d, linktbl->num: %d\n", - disk_linktbl->num, linktbl->num); - if (disk_linktbl->num == linktbl->num) { - LinkTable_free(linktbl); - linktbl = disk_linktbl; - skip_fill = 1; - } else { - LinkTable_free(disk_linktbl); - } - } - } - - if (!skip_fill) { - /* - * Fill in the link table - */ - LinkTable_fill(linktbl); - } else { - /* - * Fill in the holes in the link table - */ - LinkTable_invalid_reset(linktbl); - LinkTable_uninitialised_fill(linktbl); - } - + LinkTable_fill(linktbl); + } else { /* - * Save the link table + * Fill in the holes in the link table */ - if (CACHE_SYSTEM_INIT) { - if (LinkTable_disk_save(linktbl, unescaped_path)) { - exit_failure(); - } + LinkTable_invalid_reset(linktbl); + LinkTable_uninitialised_fill(linktbl); + } + + /* + * Save the link table + */ + if (CACHE_SYSTEM_INIT) { + if (LinkTable_disk_save(linktbl, unescaped_path)) { + exit_failure(); } + } - curl_free(unescaped_path); - curl_easy_cleanup(c); + curl_free(unescaped_path); + curl_easy_cleanup(c); - LinkTable_print(linktbl); + LinkTable_print(linktbl); - return linktbl; + return linktbl; } static void LinkTable_disk_delete(const char *dirn) { - char *metadirn = path_append(META_DIR, dirn); - char *path; - path = path_append(metadirn, "/.LinkTable"); - if (unlink(path)) { - lprintf(error, "unlink(%s): %s\n", path, strerror(errno)); - } - FREE(path); - FREE(metadirn); + char *metadirn = path_append(META_DIR, dirn); + char *path; + path = path_append(metadirn, "/.LinkTable"); + if (unlink(path)) { + lprintf(error, "unlink(%s): %s\n", path, strerror(errno)); + } + FREE(path); + FREE(metadirn); } int LinkTable_disk_save(LinkTable * linktbl, const char *dirn) { - char *metadirn = path_append(META_DIR, dirn); - char *path; - path = path_append(metadirn, "/.LinkTable"); - FILE *fp = fopen(path, "w"); - FREE(metadirn); + char *metadirn = path_append(META_DIR, dirn); + char *path; + path = path_append(metadirn, "/.LinkTable"); + FILE *fp = fopen(path, "w"); + FREE(metadirn); - if (!fp) { - lprintf(error, "fopen(%s): %s\n", path, strerror(errno)); - FREE(path); - return -1; - } + if (!fp) { + lprintf(error, "fopen(%s): %s\n", path, strerror(errno)); FREE(path); + return -1; + } + FREE(path); - fwrite(&linktbl->num, sizeof(int), 1, fp); - for (int i = 0; i < linktbl->num; i++) { - fwrite(linktbl->links[i]->linkname, sizeof(char), - MAX_FILENAME_LEN, fp); - fwrite(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, - fp); - fwrite(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); - fwrite(&linktbl->links[i]->content_length, sizeof(size_t), 1, - fp); - fwrite(&linktbl->links[i]->time, sizeof(long), 1, fp); - } + fwrite(&linktbl->num, sizeof(int), 1, fp); + for (int i = 0; i < linktbl->num; i++) { + fwrite(linktbl->links[i]->linkname, sizeof(char), + MAX_FILENAME_LEN, fp); + fwrite(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp); + fwrite(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); + fwrite(&linktbl->links[i]->content_length, sizeof(size_t), 1, fp); + fwrite(&linktbl->links[i]->time, sizeof(long), 1, fp); + } - int res = 0; + int res = 0; - if (ferror(fp)) { - lprintf(error, "encountered ferror!\n"); - res = -1; - } + if (ferror(fp)) { + lprintf(error, "encountered ferror!\n"); + res = -1; + } - if (fclose(fp)) { - lprintf(error, - "cannot close the file pointer, %s\n", strerror(errno)); - res = -1; - } + if (fclose(fp)) { + lprintf(error, + "cannot close the file pointer, %s\n", strerror(errno)); + res = -1; + } - return res; + return res; } LinkTable *LinkTable_disk_open(const char *dirn) { - char *metadirn = path_append(META_DIR, dirn); - char *path; - if (metadirn[strnlen(metadirn, MAX_PATH_LEN)] == '/') { - path = path_append(metadirn, ".LinkTable"); - } else { - path = path_append(metadirn, "/.LinkTable"); - } - FILE *fp = fopen(path, "r"); - FREE(metadirn); + char *metadirn = path_append(META_DIR, dirn); + char *path; + if (metadirn[strnlen(metadirn, MAX_PATH_LEN)] == '/') { + path = path_append(metadirn, ".LinkTable"); + } else { + path = path_append(metadirn, "/.LinkTable"); + } + FILE *fp = fopen(path, "r"); + FREE(metadirn); - if (!fp) { - FREE(path); - return NULL; - } + if (!fp) { + FREE(path); + return NULL; + } - LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); - fread(&linktbl->num, sizeof(int), 1, fp); - linktbl->links = CALLOC(linktbl->num, sizeof(Link *)); - for (int i = 0; i < linktbl->num; i++) { - linktbl->links[i] = CALLOC(1, sizeof(Link)); - fread(linktbl->links[i]->linkname, sizeof(char), - MAX_FILENAME_LEN, fp); - fread(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp); - fread(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); - fread(&linktbl->links[i]->content_length, sizeof(size_t), 1, - fp); - fread(&linktbl->links[i]->time, sizeof(long), 1, fp); - if (feof(fp)) { - /* - * reached EOF - */ - lprintf(error, "reached EOF!\n"); - LinkTable_free(linktbl); - LinkTable_disk_delete(dirn); - return NULL; - } - if (ferror(fp)) { - lprintf(error, "encountered ferror!\n"); - LinkTable_free(linktbl); - LinkTable_disk_delete(dirn); - return NULL; - } + fread(&linktbl->num, sizeof(int), 1, fp); + linktbl->links = CALLOC(linktbl->num, sizeof(Link *)); + for (int i = 0; i < linktbl->num; i++) { + linktbl->links[i] = CALLOC(1, sizeof(Link)); + fread(linktbl->links[i]->linkname, sizeof(char), + MAX_FILENAME_LEN, fp); + fread(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp); + fread(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); + fread(&linktbl->links[i]->content_length, sizeof(size_t), 1, fp); + fread(&linktbl->links[i]->time, sizeof(long), 1, fp); + if (feof(fp)) { + /* + * reached EOF + */ + lprintf(error, "reached EOF!\n"); + LinkTable_free(linktbl); + LinkTable_disk_delete(dirn); + return NULL; } - if (fclose(fp)) { - lprintf(error, - "cannot close the file pointer, %s\n", strerror(errno)); + if (ferror(fp)) { + lprintf(error, "encountered ferror!\n"); + LinkTable_free(linktbl); + LinkTable_disk_delete(dirn); + return NULL; } - return linktbl; + } + if (fclose(fp)) { + lprintf(error, + "cannot close the file pointer, %s\n", strerror(errno)); + } + return linktbl; } LinkTable *path_to_Link_LinkTable_new(const char *path) { - Link *link = path_to_Link(path); - LinkTable *next_table = link->next_table; - if (!next_table) { - if (CONFIG.mode == NORMAL) { - next_table = LinkTable_new(link->f_url); - } else if (CONFIG.mode == SONIC) { - if (!CONFIG.sonic_id3) { - next_table = - sonic_LinkTable_new_index(link->sonic_id); - } else { - next_table = - sonic_LinkTable_new_id3(link->sonic_depth, - link->sonic_id); - } - } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); - } + Link *link = path_to_Link(path); + LinkTable *next_table = link->next_table; + if (!next_table) { + if (CONFIG.mode == NORMAL) { + next_table = LinkTable_new(link->f_url); + } else if (CONFIG.mode == SONIC) { + if (!CONFIG.sonic_id3) { + next_table = sonic_LinkTable_new_index(link->sonic_id); + } else { + next_table = + sonic_LinkTable_new_id3(link->sonic_depth, + link->sonic_id); + } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); } - link->next_table = next_table; - return next_table; + } + link->next_table = next_table; + return next_table; } static Link *path_to_Link_recursive(char *path, LinkTable * linktbl) { + /* + * skip the leading '/' if it exists + */ + if (*path == '/') { + path++; + } + + /* + * remove the last '/' if it exists + */ + char *slash = &(path[strnlen(path, MAX_PATH_LEN) - 1]); + if (*slash == '/') { + *slash = '\0'; + } + + slash = strchr(path, '/'); + if (slash == NULL) { /* - * skip the leading '/' if it exists + * We cannot find another '/', we have reached the last level */ - if (*path == '/') { - path++; + for (int i = 1; i < linktbl->num; i++) { + if (!strncmp + (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { + /* + * We found our link + */ + return linktbl->links[i]; + } } + } else { + /* + * We can still find '/', time to consume the path and traverse + * the tree structure + */ /* - * remove the last '/' if it exists + * add termination mark to the current string, + * effective create two substrings */ - char *slash = &(path[strnlen(path, MAX_PATH_LEN) - 1]); - if (*slash == '/') { - *slash = '\0'; - } - - slash = strchr(path, '/'); - if (slash == NULL) { + *slash = '\0'; + /* + * move the pointer past the '/' + */ + char *next_path = slash + 1; + for (int i = 1; i < linktbl->num; i++) { + if (!strncmp + (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { /* - * We cannot find another '/', we have reached the last level + * The next sub-directory exists */ - for (int i = 1; i < linktbl->num; i++) { - if (!strncmp - (path, linktbl->links[i]->linkname, - MAX_FILENAME_LEN)) { - /* - * We found our link - */ - return linktbl->links[i]; - } - } - } else { - /* - * We can still find '/', time to consume the path and traverse - * the tree structure - */ - - /* - * add termination mark to the current string, - * effective create two substrings - */ - *slash = '\0'; - /* - * move the pointer past the '/' - */ - char *next_path = slash + 1; - for (int i = 1; i < linktbl->num; i++) { - if (!strncmp - (path, linktbl->links[i]->linkname, - MAX_FILENAME_LEN)) { - /* - * The next sub-directory exists - */ - LinkTable *next_table = - linktbl->links[i]->next_table; - if (!next_table) { - if (CONFIG.mode == NORMAL) { - next_table = - LinkTable_new(linktbl->links - [i]->f_url); - } else if (CONFIG.mode == SONIC) { - if (!CONFIG.sonic_id3) { - next_table = - sonic_LinkTable_new_index - (linktbl->links - [i]->sonic_id); - } else { - next_table = - sonic_LinkTable_new_id3 - (linktbl->links - [i]->sonic_depth, - linktbl->links - [i]->sonic_id); - } - } else { - lprintf(fatal, - "Invalid CONFIG.mode\n"); - } - } - linktbl->links[i]->next_table = next_table; - return path_to_Link_recursive(next_path, - next_table); + LinkTable *next_table = linktbl->links[i]->next_table; + if (!next_table) { + if (CONFIG.mode == NORMAL) { + next_table = + LinkTable_new(linktbl->links[i]->f_url); + } else if (CONFIG.mode == SONIC) { + if (!CONFIG.sonic_id3) { + next_table = + sonic_LinkTable_new_index + (linktbl->links[i]->sonic_id); + } else { + next_table = + sonic_LinkTable_new_id3 + (linktbl->links + [i]->sonic_depth, + linktbl->links[i]->sonic_id); } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } } + linktbl->links[i]->next_table = next_table; + return path_to_Link_recursive(next_path, next_table); + } } - return NULL; + } + return NULL; } Link *path_to_Link(const char *path) { - lprintf(link_lock_debug, - "thread %x: locking link_lock;\n", pthread_self()); + lprintf(link_lock_debug, + "thread %x: locking link_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&link_lock); - char *new_path = strndup(path, MAX_PATH_LEN); - if (!new_path) { - lprintf(fatal, "cannot allocate memory\n"); - } - Link *link = path_to_Link_recursive(new_path, ROOT_LINK_TBL); - FREE(new_path); + PTHREAD_MUTEX_LOCK(&link_lock); + char *new_path = strndup(path, MAX_PATH_LEN); + if (!new_path) { + lprintf(fatal, "cannot allocate memory\n"); + } + Link *link = path_to_Link_recursive(new_path, ROOT_LINK_TBL); + FREE(new_path); - lprintf(link_lock_debug, - "thread %x: unlocking link_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&link_lock); - return link; + lprintf(link_lock_debug, + "thread %x: unlocking link_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&link_lock); + return link; } long -path_download(const char *path, char *output_buf, size_t size, off_t offset) +path_download(const char *path, char *output_buf, size_t size, + off_t offset) { - if (!path) { - lprintf(fatal, "\npath_download(): NULL path supplied\n"); - } - Link *link; - link = path_to_Link(path); - if (!link) { - return -ENOENT; - } + if (!path) { + lprintf(fatal, "\npath_download(): NULL path supplied\n"); + } + Link *link; + link = path_to_Link(path); + if (!link) { + return -ENOENT; + } - size_t start = offset; - size_t end = start + size; - char range_str[64]; - snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end); - lprintf(debug, "path_download(%s, %s);\n", path, range_str); + size_t start = offset; + size_t end = start + size; + char range_str[64]; + snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end); + lprintf(debug, "path_download(%s, %s);\n", path, range_str); - DataStruct buf; - buf.size = 0; - buf.data = NULL; + DataStruct buf; + buf.size = 0; + buf.data = NULL; - CURL *curl = Link_to_curl(link); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buf); - curl_easy_setopt(curl, CURLOPT_RANGE, range_str); + CURL *curl = Link_to_curl(link); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &buf); + curl_easy_setopt(curl, CURLOPT_RANGE, range_str); - DataStruct header; - header.size = 0; - header.data = NULL; - curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&header); + DataStruct header; + header.size = 0; + header.data = NULL; + curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) &header); - transfer_blocking(curl); + transfer_blocking(curl); - /* - * Check for range seek support - */ - if (!CONFIG.no_range_check) { - if (!strcasestr((header.data), "Accept-Ranges: bytes")) { - fprintf(stderr, "This web server does not support HTTP \ + /* + * Check for range seek support + */ + if (!CONFIG.no_range_check) { + if (!strcasestr((header.data), "Accept-Ranges: bytes")) { + fprintf(stderr, "This web server does not support HTTP \ range requests\n"); - exit(EXIT_FAILURE); - } + exit(EXIT_FAILURE); } + } - FREE(header.data); + FREE(header.data); - long http_resp; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (!((http_resp != HTTP_OK) || - (http_resp != HTTP_PARTIAL_CONTENT) || - (http_resp != HTTP_RANGE_NOT_SATISFIABLE))) { - lprintf(warning, - "Could not download %s, HTTP %ld\n", - link->f_url, http_resp); - return -ENOENT; - } + long http_resp; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (!((http_resp != HTTP_OK) || + (http_resp != HTTP_PARTIAL_CONTENT) || + (http_resp != HTTP_RANGE_NOT_SATISFIABLE))) { + lprintf(warning, + "Could not download %s, HTTP %ld\n", + link->f_url, http_resp); + return -ENOENT; + } - double dl; - curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &dl); + double dl; + curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &dl); - size_t recv = dl; - if (recv > size) { - recv = size; - } + size_t recv = dl; + if (recv > size) { + recv = size; + } - memmove(output_buf, buf.data, recv); - curl_easy_cleanup(curl); - FREE(buf.data); + memmove(output_buf, buf.data, recv); + curl_easy_cleanup(curl); + FREE(buf.data); - return recv; + return recv; } diff --git a/src/link.h b/src/link.h index 16ab612..7786d86 100644 --- a/src/link.h +++ b/src/link.h @@ -16,30 +16,30 @@ typedef struct Link Link; /** \brief the link type */ typedef enum { - LINK_HEAD = 'H', - LINK_DIR = 'D', - LINK_FILE = 'F', - LINK_INVALID = 'I', - LINK_UNINITIALISED_FILE = 'U' + LINK_HEAD = 'H', + LINK_DIR = 'D', + LINK_FILE = 'F', + LINK_INVALID = 'I', + LINK_UNINITIALISED_FILE = 'U' } LinkType; /** \brief for storing downloaded data in memory */ typedef struct { - char *data; - size_t size; + char *data; + size_t size; } DataStruct; /** \brief specify the type of data transfer */ typedef enum { - FILESTAT = 's', - DATA = 'd' + FILESTAT = 's', + DATA = 'd' } TransferType; /** \brief for storing the link being transferred, and metadata */ typedef struct { - TransferType type; - int transferring; - Link *link; + TransferType type; + int transferring; + Link *link; } TransferStruct; /** @@ -53,21 +53,21 @@ typedef struct LinkTable LinkTable; */ struct Link { /** \brief The link name in the last level of the URL */ - char linkname[MAX_FILENAME_LEN + 1]; + char linkname[MAX_FILENAME_LEN + 1]; /** \brief The full URL of the file */ - char f_url[MAX_PATH_LEN + 1]; + char f_url[MAX_PATH_LEN + 1]; /** \brief The type of the link */ - LinkType type; + LinkType type; /** \brief CURLINFO_CONTENT_LENGTH_DOWNLOAD of the file */ - size_t content_length; + size_t content_length; /** \brief The next LinkTable level, if it is a LINK_DIR */ - LinkTable *next_table; + LinkTable *next_table; /** \brief CURLINFO_FILETIME obtained from the server */ - long time; + long time; /** \brief How many times associated cache has been opened */ - int cache_opened; + int cache_opened; /** \brief The pointer associated with the cache file */ - Cache *cache_ptr; + Cache *cache_ptr; /** * \brief Sonic id field * \details This is used to store the followings: @@ -77,18 +77,18 @@ struct Link { * - Sub-directory ID (in the XML response, this is the ID on the "child" * element) */ - char *sonic_id; + char *sonic_id; /** * \brief Sonic directory depth * \details This is used exclusively in ID3 mode to store the depth of the * current directory. */ - int sonic_depth; + int sonic_depth; }; struct LinkTable { - int num; - Link **links; + int num; + Link **links; }; /** diff --git a/src/log.c b/src/log.c index afb4b9e..83faf29 100644 --- a/src/log.c +++ b/src/log.c @@ -9,50 +9,50 @@ int log_level_init() { - char *env = getenv("HTTPDIRFS_LOG_LEVEL"); - if (env) { - return atoi(env); - } - return DEFAULT_LOG_LEVEL; + char *env = getenv("HTTPDIRFS_LOG_LEVEL"); + if (env) { + return atoi(env); + } + return DEFAULT_LOG_LEVEL; } void log_printf(LogType type, const char *file, const char *func, int line, const char *format, ...) { - FILE *out = stderr; - if (type & CONFIG.log_type) { - switch (type) { - case fatal: - fprintf(out, "Fatal: "); - break; - case error: - fprintf(out, "Error: "); - break; - case warning: - fprintf(out, "Warning: "); - break; - case info: - out = stderr; - goto print_actual_message; - break; - default: - fprintf(out, "Debug (%x):", type); - break; - } - - fprintf(out, "(%s:%s:%d): ", file, func, line); - - print_actual_message: - { - } - va_list args; - va_start(args, format); - vfprintf(out, format, args); - va_end(args); - - if (type == fatal) { - exit_failure(); - } + FILE *out = stderr; + if (type & CONFIG.log_type) { + switch (type) { + case fatal: + fprintf(out, "Fatal: "); + break; + case error: + fprintf(out, "Error: "); + break; + case warning: + fprintf(out, "Warning: "); + break; + case info: + out = stderr; + goto print_actual_message; + break; + default: + fprintf(out, "Debug (%x):", type); + break; } + + fprintf(out, "(%s:%s:%d): ", file, func, line); + + print_actual_message: + { + } + va_list args; + va_start(args, format); + vfprintf(out, format, args); + va_end(args); + + if (type == fatal) { + exit_failure(); + } + } } diff --git a/src/log.h b/src/log.h index 30eeccb..e907b52 100644 --- a/src/log.h +++ b/src/log.h @@ -5,14 +5,14 @@ * \brief Log types */ typedef enum { - fatal = 1 << 0, - error = 1 << 1, - warning = 1 << 2, - info = 1 << 3, - debug = 1 << 4, - link_lock_debug = 1 << 5, - network_lock_debug = 1 << 6, - cache_lock_debug = 1 << 7, + fatal = 1 << 0, + error = 1 << 1, + warning = 1 << 2, + info = 1 << 3, + debug = 1 << 4, + link_lock_debug = 1 << 5, + network_lock_debug = 1 << 6, + cache_lock_debug = 1 << 7, } LogType; /** diff --git a/src/main.c b/src/main.c index 7c9a56d..cec24fb 100644 --- a/src/main.c +++ b/src/main.c @@ -20,301 +20,294 @@ static char *config_path = NULL; int main(int argc, char **argv) { - /* - * Automatically print help if not enough arguments are supplied - */ - if (argc < 2) { - print_help(argv[0], 0); - fprintf(stderr, "For more information, run \"%s --help.\"\n", - argv[0]); - exit(EXIT_FAILURE); - } + /* + * Automatically print help if not enough arguments are supplied + */ + if (argc < 2) { + print_help(argv[0], 0); + fprintf(stderr, "For more information, run \"%s --help.\"\n", + argv[0]); + exit(EXIT_FAILURE); + } - /* - * These are passed into fuse initialiser - */ - char **fuse_argv = NULL; - int fuse_argc = 0; - /* - * These are the combined argument with the config file - */ - char **all_argv = NULL; - int all_argc = 0; + /* + * These are passed into fuse initialiser + */ + char **fuse_argv = NULL; + int fuse_argc = 0; + /* + * These are the combined argument with the config file + */ + char **all_argv = NULL; + int all_argc = 0; /*--- Add the program's name to the combined argument list ---*/ - add_arg(&all_argv, &all_argc, argv[0]); + add_arg(&all_argv, &all_argc, argv[0]); /*--- FUSE expects the first initialisation to be the program's name ---*/ - add_arg(&fuse_argv, &fuse_argc, argv[0]); + add_arg(&fuse_argv, &fuse_argc, argv[0]); - /* - * initialise network configuration struct - */ - Config_init(); + /* + * initialise network configuration struct + */ + Config_init(); - /* - * initialise network subsystem - */ - NetworkSystem_init(); + /* + * initialise network subsystem + */ + NetworkSystem_init(); - /* - * Copy the command line argument list to the combined argument list - */ - for (int i = 1; i < argc; i++) { - add_arg(&all_argv, &all_argc, argv[i]); - if (!strcmp(argv[i], "--config")) { - config_path = strdup(argv[i + 1]); - } + /* + * Copy the command line argument list to the combined argument list + */ + for (int i = 1; i < argc; i++) { + add_arg(&all_argv, &all_argc, argv[i]); + if (!strcmp(argv[i], "--config")) { + config_path = strdup(argv[i + 1]); } + } - /* - * parse the config file, if it exists, store it in all_argv and - * all_argc - */ - parse_config_file(&all_argv, &all_argc); + /* + * parse the config file, if it exists, store it in all_argv and + * all_argc + */ + parse_config_file(&all_argv, &all_argc); + /* + * parse the combined argument list + */ + if (parse_arg_list(all_argc, all_argv, &fuse_argv, &fuse_argc)) { /* - * parse the combined argument list + * The user basically didn't supply enough arguments, if we reach here + * The point is to print some error messages */ - if (parse_arg_list(all_argc, all_argv, &fuse_argv, &fuse_argc)) { - /* - * The user basically didn't supply enough arguments, if we reach here - * The point is to print some error messages - */ - goto fuse_start; - } + goto fuse_start; + } /*--- Add the last remaining argument, which is the mountpoint ---*/ - add_arg(&fuse_argv, &fuse_argc, argv[argc - 1]); + add_arg(&fuse_argv, &fuse_argc, argv[argc - 1]); - /* - * The second last remaining argument is the URL - */ - char *base_url = argv[argc - 2]; - if (strncmp(base_url, "http://", 7) && strncmp(base_url, "https://", 8)) { - fprintf(stderr, "Error: Please supply a valid URL.\n"); - print_help(argv[0], 0); - exit(EXIT_FAILURE); - } else { - if (CONFIG.sonic_username && CONFIG.sonic_password) { - CONFIG.mode = SONIC; - } else if (CONFIG.sonic_username || CONFIG.sonic_password) { - fprintf(stderr, - "Error: You have to supply both username and password to \ + /* + * The second last remaining argument is the URL + */ + char *base_url = argv[argc - 2]; + if (strncmp(base_url, "http://", 7) + && strncmp(base_url, "https://", 8)) { + fprintf(stderr, "Error: Please supply a valid URL.\n"); + print_help(argv[0], 0); + exit(EXIT_FAILURE); + } else { + if (CONFIG.sonic_username && CONFIG.sonic_password) { + CONFIG.mode = SONIC; + } else if (CONFIG.sonic_username || CONFIG.sonic_password) { + fprintf(stderr, + "Error: You have to supply both username and password to \ activate Sonic mode.\n"); - exit(EXIT_FAILURE); - } - if (!LinkSystem_init(base_url)) { - fprintf(stderr, "Network initialisation failed.\n"); - exit(EXIT_FAILURE); - } + exit(EXIT_FAILURE); } + if (!LinkSystem_init(base_url)) { + fprintf(stderr, "Network initialisation failed.\n"); + exit(EXIT_FAILURE); + } + } - fuse_start: - fuse_local_init(fuse_argc, fuse_argv); + fuse_start: + fuse_local_init(fuse_argc, fuse_argv); - return 0; + return 0; } void parse_config_file(char ***argv, int *argc) { - char *full_path; - if (!config_path) { - char *xdg_config_home = getenv("XDG_CONFIG_HOME"); - if (!xdg_config_home) { - char *home = getenv("HOME"); - char *xdg_config_home_default = "/.config"; - xdg_config_home = - path_append(home, xdg_config_home_default); - } - full_path = path_append(xdg_config_home, "/httpdirfs/config"); - } else { - full_path = config_path; + char *full_path; + if (!config_path) { + char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + if (!xdg_config_home) { + char *home = getenv("HOME"); + char *xdg_config_home_default = "/.config"; + xdg_config_home = path_append(home, xdg_config_home_default); } + full_path = path_append(xdg_config_home, "/httpdirfs/config"); + } else { + full_path = config_path; + } - /* - * The buffer has to be able to fit a URL - */ - int buf_len = MAX_PATH_LEN; - char buf[buf_len]; - FILE *config = fopen(full_path, "r"); - if (config) { - while (fgets(buf, buf_len, config)) { - if (buf[0] == '-') { - (*argc)++; - buf[strnlen(buf, buf_len) - 1] = '\0'; - char *space; - space = strchr(buf, ' '); - if (!space) { - *argv = - realloc(*argv, - *argc * sizeof(char **)); - (*argv)[*argc - 1] = - strndup(buf, buf_len); - } else { - (*argc)++; - *argv = - realloc(*argv, - *argc * sizeof(char **)); - /* - * Only copy up to the space character - */ - (*argv)[*argc - 2] = - strndup(buf, space - buf); - /* - * Starts copying after the space - */ - (*argv)[*argc - 1] = strndup(space + 1, - buf_len - - (space + - 1 - buf)); - } - } + /* + * The buffer has to be able to fit a URL + */ + int buf_len = MAX_PATH_LEN; + char buf[buf_len]; + FILE *config = fopen(full_path, "r"); + if (config) { + while (fgets(buf, buf_len, config)) { + if (buf[0] == '-') { + (*argc)++; + buf[strnlen(buf, buf_len) - 1] = '\0'; + char *space; + space = strchr(buf, ' '); + if (!space) { + *argv = realloc(*argv, *argc * sizeof(char **)); + (*argv)[*argc - 1] = strndup(buf, buf_len); + } else { + (*argc)++; + *argv = realloc(*argv, *argc * sizeof(char **)); + /* + * Only copy up to the space character + */ + (*argv)[*argc - 2] = strndup(buf, space - buf); + /* + * Starts copying after the space + */ + (*argv)[*argc - 1] = strndup(space + 1, + buf_len - + (space + 1 - buf)); } + } } - FREE(full_path); + } + FREE(full_path); } static int parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) { - char c; - int long_index = 0; - const char *short_opts = "o:hVdfsp:u:P:"; - const struct option long_opts[] = { + char c; + int long_index = 0; + const char *short_opts = "o:hVdfsp:u:P:"; + const struct option long_opts[] = { + /* + * Note that 'L' is returned for long options + */ + { "help", no_argument, NULL, 'h' }, /* 0 */ + { "version", no_argument, NULL, 'V' }, /* 1 */ + { "debug", no_argument, NULL, 'd' }, /* 2 */ + { "username", required_argument, NULL, 'u' }, /* 3 */ + { "password", required_argument, NULL, 'p' }, /* 4 */ + { "proxy", required_argument, NULL, 'P' }, /* 5 */ + { "proxy-username", required_argument, NULL, 'L' }, /* 6 */ + { "proxy-password", required_argument, NULL, 'L' }, /* 7 */ + { "cache", no_argument, NULL, 'L' }, /* 8 */ + { "dl-seg-size", required_argument, NULL, 'L' }, /* 9 */ + { "max-seg-count", required_argument, NULL, 'L' }, /* 10 */ + { "max-conns", required_argument, NULL, 'L' }, /* 11 */ + { "user-agent", required_argument, NULL, 'L' }, /* 12 */ + { "retry-wait", required_argument, NULL, 'L' }, /* 13 */ + { "cache-location", required_argument, NULL, 'L' }, /* 14 */ + { "sonic-username", required_argument, NULL, 'L' }, /* 15 */ + { "sonic-password", required_argument, NULL, 'L' }, /* 16 */ + { "sonic-id3", no_argument, NULL, 'L' }, /* 17 */ + { "no-range-check", no_argument, NULL, 'L' }, /* 18 */ + { "sonic-insecure", no_argument, NULL, 'L' }, /* 19 */ + { "insecure-tls", no_argument, NULL, 'L' }, /* 20 */ + { "config", required_argument, NULL, 'L' }, /* 21 */ + { "single-file-mode", required_argument, NULL, 'L' }, /* 22 */ + { 0, 0, 0, 0 } + }; + while ((c = + getopt_long(argc, argv, short_opts, long_opts, + &long_index)) != -1) { + switch (c) { + case 'o': + add_arg(fuse_argv, fuse_argc, "-o"); + add_arg(fuse_argv, fuse_argc, optarg); + break; + case 'h': + print_help(argv[0], 1); + add_arg(fuse_argv, fuse_argc, "-ho"); + /* + * skip everything else to print the help + */ + return 1; + case 'V': + print_version(argv[0], 1); + add_arg(fuse_argv, fuse_argc, "-V"); + return 1; + case 'd': + add_arg(fuse_argv, fuse_argc, "-d"); + break; + case 'f': + add_arg(fuse_argv, fuse_argc, "-f"); + break; + case 's': + add_arg(fuse_argv, fuse_argc, "-s"); + break; + case 'u': + CONFIG.http_username = strdup(optarg); + break; + case 'p': + CONFIG.http_password = strdup(optarg); + break; + case 'P': + CONFIG.proxy = strdup(optarg); + break; + case 'L': + /* + * Long options + */ + switch (long_index) { + case 6: + CONFIG.proxy_username = strdup(optarg); + break; + case 7: + CONFIG.proxy_password = strdup(optarg); + break; + case 8: + CONFIG.cache_enabled = 1; + break; + case 9: + CONFIG.data_blksz = atoi(optarg) * 1024 * 1024; + break; + case 10: + CONFIG.max_segbc = atoi(optarg); + break; + case 11: + CONFIG.max_conns = atoi(optarg); + break; + case 12: + CONFIG.user_agent = strdup(optarg); + break; + case 13: + CONFIG.http_wait_sec = atoi(optarg); + break; + case 14: + CONFIG.cache_dir = strdup(optarg); + break; + case 15: + CONFIG.sonic_username = strdup(optarg); + break; + case 16: + CONFIG.sonic_password = strdup(optarg); + break; + case 17: + CONFIG.sonic_id3 = 1; + break; + case 18: + CONFIG.no_range_check = 1; + break; + case 19: + CONFIG.sonic_insecure = 1; + break; + case 20: + CONFIG.insecure_tls = 1; + break; + case 21: /* - * Note that 'L' is returned for long options + * This is for --config, we don't need to do anything */ - {"help", no_argument, NULL, 'h'}, /* 0 */ - {"version", no_argument, NULL, 'V'}, /* 1 */ - {"debug", no_argument, NULL, 'd'}, /* 2 */ - {"username", required_argument, NULL, 'u'}, /* 3 */ - {"password", required_argument, NULL, 'p'}, /* 4 */ - {"proxy", required_argument, NULL, 'P'}, /* 5 */ - {"proxy-username", required_argument, NULL, 'L'}, /* 6 */ - {"proxy-password", required_argument, NULL, 'L'}, /* 7 */ - {"cache", no_argument, NULL, 'L'}, /* 8 */ - {"dl-seg-size", required_argument, NULL, 'L'}, /* 9 */ - {"max-seg-count", required_argument, NULL, 'L'}, /* 10 */ - {"max-conns", required_argument, NULL, 'L'}, /* 11 */ - {"user-agent", required_argument, NULL, 'L'}, /* 12 */ - {"retry-wait", required_argument, NULL, 'L'}, /* 13 */ - {"cache-location", required_argument, NULL, 'L'}, /* 14 */ - {"sonic-username", required_argument, NULL, 'L'}, /* 15 */ - {"sonic-password", required_argument, NULL, 'L'}, /* 16 */ - {"sonic-id3", no_argument, NULL, 'L'}, /* 17 */ - {"no-range-check", no_argument, NULL, 'L'}, /* 18 */ - {"sonic-insecure", no_argument, NULL, 'L'}, /* 19 */ - {"insecure-tls", no_argument, NULL, 'L'}, /* 20 */ - {"config", required_argument, NULL, 'L'}, /* 21 */ - {"single-file-mode", required_argument, NULL, 'L'}, /* 22 */ - {0, 0, 0, 0} - }; - while ((c = - getopt_long(argc, argv, short_opts, long_opts, - &long_index)) != -1) { - switch (c) { - case 'o': - add_arg(fuse_argv, fuse_argc, "-o"); - add_arg(fuse_argv, fuse_argc, optarg); - break; - case 'h': - print_help(argv[0], 1); - add_arg(fuse_argv, fuse_argc, "-ho"); - /* - * skip everything else to print the help - */ - return 1; - case 'V': - print_version(argv[0], 1); - add_arg(fuse_argv, fuse_argc, "-V"); - return 1; - case 'd': - add_arg(fuse_argv, fuse_argc, "-d"); - break; - case 'f': - add_arg(fuse_argv, fuse_argc, "-f"); - break; - case 's': - add_arg(fuse_argv, fuse_argc, "-s"); - break; - case 'u': - CONFIG.http_username = strdup(optarg); - break; - case 'p': - CONFIG.http_password = strdup(optarg); - break; - case 'P': - CONFIG.proxy = strdup(optarg); - break; - case 'L': - /* - * Long options - */ - switch (long_index) { - case 6: - CONFIG.proxy_username = strdup(optarg); - break; - case 7: - CONFIG.proxy_password = strdup(optarg); - break; - case 8: - CONFIG.cache_enabled = 1; - break; - case 9: - CONFIG.data_blksz = atoi(optarg) * 1024 * 1024; - break; - case 10: - CONFIG.max_segbc = atoi(optarg); - break; - case 11: - CONFIG.max_conns = atoi(optarg); - break; - case 12: - CONFIG.user_agent = strdup(optarg); - break; - case 13: - CONFIG.http_wait_sec = atoi(optarg); - break; - case 14: - CONFIG.cache_dir = strdup(optarg); - break; - case 15: - CONFIG.sonic_username = strdup(optarg); - break; - case 16: - CONFIG.sonic_password = strdup(optarg); - break; - case 17: - CONFIG.sonic_id3 = 1; - break; - case 18: - CONFIG.no_range_check = 1; - break; - case 19: - CONFIG.sonic_insecure = 1; - break; - case 20: - CONFIG.insecure_tls = 1; - break; - case 21: - /* - * This is for --config, we don't need to do anything - */ - break; - case 22: - CONFIG.mode = SINGLE_FILE; - break; - default: - fprintf(stderr, "see httpdirfs -h for usage\n"); - return 1; - } - break; - default: - fprintf(stderr, "see httpdirfs -h for usage\n"); - return 1; - } - }; - return 0; + break; + case 22: + CONFIG.mode = SINGLE_FILE; + break; + default: + fprintf(stderr, "see httpdirfs -h for usage\n"); + return 1; + } + break; + default: + fprintf(stderr, "see httpdirfs -h for usage\n"); + return 1; + } + }; + return 0; } /** @@ -323,33 +316,33 @@ parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) */ void add_arg(char ***fuse_argv_ptr, int *fuse_argc, char *opt_string) { - (*fuse_argc)++; - *fuse_argv_ptr = realloc(*fuse_argv_ptr, *fuse_argc * sizeof(char *)); - char **fuse_argv = *fuse_argv_ptr; - fuse_argv[*fuse_argc - 1] = strdup(opt_string); + (*fuse_argc)++; + *fuse_argv_ptr = realloc(*fuse_argv_ptr, *fuse_argc * sizeof(char *)); + char **fuse_argv = *fuse_argv_ptr; + fuse_argv[*fuse_argc - 1] = strdup(opt_string); } static void print_help(char *program_name, int long_help) { - printf("usage: %s [options] URL mountpoint\n", program_name); - if (long_help) { - print_long_help(); - } + printf("usage: %s [options] URL mountpoint\n", program_name); + if (long_help) { + print_long_help(); + } } static void print_version() { - printf("HTTPDirFS version " VERSION "\n"); - /* - * --------- Print off SSL engine version --------- - */ - curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); - printf("libcurl SSL engine: %s\n", data->ssl_version); + printf("HTTPDirFS version " VERSION "\n"); + /* + * --------- Print off SSL engine version --------- + */ + curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); + printf("libcurl SSL engine: %s\n", data->ssl_version); } static void print_long_help() { - printf("\n\ + printf("\n\ general options:\n\ --config Specify a configuration file \n\ -o opt,[opt...] Mount options\n\ diff --git a/src/network.c b/src/network.c index 5da75bb..ed21d30 100644 --- a/src/network.c +++ b/src/network.c @@ -39,13 +39,13 @@ static pthread_mutex_t curl_lock; */ static void crypto_lock_callback(int mode, int type, char *file, int line) { - (void)file; - (void)line; - if (mode & CRYPTO_LOCK) { - PTHREAD_MUTEX_LOCK(&(crypto_lockarray[type])); - } else { - PTHREAD_MUTEX_UNLOCK(&(crypto_lockarray[type])); - } + (void) file; + (void) line; + if (mode & CRYPTO_LOCK) { + PTHREAD_MUTEX_LOCK(&(crypto_lockarray[type])); + } else { + PTHREAD_MUTEX_UNLOCK(&(crypto_lockarray[type])); + } } /** @@ -54,30 +54,30 @@ static void crypto_lock_callback(int mode, int type, char *file, int line) */ static unsigned long thread_id(void) { - unsigned long ret; + unsigned long ret; - ret = (unsigned long)pthread_self(); - return ret; + ret = (unsigned long) pthread_self(); + return ret; } #pragma GCC diagnostic pop static void crypto_lock_init(void) { - int i; + int i; - crypto_lockarray = - (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * - sizeof(pthread_mutex_t)); - for (i = 0; i < CRYPTO_num_locks(); i++) { - if (pthread_mutex_init(&(crypto_lockarray[i]), NULL)) { - lprintf(fatal, "crypto_lockarray[%d] initialisation \ + crypto_lockarray = + (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * + sizeof(pthread_mutex_t)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + if (pthread_mutex_init(&(crypto_lockarray[i]), NULL)) { + lprintf(fatal, "crypto_lockarray[%d] initialisation \ failed!\n", i); - }; - } + }; + } - CRYPTO_set_id_callback((unsigned long (*)())thread_id); - CRYPTO_set_locking_callback((void (*)())crypto_lock_callback); + CRYPTO_set_id_callback((unsigned long (*)()) thread_id); + CRYPTO_set_locking_callback((void (*)()) crypto_lock_callback); } /** @@ -89,20 +89,20 @@ static void curl_callback_lock(CURL * handle, curl_lock_data data, curl_lock_access access, void *userptr) { - (void)access; /* unused */ - (void)userptr; /* unused */ - (void)handle; /* unused */ - (void)data; /* unused */ - PTHREAD_MUTEX_LOCK(&curl_lock); + (void) access; /* unused */ + (void) userptr; /* unused */ + (void) handle; /* unused */ + (void) data; /* unused */ + PTHREAD_MUTEX_LOCK(&curl_lock); } static void curl_callback_unlock(CURL * handle, curl_lock_data data, void *userptr) { - (void)userptr; /* unused */ - (void)handle; /* unused */ - (void)data; /* unused */ - PTHREAD_MUTEX_UNLOCK(&curl_lock); + (void) userptr; /* unused */ + (void) handle; /* unused */ + (void) data; /* unused */ + PTHREAD_MUTEX_UNLOCK(&curl_lock); } /** @@ -113,58 +113,58 @@ curl_callback_unlock(CURL * handle, curl_lock_data data, void *userptr) static void curl_process_msgs(CURLMsg * curl_msg, int n_running_curl, int n_mesgs) { - (void)n_running_curl; - (void)n_mesgs; - static volatile int slept = 0; - if (curl_msg->msg == CURLMSG_DONE) { - TransferStruct *transfer; - CURL *curl = curl_msg->easy_handle; - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, - &transfer); - transfer->transferring = 0; - char *url = NULL; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + (void) n_running_curl; + (void) n_mesgs; + static volatile int slept = 0; + if (curl_msg->msg == CURLMSG_DONE) { + TransferStruct *transfer; + CURL *curl = curl_msg->easy_handle; + curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, + &transfer); + transfer->transferring = 0; + char *url = NULL; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - /* - * Wait for 5 seconds if we get HTTP 429 - */ - long http_resp = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (HTTP_temp_failure(http_resp)) { - if (!slept) { - lprintf(warning, - "HTTP %ld, sleeping for %d sec\n", - http_resp, CONFIG.http_wait_sec); - sleep(CONFIG.http_wait_sec); - slept = 1; - } - } else { - slept = 0; - } - - if (!curl_msg->data.result) { - /* - * Transfer successful, set the file size - */ - if (transfer->type == FILESTAT) { - Link_set_file_stat(transfer->link, curl); - } - } else { - lprintf(error, "%d - %s <%s>\n", - curl_msg->data.result, - curl_easy_strerror(curl_msg->data.result), url); - } - curl_multi_remove_handle(curl_multi, curl); - /* - * clean up the handle, if we are querying the file size - */ - if (transfer->type == FILESTAT) { - curl_easy_cleanup(curl); - FREE(transfer); - } + /* + * Wait for 5 seconds if we get HTTP 429 + */ + long http_resp = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (HTTP_temp_failure(http_resp)) { + if (!slept) { + lprintf(warning, + "HTTP %ld, sleeping for %d sec\n", + http_resp, CONFIG.http_wait_sec); + sleep(CONFIG.http_wait_sec); + slept = 1; + } } else { - lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg); + slept = 0; } + + if (!curl_msg->data.result) { + /* + * Transfer successful, set the file size + */ + if (transfer->type == FILESTAT) { + Link_set_file_stat(transfer->link, curl); + } + } else { + lprintf(error, "%d - %s <%s>\n", + curl_msg->data.result, + curl_easy_strerror(curl_msg->data.result), url); + } + curl_multi_remove_handle(curl_multi, curl); + /* + * clean up the handle, if we are querying the file size + */ + if (transfer->type == FILESTAT) { + curl_easy_cleanup(curl); + FREE(transfer); + } + } else { + lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg); + } } /** @@ -173,215 +173,215 @@ curl_process_msgs(CURLMsg * curl_msg, int n_running_curl, int n_mesgs) */ int curl_multi_perform_once(void) { - lprintf(network_lock_debug, - "thread %x: locking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&transfer_lock); + lprintf(network_lock_debug, + "thread %x: locking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&transfer_lock); - /* - * Get curl multi interface to perform pending tasks - */ - int n_running_curl; - CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl); - if (mc > 0) { - lprintf(error, "%s\n", curl_multi_strerror(mc)); - } + /* + * Get curl multi interface to perform pending tasks + */ + int n_running_curl; + CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl); + if (mc > 0) { + lprintf(error, "%s\n", curl_multi_strerror(mc)); + } - fd_set fdread; - fd_set fdwrite; - fd_set fdexcep; - int maxfd = -1; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd = -1; - long curl_timeo = -1; + long curl_timeo = -1; - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - FD_ZERO(&fdexcep); + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); - /* - * set a default timeout for select() - */ - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; + /* + * set a default timeout for select() + */ + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; - curl_multi_timeout(curl_multi, &curl_timeo); - /* - * We effectively cap timeout to 1 sec - */ - if (curl_timeo >= 0) { - timeout.tv_sec = curl_timeo / 1000; - if (timeout.tv_sec > 1) { - timeout.tv_sec = 1; - } else { - timeout.tv_usec = (curl_timeo % 1000) * 1000; - } - } - - /* - * get file descriptors from the transfers - */ - mc = curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd); - - if (mc > 0) { - lprintf(error, "%s.\n", curl_multi_strerror(mc)); - } - - if (maxfd == -1) { - usleep(100 * 1000); + curl_multi_timeout(curl_multi, &curl_timeo); + /* + * We effectively cap timeout to 1 sec + */ + if (curl_timeo >= 0) { + timeout.tv_sec = curl_timeo / 1000; + if (timeout.tv_sec > 1) { + timeout.tv_sec = 1; } else { - if (select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) < - 0) { - lprintf(error, "select(): %s.\n", strerror(errno)); - } + timeout.tv_usec = (curl_timeo % 1000) * 1000; } + } - /* - * Process the message queue - */ - int n_mesgs; - CURLMsg *curl_msg; - while ((curl_msg = curl_multi_info_read(curl_multi, &n_mesgs))) { - curl_process_msgs(curl_msg, n_running_curl, n_mesgs); + /* + * get file descriptors from the transfers + */ + mc = curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd); + + if (mc > 0) { + lprintf(error, "%s.\n", curl_multi_strerror(mc)); + } + + if (maxfd == -1) { + usleep(100 * 1000); + } else { + if (select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) < 0) { + lprintf(error, "select(): %s.\n", strerror(errno)); } + } - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); + /* + * Process the message queue + */ + int n_mesgs; + CURLMsg *curl_msg; + while ((curl_msg = curl_multi_info_read(curl_multi, &n_mesgs))) { + curl_process_msgs(curl_msg, n_running_curl, n_mesgs); + } - return n_running_curl; + lprintf(network_lock_debug, + "thread %x: unlocking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); + + return n_running_curl; } void NetworkSystem_init(void) { - /* - * ------- Global related ---------- - */ - if (curl_global_init(CURL_GLOBAL_ALL)) { - lprintf(fatal, "curl_global_init() failed!\n"); - } + /* + * ------- Global related ---------- + */ + if (curl_global_init(CURL_GLOBAL_ALL)) { + lprintf(fatal, "curl_global_init() failed!\n"); + } - /* - * -------- Share related ---------- - */ - CURL_SHARE = curl_share_init(); - if (!(CURL_SHARE)) { - lprintf(fatal, "curl_share_init() failed!\n"); - } + /* + * -------- Share related ---------- + */ + CURL_SHARE = curl_share_init(); + if (!(CURL_SHARE)) { + lprintf(fatal, "curl_share_init() failed!\n"); + } - curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); - curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); - curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, - CURL_LOCK_DATA_SSL_SESSION); + curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, + CURL_LOCK_DATA_SSL_SESSION); - if (pthread_mutex_init(&curl_lock, NULL)) { - lprintf(fatal, "curl_lock initialisation failed!\n"); - } - curl_share_setopt(CURL_SHARE, CURLSHOPT_LOCKFUNC, curl_callback_lock); - curl_share_setopt(CURL_SHARE, CURLSHOPT_UNLOCKFUNC, - curl_callback_unlock); + if (pthread_mutex_init(&curl_lock, NULL)) { + lprintf(fatal, "curl_lock initialisation failed!\n"); + } + curl_share_setopt(CURL_SHARE, CURLSHOPT_LOCKFUNC, curl_callback_lock); + curl_share_setopt(CURL_SHARE, CURLSHOPT_UNLOCKFUNC, + curl_callback_unlock); - /* - * ------------- Multi related ----------- - */ - curl_multi = curl_multi_init(); - if (!curl_multi) { - lprintf(fatal, "curl_multi_init() failed!\n"); - } - curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, - CONFIG.max_conns); - curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS, - CONFIG.max_conns); + /* + * ------------- Multi related ----------- + */ + curl_multi = curl_multi_init(); + if (!curl_multi) { + lprintf(fatal, "curl_multi_init() failed!\n"); + } + curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, + CONFIG.max_conns); + curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS, + CONFIG.max_conns); - /* - * ------------ Initialise locks --------- - */ - if (pthread_mutex_init(&transfer_lock, NULL)) { - lprintf(fatal, "transfer_lock initialisation failed!\n"); - } + /* + * ------------ Initialise locks --------- + */ + if (pthread_mutex_init(&transfer_lock, NULL)) { + lprintf(fatal, "transfer_lock initialisation failed!\n"); + } - /* - * cryptographic lock functions were shamelessly copied from - * https://curl.haxx.se/libcurl/c/threaded-ssl.html - */ - crypto_lock_init(); + /* + * cryptographic lock functions were shamelessly copied from + * https://curl.haxx.se/libcurl/c/threaded-ssl.html + */ + crypto_lock_init(); } void transfer_blocking(CURL * curl) { - /* - * We don't need to malloc here, as the transfer is finished before - * the variable gets popped from the stack - */ - volatile TransferStruct transfer; - transfer.type = DATA; - transfer.transferring = 1; - curl_easy_setopt(curl, CURLOPT_PRIVATE, &transfer); + /* + * We don't need to malloc here, as the transfer is finished before + * the variable gets popped from the stack + */ + volatile TransferStruct transfer; + transfer.type = DATA; + transfer.transferring = 1; + curl_easy_setopt(curl, CURLOPT_PRIVATE, &transfer); - lprintf(network_lock_debug, - "thread %x: locking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&transfer_lock); + lprintf(network_lock_debug, + "thread %x: locking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&transfer_lock); - CURLMcode res = curl_multi_add_handle(curl_multi, curl); + CURLMcode res = curl_multi_add_handle(curl_multi, curl); - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); + lprintf(network_lock_debug, + "thread %x: unlocking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); - if (res > 0) { - lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); - } + if (res > 0) { + lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); + } - while (transfer.transferring) { - curl_multi_perform_once(); - } + while (transfer.transferring) { + curl_multi_perform_once(); + } } void transfer_nonblocking(CURL * curl) { - lprintf(network_lock_debug, - "thread %x: locking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&transfer_lock); + lprintf(network_lock_debug, + "thread %x: locking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&transfer_lock); - CURLMcode res = curl_multi_add_handle(curl_multi, curl); + CURLMcode res = curl_multi_add_handle(curl_multi, curl); - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); + lprintf(network_lock_debug, + "thread %x: unlocking transfer_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); - if (res > 0) { - lprintf(error, "%s\n", curl_multi_strerror(res)); - } + if (res > 0) { + lprintf(error, "%s\n", curl_multi_strerror(res)); + } } size_t -write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp) +write_memory_callback(void *contents, size_t size, size_t nmemb, + void *userp) { - size_t realsize = size * nmemb; - DataStruct *mem = (DataStruct *) userp; + size_t realsize = size * nmemb; + DataStruct *mem = (DataStruct *) userp; - mem->data = realloc(mem->data, mem->size + realsize + 1); - if (!mem->data) { - /* - * out of memory! - */ - lprintf(fatal, "realloc failure!\n"); - } + mem->data = realloc(mem->data, mem->size + realsize + 1); + if (!mem->data) { + /* + * out of memory! + */ + lprintf(fatal, "realloc failure!\n"); + } - memmove(&mem->data[mem->size], contents, realsize); - mem->size += realsize; - mem->data[mem->size] = 0; + memmove(&mem->data[mem->size], contents, realsize); + mem->size += realsize; + mem->data[mem->size] = 0; - return realsize; + return realsize; } int HTTP_temp_failure(HTTPResponseCode http_resp) { - switch (http_resp) { - case HTTP_TOO_MANY_REQUESTS: - case HTTP_CLOUDFLARE_UNKNOWN_ERROR: - case HTTP_CLOUDFLARE_TIMEOUT: - return 1; - default: - return 0; - } + switch (http_resp) { + case HTTP_TOO_MANY_REQUESTS: + case HTTP_CLOUDFLARE_UNKNOWN_ERROR: + case HTTP_CLOUDFLARE_TIMEOUT: + return 1; + default: + return 0; + } } diff --git a/src/network.h b/src/network.h index 68576ab..15c2dc5 100644 --- a/src/network.h +++ b/src/network.h @@ -10,12 +10,12 @@ /** \brief HTTP response codes */ typedef enum { - HTTP_OK = 200, - HTTP_PARTIAL_CONTENT = 206, - HTTP_RANGE_NOT_SATISFIABLE = 416, - HTTP_TOO_MANY_REQUESTS = 429, - HTTP_CLOUDFLARE_UNKNOWN_ERROR = 520, - HTTP_CLOUDFLARE_TIMEOUT = 524 + HTTP_OK = 200, + HTTP_PARTIAL_CONTENT = 206, + HTTP_RANGE_NOT_SATISFIABLE = 416, + HTTP_TOO_MANY_REQUESTS = 429, + HTTP_CLOUDFLARE_UNKNOWN_ERROR = 520, + HTTP_CLOUDFLARE_TIMEOUT = 524 } HTTPResponseCode; /** \brief curl shared interface */ @@ -35,7 +35,8 @@ void transfer_nonblocking(CURL * curl); /** \brief callback function for file transfer */ size_t -write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp); +write_memory_callback(void *contents, size_t size, size_t nmemb, + void *userp); /** * \brief check if a HTTP response code corresponds to a temporary failure diff --git a/src/sonic.c b/src/sonic.c index 51d109e..8975217 100644 --- a/src/sonic.c +++ b/src/sonic.c @@ -14,11 +14,11 @@ #include typedef struct { - char *server; - char *username; - char *password; - char *client; - char *api_version; + char *server; + char *username; + char *password; + char *client; + char *api_version; } SonicConfigStruct; static SonicConfigStruct SONIC_CONFIG; @@ -30,30 +30,30 @@ void sonic_config_init(const char *server, const char *username, const char *password) { - SONIC_CONFIG.server = strndup(server, MAX_PATH_LEN); - /* - * Correct for the extra '/' - */ - size_t server_url_len = strnlen(SONIC_CONFIG.server, MAX_PATH_LEN) - 1; - if (SONIC_CONFIG.server[server_url_len] == '/') { - SONIC_CONFIG.server[server_url_len] = '\0'; - } - SONIC_CONFIG.username = strndup(username, MAX_FILENAME_LEN); - SONIC_CONFIG.password = strndup(password, MAX_FILENAME_LEN); - SONIC_CONFIG.client = DEFAULT_USER_AGENT; + SONIC_CONFIG.server = strndup(server, MAX_PATH_LEN); + /* + * Correct for the extra '/' + */ + size_t server_url_len = strnlen(SONIC_CONFIG.server, MAX_PATH_LEN) - 1; + if (SONIC_CONFIG.server[server_url_len] == '/') { + SONIC_CONFIG.server[server_url_len] = '\0'; + } + SONIC_CONFIG.username = strndup(username, MAX_FILENAME_LEN); + SONIC_CONFIG.password = strndup(password, MAX_FILENAME_LEN); + SONIC_CONFIG.client = DEFAULT_USER_AGENT; - if (!CONFIG.sonic_insecure) { - /* - * API 1.13.0 is the minimum version that supports - * salt authentication scheme - */ - SONIC_CONFIG.api_version = "1.13.0"; - } else { - /* - * API 1.8.0 is the minimum version that supports ID3 mode - */ - SONIC_CONFIG.api_version = "1.8.0"; - } + if (!CONFIG.sonic_insecure) { + /* + * API 1.13.0 is the minimum version that supports + * salt authentication scheme + */ + SONIC_CONFIG.api_version = "1.13.0"; + } else { + /* + * API 1.8.0 is the minimum version that supports ID3 mode + */ + SONIC_CONFIG.api_version = "1.8.0"; + } } /** @@ -61,33 +61,32 @@ sonic_config_init(const char *server, const char *username, */ static char *sonic_gen_auth_str(void) { - if (!CONFIG.sonic_insecure) { - char *salt = generate_salt(); - size_t pwd_len = - strnlen(SONIC_CONFIG.password, MAX_FILENAME_LEN); - size_t pwd_salt_len = pwd_len + strnlen(salt, MAX_FILENAME_LEN); - char *pwd_salt = CALLOC(pwd_salt_len + 1, sizeof(char)); - strncat(pwd_salt, SONIC_CONFIG.password, MAX_FILENAME_LEN); - strncat(pwd_salt + pwd_len, salt, MAX_FILENAME_LEN); - char *token = generate_md5sum(pwd_salt); - char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(auth_str, MAX_PATH_LEN, - ".view?u=%s&t=%s&s=%s&v=%s&c=%s", - SONIC_CONFIG.username, token, salt, - SONIC_CONFIG.api_version, SONIC_CONFIG.client); - FREE(salt); - FREE(token); - return auth_str; - } else { - char *pwd_hex = str_to_hex(SONIC_CONFIG.password); - char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(auth_str, MAX_PATH_LEN, - ".view?u=%s&p=enc:%s&v=%s&c=%s", - SONIC_CONFIG.username, pwd_hex, - SONIC_CONFIG.api_version, SONIC_CONFIG.client); - FREE(pwd_hex); - return auth_str; - } + if (!CONFIG.sonic_insecure) { + char *salt = generate_salt(); + size_t pwd_len = strnlen(SONIC_CONFIG.password, MAX_FILENAME_LEN); + size_t pwd_salt_len = pwd_len + strnlen(salt, MAX_FILENAME_LEN); + char *pwd_salt = CALLOC(pwd_salt_len + 1, sizeof(char)); + strncat(pwd_salt, SONIC_CONFIG.password, MAX_FILENAME_LEN); + strncat(pwd_salt + pwd_len, salt, MAX_FILENAME_LEN); + char *token = generate_md5sum(pwd_salt); + char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(auth_str, MAX_PATH_LEN, + ".view?u=%s&t=%s&s=%s&v=%s&c=%s", + SONIC_CONFIG.username, token, salt, + SONIC_CONFIG.api_version, SONIC_CONFIG.client); + FREE(salt); + FREE(token); + return auth_str; + } else { + char *pwd_hex = str_to_hex(SONIC_CONFIG.password); + char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(auth_str, MAX_PATH_LEN, + ".view?u=%s&p=enc:%s&v=%s&c=%s", + SONIC_CONFIG.username, pwd_hex, + SONIC_CONFIG.api_version, SONIC_CONFIG.client); + FREE(pwd_hex); + return auth_str; + } } /** @@ -95,12 +94,12 @@ static char *sonic_gen_auth_str(void) */ static char *sonic_gen_url_first_part(char *method) { - char *auth_str = sonic_gen_auth_str(); - char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(url, MAX_PATH_LEN, "%s/rest/%s%s", SONIC_CONFIG.server, - method, auth_str); - FREE(auth_str); - return url; + char *auth_str = sonic_gen_auth_str(); + char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(url, MAX_PATH_LEN, "%s/rest/%s%s", SONIC_CONFIG.server, + method, auth_str); + FREE(auth_str); + return url; } /** @@ -108,11 +107,11 @@ static char *sonic_gen_url_first_part(char *method) */ static char *sonic_getMusicDirectory_link(const char *id) { - char *first_part = sonic_gen_url_first_part("getMusicDirectory"); - char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); - FREE(first_part); - return url; + char *first_part = sonic_gen_url_first_part("getMusicDirectory"); + char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); + FREE(first_part); + return url; } /** @@ -120,11 +119,11 @@ static char *sonic_getMusicDirectory_link(const char *id) */ static char *sonic_getArtist_link(const char *id) { - char *first_part = sonic_gen_url_first_part("getArtist"); - char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); - FREE(first_part); - return url; + char *first_part = sonic_gen_url_first_part("getArtist"); + char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); + FREE(first_part); + return url; } /** @@ -132,11 +131,11 @@ static char *sonic_getArtist_link(const char *id) */ static char *sonic_getAlbum_link(const char *id) { - char *first_part = sonic_gen_url_first_part("getAlbum"); - char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); - FREE(first_part); - return url; + char *first_part = sonic_gen_url_first_part("getAlbum"); + char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); + FREE(first_part); + return url; } /** @@ -144,11 +143,11 @@ static char *sonic_getAlbum_link(const char *id) */ static char *sonic_stream_link(const char *id) { - char *first_part = sonic_gen_url_first_part("stream"); - char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(url, MAX_PATH_LEN, "%s&format=raw&id=%s", first_part, id); - FREE(first_part); - return url; + char *first_part = sonic_gen_url_first_part("stream"); + char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(url, MAX_PATH_LEN, "%s&format=raw&id=%s", first_part, id); + FREE(first_part); + return url; } /** @@ -168,158 +167,155 @@ static char *sonic_stream_link(const char *id) static void XMLCALL XML_parser_general(void *data, const char *elem, const char **attr) { - /* - * Error checking - */ - if (!strcmp(elem, "error")) { - lprintf(error, "error:\n"); - for (int i = 0; attr[i]; i += 2) { - lprintf(error, "%s: %s\n", attr[i], attr[i + 1]); - } - } - - LinkTable *linktbl = (LinkTable *) data; - Link *link; - - /* - * Please refer to the documentation at the function prototype of - * sonic_LinkTable_new_id3() - */ - if (!strcmp(elem, "child")) { - link = CALLOC(1, sizeof(Link)); - /* - * Initialise to LINK_DIR, as the LINK_FILE is set later. - */ - link->type = LINK_DIR; - } else if (!strcmp(elem, "artist") - && linktbl->links[0]->sonic_depth != 3) { - /* - * We want to skip the first "artist" element in the album table - */ - link = CALLOC(1, sizeof(Link)); - link->type = LINK_DIR; - } else if (!strcmp(elem, "album") - && linktbl->links[0]->sonic_depth == 3) { - link = CALLOC(1, sizeof(Link)); - link->type = LINK_DIR; - /* - * The new table should be a level 4 song table - */ - link->sonic_depth = 4; - } else if (!strcmp(elem, "song") && linktbl->links[0]->sonic_depth == 4) { - link = CALLOC(1, sizeof(Link)); - link->type = LINK_FILE; - } else { - /* - * The element does not contain directory structural information - */ - return; - } - - int id_set = 0; - int linkname_set = 0; - - int track = 0; - char *title = ""; - char *suffix = ""; + /* + * Error checking + */ + if (!strcmp(elem, "error")) { + lprintf(error, "error:\n"); for (int i = 0; attr[i]; i += 2) { - if (!strcmp("id", attr[i])) { - link->sonic_id = - CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); - strncpy(link->sonic_id, attr[i + 1], MAX_FILENAME_LEN); - id_set = 1; - continue; - } + lprintf(error, "%s: %s\n", attr[i], attr[i + 1]); + } + } - if (!strcmp("path", attr[i])) { - memset(link->linkname, 0, MAX_FILENAME_LEN); - /* - * Skip to the last '/' if it exists - */ - char *s = strrchr(attr[i + 1], '/'); - if (s) { - strncpy(link->linkname, s + 1, - MAX_FILENAME_LEN); - } else { - strncpy(link->linkname, attr[i + 1], - MAX_FILENAME_LEN); - } - linkname_set = 1; - continue; - } + LinkTable *linktbl = (LinkTable *) data; + Link *link; - /* - * "title" is used for directory name, - * "name" is for top level directories - * N.B. "path" attribute is given the preference - */ - if (!linkname_set) { - if (!strcmp("title", attr[i]) - || !strcmp("name", attr[i])) { - strncpy(link->linkname, attr[i + 1], - MAX_FILENAME_LEN); - linkname_set = 1; - continue; - } - } + /* + * Please refer to the documentation at the function prototype of + * sonic_LinkTable_new_id3() + */ + if (!strcmp(elem, "child")) { + link = CALLOC(1, sizeof(Link)); + /* + * Initialise to LINK_DIR, as the LINK_FILE is set later. + */ + link->type = LINK_DIR; + } else if (!strcmp(elem, "artist") + && linktbl->links[0]->sonic_depth != 3) { + /* + * We want to skip the first "artist" element in the album table + */ + link = CALLOC(1, sizeof(Link)); + link->type = LINK_DIR; + } else if (!strcmp(elem, "album") + && linktbl->links[0]->sonic_depth == 3) { + link = CALLOC(1, sizeof(Link)); + link->type = LINK_DIR; + /* + * The new table should be a level 4 song table + */ + link->sonic_depth = 4; + } else if (!strcmp(elem, "song") + && linktbl->links[0]->sonic_depth == 4) { + link = CALLOC(1, sizeof(Link)); + link->type = LINK_FILE; + } else { + /* + * The element does not contain directory structural information + */ + return; + } - if (!strcmp("isDir", attr[i])) { - if (!strcmp("false", attr[i + 1])) { - link->type = LINK_FILE; - } - continue; - } + int id_set = 0; + int linkname_set = 0; - if (!strcmp("created", attr[i])) { - struct tm *tm = CALLOC(1, sizeof(struct tm)); - strptime(attr[i + 1], "%Y-%m-%dT%H:%M:%S.000Z", tm); - link->time = mktime(tm); - FREE(tm); - continue; - } - - if (!strcmp("size", attr[i])) { - link->content_length = atoll(attr[i + 1]); - continue; - } - - if (!strcmp("track", attr[i])) { - track = atoi(attr[i + 1]); - continue; - } - - if (!strcmp("title", attr[i])) { - title = (char *)attr[i + 1]; - continue; - } - - if (!strcmp("suffix", attr[i])) { - suffix = (char *)attr[i + 1]; - continue; - } + int track = 0; + char *title = ""; + char *suffix = ""; + for (int i = 0; attr[i]; i += 2) { + if (!strcmp("id", attr[i])) { + link->sonic_id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); + strncpy(link->sonic_id, attr[i + 1], MAX_FILENAME_LEN); + id_set = 1; + continue; } - if (!linkname_set && strlen(title) > 0 && strlen(suffix) > 0) { - snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s", - track, title, suffix); - linkname_set = 1; + if (!strcmp("path", attr[i])) { + memset(link->linkname, 0, MAX_FILENAME_LEN); + /* + * Skip to the last '/' if it exists + */ + char *s = strrchr(attr[i + 1], '/'); + if (s) { + strncpy(link->linkname, s + 1, MAX_FILENAME_LEN); + } else { + strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); + } + linkname_set = 1; + continue; } /* - * Clean up if linkname or id is not set + * "title" is used for directory name, + * "name" is for top level directories + * N.B. "path" attribute is given the preference */ - if (!linkname_set || !id_set) { - FREE(link); - return; + if (!linkname_set) { + if (!strcmp("title", attr[i]) + || !strcmp("name", attr[i])) { + strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); + linkname_set = 1; + continue; + } } - if (link->type == LINK_FILE) { - char *url = sonic_stream_link(link->sonic_id); - strncpy(link->f_url, url, MAX_PATH_LEN); - FREE(url); + if (!strcmp("isDir", attr[i])) { + if (!strcmp("false", attr[i + 1])) { + link->type = LINK_FILE; + } + continue; } - LinkTable_add(linktbl, link); + if (!strcmp("created", attr[i])) { + struct tm *tm = CALLOC(1, sizeof(struct tm)); + strptime(attr[i + 1], "%Y-%m-%dT%H:%M:%S.000Z", tm); + link->time = mktime(tm); + FREE(tm); + continue; + } + + if (!strcmp("size", attr[i])) { + link->content_length = atoll(attr[i + 1]); + continue; + } + + if (!strcmp("track", attr[i])) { + track = atoi(attr[i + 1]); + continue; + } + + if (!strcmp("title", attr[i])) { + title = (char *) attr[i + 1]; + continue; + } + + if (!strcmp("suffix", attr[i])) { + suffix = (char *) attr[i + 1]; + continue; + } + } + + if (!linkname_set && strlen(title) > 0 && strlen(suffix) > 0) { + snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s", + track, title, suffix); + linkname_set = 1; + } + + /* + * Clean up if linkname or id is not set + */ + if (!linkname_set || !id_set) { + FREE(link); + return; + } + + if (link->type == LINK_FILE) { + char *url = sonic_stream_link(link->sonic_id); + strncpy(link->f_url, url, MAX_PATH_LEN); + FREE(url); + } + + LinkTable_add(linktbl, link); } /** @@ -329,181 +325,177 @@ static LinkTable *sonic_url_to_LinkTable(const char *url, XML_StartElementHandler handler, int depth) { - LinkTable *linktbl = LinkTable_alloc(url); - linktbl->links[0]->sonic_depth = depth; + LinkTable *linktbl = LinkTable_alloc(url); + linktbl->links[0]->sonic_depth = depth; - /* - * start downloading the base URL - */ - DataStruct xml = Link_to_DataStruct(linktbl->links[0]); - if (xml.size == 0) { - LinkTable_free(linktbl); - return NULL; - } + /* + * start downloading the base URL + */ + DataStruct xml = Link_to_DataStruct(linktbl->links[0]); + if (xml.size == 0) { + LinkTable_free(linktbl); + return NULL; + } - XML_Parser parser = XML_ParserCreate(NULL); - XML_SetUserData(parser, linktbl); + XML_Parser parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, linktbl); - XML_SetStartElementHandler(parser, handler); + XML_SetStartElementHandler(parser, handler); - if (XML_Parse(parser, xml.data, xml.size, 1) == XML_STATUS_ERROR) { - lprintf(error, - "Parse error at line %lu: %s\n", - XML_GetCurrentLineNumber(parser), - XML_ErrorString(XML_GetErrorCode(parser))); - } + if (XML_Parse(parser, xml.data, xml.size, 1) == XML_STATUS_ERROR) { + lprintf(error, + "Parse error at line %lu: %s\n", + XML_GetCurrentLineNumber(parser), + XML_ErrorString(XML_GetErrorCode(parser))); + } - XML_ParserFree(parser); + XML_ParserFree(parser); - FREE(xml.data); + FREE(xml.data); - LinkTable_print(linktbl); + LinkTable_print(linktbl); - return linktbl; + return linktbl; } LinkTable *sonic_LinkTable_new_index(const char *id) { - char *url; - if (strcmp(id, "0")) { - url = sonic_getMusicDirectory_link(id); - } else { - url = sonic_gen_url_first_part("getIndexes"); - } - LinkTable *linktbl = sonic_url_to_LinkTable(url, XML_parser_general, 0); - FREE(url); - return linktbl; + char *url; + if (strcmp(id, "0")) { + url = sonic_getMusicDirectory_link(id); + } else { + url = sonic_gen_url_first_part("getIndexes"); + } + LinkTable *linktbl = + sonic_url_to_LinkTable(url, XML_parser_general, 0); + FREE(url); + return linktbl; } static void XMLCALL XML_parser_id3_root(void *data, const char *elem, const char **attr) { - if (!strcmp(elem, "error")) { - lprintf(error, "\n"); - for (int i = 0; attr[i]; i += 2) { - lprintf(error, "%s: %s\n", attr[i], attr[i + 1]); - } + if (!strcmp(elem, "error")) { + lprintf(error, "\n"); + for (int i = 0; attr[i]; i += 2) { + lprintf(error, "%s: %s\n", attr[i], attr[i + 1]); } + } - LinkTable *root_linktbl = (LinkTable *) data; - LinkTable *this_linktbl = NULL; + LinkTable *root_linktbl = (LinkTable *) data; + LinkTable *this_linktbl = NULL; + /* + * Set the current linktbl, if we have more than head link. + */ + if (root_linktbl->num > 1) { + this_linktbl = + root_linktbl->links[root_linktbl->num - 1]->next_table; + } + + int id_set = 0; + int linkname_set = 0; + Link *link; + if (!strcmp(elem, "index")) { /* - * Set the current linktbl, if we have more than head link. + * Add a subdirectory */ - if (root_linktbl->num > 1) { - this_linktbl = - root_linktbl->links[root_linktbl->num - 1]->next_table; - } - - int id_set = 0; - int linkname_set = 0; - Link *link; - if (!strcmp(elem, "index")) { + link = CALLOC(1, sizeof(Link)); + link->type = LINK_DIR; + for (int i = 0; attr[i]; i += 2) { + if (!strcmp("name", attr[i])) { + strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); + linkname_set = 1; /* - * Add a subdirectory + * Allocate a new LinkTable */ - link = CALLOC(1, sizeof(Link)); - link->type = LINK_DIR; - for (int i = 0; attr[i]; i += 2) { - if (!strcmp("name", attr[i])) { - strncpy(link->linkname, attr[i + 1], - MAX_FILENAME_LEN); - linkname_set = 1; - /* - * Allocate a new LinkTable - */ - link->next_table = LinkTable_alloc("/"); - } - } - /* - * Make sure we don't add an empty directory - */ - if (linkname_set) { - LinkTable_add(root_linktbl, link); - } else { - FREE(link); - } - return; - } else if (!strcmp(elem, "artist")) { - link = CALLOC(1, sizeof(Link)); - link->type = LINK_DIR; - /* - * The new table should be a level 3 album table - */ - link->sonic_depth = 3; - for (int i = 0; attr[i]; i += 2) { - if (!strcmp("name", attr[i])) { - strncpy(link->linkname, attr[i + 1], - MAX_FILENAME_LEN); - linkname_set = 1; - continue; - } - - if (!strcmp("id", attr[i])) { - link->sonic_id = - CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); - strncpy(link->sonic_id, attr[i + 1], - MAX_FILENAME_LEN); - id_set = 1; - continue; - } - } - - /* - * Clean up if linkname is not set - */ - if (!linkname_set || !id_set) { - FREE(link); - return; - } - - LinkTable_add(this_linktbl, link); + link->next_table = LinkTable_alloc("/"); + } } /* - * If we reach here, then this element does not contain directory structural - * information + * Make sure we don't add an empty directory */ + if (linkname_set) { + LinkTable_add(root_linktbl, link); + } else { + FREE(link); + } + return; + } else if (!strcmp(elem, "artist")) { + link = CALLOC(1, sizeof(Link)); + link->type = LINK_DIR; + /* + * The new table should be a level 3 album table + */ + link->sonic_depth = 3; + for (int i = 0; attr[i]; i += 2) { + if (!strcmp("name", attr[i])) { + strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); + linkname_set = 1; + continue; + } + + if (!strcmp("id", attr[i])) { + link->sonic_id = + CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); + strncpy(link->sonic_id, attr[i + 1], MAX_FILENAME_LEN); + id_set = 1; + continue; + } + } + + /* + * Clean up if linkname is not set + */ + if (!linkname_set || !id_set) { + FREE(link); + return; + } + + LinkTable_add(this_linktbl, link); + } + /* + * If we reach here, then this element does not contain directory structural + * information + */ } LinkTable *sonic_LinkTable_new_id3(int depth, const char *id) { - char *url; - LinkTable *linktbl = ROOT_LINK_TBL; - switch (depth) { - /* - * Root table - */ - case 0: - url = sonic_gen_url_first_part("getArtists"); - linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0); - FREE(url); - break; - /* - * Album table - get all the albums of an artist - */ - case 3: - url = sonic_getArtist_link(id); - linktbl = - sonic_url_to_LinkTable(url, XML_parser_general, depth); - FREE(url); - break; - /* - * Song table - get all the songs of an album - */ - case 4: - url = sonic_getAlbum_link(id); - linktbl = - sonic_url_to_LinkTable(url, XML_parser_general, depth); - FREE(url); - break; - default: - /* - * We shouldn't reach here. - */ - lprintf(fatal, "case %d.\n", depth); - break; - } - return linktbl; + char *url; + LinkTable *linktbl = ROOT_LINK_TBL; + switch (depth) { + /* + * Root table + */ + case 0: + url = sonic_gen_url_first_part("getArtists"); + linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0); + FREE(url); + break; + /* + * Album table - get all the albums of an artist + */ + case 3: + url = sonic_getArtist_link(id); + linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth); + FREE(url); + break; + /* + * Song table - get all the songs of an album + */ + case 4: + url = sonic_getAlbum_link(id); + linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth); + FREE(url); + break; + default: + /* + * We shouldn't reach here. + */ + lprintf(fatal, "case %d.\n", depth); + break; + } + return linktbl; } diff --git a/src/util.c b/src/util.c index 23773d6..7181ffc 100644 --- a/src/util.c +++ b/src/util.c @@ -29,126 +29,126 @@ char *path_append(const char *path, const char *filename) { - int needs_separator = 0; - if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/') - && (filename[0] != '/')) { - needs_separator = 1; - } + int needs_separator = 0; + if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/') + && (filename[0] != '/')) { + needs_separator = 1; + } - char *str; - size_t ul = strnlen(path, MAX_PATH_LEN); - size_t sl = strnlen(filename, MAX_FILENAME_LEN); - str = CALLOC(ul + sl + needs_separator + 1, sizeof(char)); - strncpy(str, path, ul); - if (needs_separator) { - str[ul] = '/'; - } - strncat(str, filename, sl); - return str; + char *str; + size_t ul = strnlen(path, MAX_PATH_LEN); + size_t sl = strnlen(filename, MAX_FILENAME_LEN); + str = CALLOC(ul + sl + needs_separator + 1, sizeof(char)); + strncpy(str, path, ul); + if (needs_separator) { + str[ul] = '/'; + } + strncat(str, filename, sl); + return str; } int64_t round_div(int64_t a, int64_t b) { - return (a + (b / 2)) / b; + return (a + (b / 2)) / b; } void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t * x) { - int i; - i = pthread_mutex_unlock(x); - if (i) { - lprintf(fatal, - "thread %x: pthread_mutex_unlock() failed, %d, %s\n", - pthread_self(), i, strerror(i)); - } + int i; + i = pthread_mutex_unlock(x); + if (i) { + lprintf(fatal, + "thread %x: pthread_mutex_unlock() failed, %d, %s\n", + pthread_self(), i, strerror(i)); + } } void PTHREAD_MUTEX_LOCK(pthread_mutex_t * x) { - int i; - i = pthread_mutex_lock(x); - if (i) { - lprintf(fatal, - "thread %x: pthread_mutex_lock() failed, %d, %s\n", - pthread_self(), i, strerror(i)); - } + int i; + i = pthread_mutex_lock(x); + if (i) { + lprintf(fatal, + "thread %x: pthread_mutex_lock() failed, %d, %s\n", + pthread_self(), i, strerror(i)); + } } void exit_failure(void) { - int nptrs; - void *buffer[BT_BUF_SIZE]; + int nptrs; + void *buffer[BT_BUF_SIZE]; - nptrs = backtrace(buffer, BT_BUF_SIZE); - fprintf(stderr, "\nOops! HTTPDirFS crashed! :(\n"); - fprintf(stderr, "backtrace() returned the following %d addresses:\n", - nptrs); - backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO); + nptrs = backtrace(buffer, BT_BUF_SIZE); + fprintf(stderr, "\nOops! HTTPDirFS crashed! :(\n"); + fprintf(stderr, "backtrace() returned the following %d addresses:\n", + nptrs); + backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO); - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); } void erase_string(FILE * file, size_t max_len, char *s) { - size_t l = strnlen(s, max_len); - for (size_t k = 0; k < l; k++) { - fprintf(file, "\b"); - } - for (size_t k = 0; k < l; k++) { - fprintf(file, " "); - } - for (size_t k = 0; k < l; k++) { - fprintf(file, "\b"); - } + size_t l = strnlen(s, max_len); + for (size_t k = 0; k < l; k++) { + fprintf(file, "\b"); + } + for (size_t k = 0; k < l; k++) { + fprintf(file, " "); + } + for (size_t k = 0; k < l; k++) { + fprintf(file, "\b"); + } } char *generate_salt(void) { - char *out; - out = CALLOC(SALT_LEN + 1, sizeof(char)); - uuid_t uu; - uuid_generate(uu); - uuid_unparse(uu, out); - return out; + char *out; + out = CALLOC(SALT_LEN + 1, sizeof(char)); + uuid_t uu; + uuid_generate(uu); + uuid_unparse(uu, out); + return out; } char *generate_md5sum(const char *str) { - MD5_CTX c; - unsigned char md5[MD5_DIGEST_LENGTH]; - size_t len = strnlen(str, MAX_PATH_LEN); - char *out = CALLOC(MD5_HASH_LEN + 1, sizeof(char)); + MD5_CTX c; + unsigned char md5[MD5_DIGEST_LENGTH]; + size_t len = strnlen(str, MAX_PATH_LEN); + char *out = CALLOC(MD5_HASH_LEN + 1, sizeof(char)); - MD5_Init(&c); - MD5_Update(&c, str, len); - MD5_Final(md5, &c); + MD5_Init(&c); + MD5_Update(&c, str, len); + MD5_Final(md5, &c); - for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { - sprintf(out + 2 * i, "%02x", md5[i]); - } - return out; + for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { + sprintf(out + 2 * i, "%02x", md5[i]); + } + return out; } void *CALLOC(size_t nmemb, size_t size) { - void *ptr = calloc(nmemb, size); - if (!ptr) { - lprintf(fatal, "calloc() failed, %s!\n", strerror(errno)); - } - return ptr; + void *ptr = calloc(nmemb, size); + if (!ptr) { + lprintf(fatal, "calloc() failed, %s!\n", strerror(errno)); + } + return ptr; } void FREE(void *ptr) { - free(ptr); - ptr = NULL; + free(ptr); + ptr = NULL; } char *str_to_hex(char *s) { - char *hex = CALLOC(strlen(s) * 2 + 1, sizeof(char)); - for (char *c = s, *h = hex; *c; c++, h += 2) { - sprintf(h, "%x", *c); - } - return hex; + char *hex = CALLOC(strlen(s) * 2 + 1, sizeof(char)); + for (char *c = s, *h = hex; *c; c++, h += 2) { + sprintf(h, "%x", *c); + } + return hex; }