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