master uint/parados / server / scan.c
  1#include <stdbool.h>
  2#include <stdint.h>
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <string.h>
  6
  7#include <dirent.h>
  8#include <sys/stat.h>
  9#include <unistd.h>
 10
 11#include "config.h"
 12#include "scan.h"
 13#include "log.h"
 14#include "util.h"
 15
 16static uint64_t encode_fnv1a64(const char* s);
 17static int library_push(struct library* l, const char* rel);
 18static int scan_dir(struct library* l, const char* root, const char* rel);
 19
 20/**
 21 * @brief Encode string using FNV1a64
 22 *
 23 * @param s Input string
 24 *
 25 * @return 64bit hash value
 26 */
 27static uint64_t encode_fnv1a64(const char* s)
 28{
 29	/* standard fnv1a64 encoding */
 30	uint64_t h = 1469598103934665603ULL;
 31
 32	for (; *s; s++) {
 33		h ^= (unsigned char)(*s);
 34		h *= 1099511628211ULL;
 35	}
 36
 37	return h;
 38}
 39
 40/**
 41 * @brief Append item path to library
 42 *
 43 * @param l Output library
 44 * @param rel Relative item path
 45 *
 46 * @return 0=Success, -1=Failure
 47 */
 48static int library_push(struct library* l, const char* rel)
 49{
 50	if (l->len == l->cap) {
 51		size_t ncap = l->cap ? (l->cap * 2) : 64;
 52		struct item* ni = realloc(l->items, ncap * sizeof(*ni));
 53		if (!ni) {
 54			LOG(verbose_log, "SCAN", "realloc FAILED     OOM");
 55			return -1;
 56		}
 57		l->items = ni;
 58		l->cap = ncap;
 59	}
 60
 61	char* p = strdup(rel);
 62	if (!p)
 63		return -1;
 64
 65	l->items[l->len].id = encode_fnv1a64(rel);
 66	l->items[l->len].path = p;
 67	l->len++;
 68
 69	return 0;
 70}
 71
 72/**
 73 * @brief Recursively scan one directory subtree
 74 *
 75 * @param l Output library
 76 * @param root Media root directory
 77 * @param rel Relative path within root
 78 *
 79 * @return 0=Success, -1=Failure
 80 */
 81static int scan_dir(struct library* l, const char* root, const char* rel)
 82{
 83	char full[4096];
 84	DIR* d;
 85	struct dirent* e;
 86
 87	if (rel[0] == '\0') {
 88		if (snprintf(full, sizeof(full), "%s", root) >= (int)sizeof(full))
 89			return -1;
 90	}
 91	else {
 92		if (join_path(full, sizeof(full), root, rel) < 0)
 93			return -1;
 94	}
 95
 96	d = opendir(full);
 97	if (!d) {
 98		LOG(verbose_log, "SCAN", "opendir FAILED     %s", full);
 99		return -1;
100	}
101
102	while ((e = readdir(d)) != NULL) {
103		if (strcmp(e->d_name, ".") == 0)
104			continue;
105		if (strcmp(e->d_name, "..") == 0)
106			continue;
107
108		char rel2[4096];
109		char full2[4096];
110
111		if (rel[0] == '\0') {
112			if (snprintf(rel2, sizeof(rel2), "%s", e->d_name) >= (int)sizeof(rel2))
113				goto fail;
114		}
115		else {
116			if (join_path(rel2, sizeof(rel2), rel, e->d_name) < 0)
117				goto fail;
118		}
119
120		if (join_path(full2, sizeof(full2), root, rel2) < 0)
121			goto fail;
122
123		struct stat st;
124		if (lstat(full2, &st) < 0)
125			continue;
126
127		if (S_ISDIR(st.st_mode)) {
128			if (scan_dir(l, root, rel2) < 0)
129				goto fail;
130			continue;
131		}
132
133		if (S_ISREG(st.st_mode)) {
134			if (library_push(l, rel2) < 0)
135				goto fail;
136			continue;
137		}
138	}
139
140	closedir(d);
141	return 0;
142
143fail:
144	closedir(d);
145	return -1;
146}
147
148int scan_library(struct library* l, const char* root)
149{
150	memset(l, 0, sizeof(*l));
151	return scan_dir(l, root, "");
152}
153
154void scan_library_free(struct library* l)
155{
156	if (!l)
157		return;
158
159	for (size_t i = 0; i < l->len; i++)
160		free(l->items[i].path);
161
162	free(l->items);
163
164	l->items = NULL;
165	l->len = 0;
166	l->cap = 0;
167}
168
169int scan_library_rescan(struct library* l, const char* root)
170{
171	struct library new;
172	struct library old;
173
174	if (!l || !root)
175		return -1;
176
177	memset(&new, 0, sizeof(new));
178
179	if (scan_library(&new, root) < 0) {
180		scan_library_free(&new);
181		return -1;
182	}
183
184	/* swap */
185	old = *l;
186	*l = new;
187
188	/* free old */
189	scan_library_free(&old);
190
191	return 0;
192}
193