1 /* $Id: timehash.c 7412 2005-10-09 03:44:35Z eagle $
3 ** Timehash based storage method.
8 #include "portable/mmap.h"
13 #include <netinet/in.h>
18 #include "inn/innconf.h"
26 char *base; /* Base of the mmaped file */
27 int len; /* Length of the file */
28 DIR *top; /* Open handle on the top level directory */
29 DIR *sec; /* Open handle on the 2nd level directory */
30 DIR *ter; /* Open handle on the third level directory */
31 DIR *artdir; /* Open handle on the article directory */
32 struct dirent *topde; /* dirent entry for the last entry retrieved in top */
33 struct dirent *secde; /* dirent entry for the last entry retrieved in sec */
34 struct dirent *terde; /* dirent entry for the last entry retrieved in ter */
37 typedef enum {FIND_DIR, FIND_ART, FIND_TOPDIR} FINDTYPE;
39 static int SeqNum = 0;
41 static TOKEN MakeToken(time_t now, int seqnum, STORAGECLASS class, TOKEN *oldtoken) {
46 if (oldtoken == (TOKEN *)NULL)
47 memset(&token, '\0', sizeof(token));
49 memcpy(&token, oldtoken, sizeof(token));
50 token.type = TOKEN_TIMEHASH;
53 memcpy(token.token, &i, sizeof(i));
55 memmove(token.token, &token.token[sizeof(i) - 4], 4);
57 memcpy(&token.token[4], &s + (sizeof(s) - 2), 2);
61 static void BreakToken(TOKEN token, int *now, int *seqnum) {
65 memcpy(&i, token.token, sizeof(i));
66 memcpy(&s, &token.token[4], sizeof(s));
68 *seqnum = (int)ntohs(s);
71 static char *MakePath(int now, int seqnum, const STORAGECLASS class) {
75 /* innconf->patharticles + '/time-zz/xx/xx/yyyy-xxxx' */
76 length = strlen(innconf->patharticles) + 32;
77 path = xmalloc(length);
78 snprintf(path, length, "%s/time-%02x/%02x/%02x/%04x-%04x",
79 innconf->patharticles, class,
80 (now >> 16) & 0xff, (now >> 8) & 0xff, seqnum,
81 (now & 0xff) | ((now >> 16 & 0xff00)));
85 static TOKEN *PathToToken(char *path) {
87 unsigned int t1, t2, t3, seqnum, class;
91 n = sscanf(path, "time-%02x/%02x/%02x/%04x-%04x", &class, &t1, &t2, &seqnum, &t3);
94 now = ((t1 << 16) & 0xff0000) | ((t2 << 8) & 0xff00) | ((t3 << 16) & 0xff000000) | (t3 & 0xff);
95 token = MakeToken(now, seqnum, class, (TOKEN *)NULL);
99 bool timehash_init(SMATTRIBUTE *attr) {
101 syslog(L_ERROR, "timehash: attr is NULL");
102 SMseterror(SMERR_INTERNAL, "attr is NULL");
105 attr->selfexpire = false;
106 attr->expensivestat = true;
107 if (STORAGE_TOKEN_LENGTH < 6) {
108 syslog(L_FATAL, "timehash: token length is less than 6 bytes");
109 SMseterror(SMERR_TOKENSHORT, NULL);
115 TOKEN timehash_store(const ARTHANDLE article, const STORAGECLASS class) {
125 if (article.arrived == (time_t)0)
128 now = article.arrived;
130 for (i = 0; i < 0x10000; i++) {
132 SeqNum = (SeqNum + 1) & 0xffff;
133 path = MakePath(now, seq, class);
135 if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, ARTFILE_MODE)) < 0) {
138 p = strrchr(path, '/');
140 if (!MakeDirectory(path, true)) {
141 syslog(L_ERROR, "timehash: could not make directory %s %m", path);
142 token.type = TOKEN_EMPTY;
144 SMseterror(SMERR_UNDEFINED, NULL);
148 if ((fd = open(path, O_CREAT|O_EXCL|O_WRONLY, ARTFILE_MODE)) < 0) {
149 SMseterror(SMERR_UNDEFINED, NULL);
150 syslog(L_ERROR, "timehash: could not open %s %m", path);
151 token.type = TOKEN_EMPTY;
160 SMseterror(SMERR_UNDEFINED, NULL);
161 syslog(L_ERROR, "timehash: all sequence numbers for the time and class are reserved %lu %d", (unsigned long)now, class);
162 token.type = TOKEN_EMPTY;
167 result = xwritev(fd, article.iov, article.iovcnt);
168 if (result != (ssize_t) article.len) {
169 SMseterror(SMERR_UNDEFINED, NULL);
170 syslog(L_ERROR, "timehash error writing %s %m", path);
172 token.type = TOKEN_EMPTY;
179 return MakeToken(now, seq, class, article.token);
182 static ARTHANDLE *OpenArticle(const char *path, RETRTYPE amount) {
184 PRIV_TIMEHASH *private;
189 if (amount == RETR_STAT) {
190 if (access(path, R_OK) < 0) {
191 SMseterror(SMERR_UNDEFINED, NULL);
194 art = xmalloc(sizeof(ARTHANDLE));
195 art->type = TOKEN_TIMEHASH;
202 if ((fd = open(path, O_RDONLY)) < 0) {
203 SMseterror(SMERR_UNDEFINED, NULL);
207 art = xmalloc(sizeof(ARTHANDLE));
208 art->type = TOKEN_TIMEHASH;
210 if (fstat(fd, &sb) < 0) {
211 SMseterror(SMERR_UNDEFINED, NULL);
212 syslog(L_ERROR, "timehash: could not fstat article: %m");
217 private = xmalloc(sizeof(PRIV_TIMEHASH));
218 art->private = (void *)private;
219 private->len = sb.st_size;
220 if (innconf->articlemmap) {
221 if ((private->base = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
222 SMseterror(SMERR_UNDEFINED, NULL);
223 syslog(L_ERROR, "timehash: could not mmap article: %m");
228 if (amount == RETR_ALL)
229 madvise(private->base, sb.st_size, MADV_WILLNEED);
231 madvise(private->base, sb.st_size, MADV_SEQUENTIAL);
233 private->base = xmalloc(private->len);
234 if (read(fd, private->base, private->len) < 0) {
235 SMseterror(SMERR_UNDEFINED, NULL);
236 syslog(L_ERROR, "timehash: could not read article: %m");
248 private->artdir = NULL;
249 private->topde = NULL;
250 private->secde = NULL;
251 private->terde = NULL;
253 if (amount == RETR_ALL) {
254 art->data = private->base;
255 art->len = private->len;
259 if ((p = wire_findbody(private->base, private->len)) == NULL) {
260 SMseterror(SMERR_NOBODY, NULL);
261 if (innconf->articlemmap)
262 munmap(private->base, private->len);
270 if (amount == RETR_HEAD) {
271 art->data = private->base;
272 art->len = p - private->base;
276 if (amount == RETR_BODY) {
278 art->len = private->len - (p - private->base);
281 SMseterror(SMERR_UNDEFINED, "Invalid retrieve request");
282 if (innconf->articlemmap)
283 munmap(private->base, private->len);
291 ARTHANDLE *timehash_retrieve(const TOKEN token, const RETRTYPE amount) {
296 static TOKEN ret_token;
298 if (token.type != TOKEN_TIMEHASH) {
299 SMseterror(SMERR_INTERNAL, NULL);
303 BreakToken(token, &now, &seqnum);
304 path = MakePath(now, seqnum, token.class);
305 if ((art = OpenArticle(path, amount)) != (ARTHANDLE *)NULL) {
308 art->token = &ret_token;
314 void timehash_freearticle(ARTHANDLE *article) {
315 PRIV_TIMEHASH *private;
320 if (article->private) {
321 private = (PRIV_TIMEHASH *)article->private;
322 if (innconf->articlemmap)
323 munmap(private->base, private->len);
327 closedir(private->top);
329 closedir(private->sec);
331 closedir(private->ter);
333 closedir(private->artdir);
339 bool timehash_cancel(TOKEN token) {
345 BreakToken(token, &now, &seqnum);
346 path = MakePath(now, seqnum, token.class);
347 result = unlink(path);
350 SMseterror(SMERR_UNDEFINED, NULL);
356 static struct dirent *FindDir(DIR *dir, FINDTYPE type) {
359 while ((de = readdir(dir)) != NULL) {
360 if (type == FIND_TOPDIR)
361 if ((strlen(de->d_name) == 7) &&
362 (strncmp(de->d_name, "time-", 5) == 0) &&
363 isxdigit((int)de->d_name[5]) &&
364 isxdigit((int)de->d_name[6]))
367 if (type == FIND_DIR)
368 if ((strlen(de->d_name) == 2) && isxdigit((int)de->d_name[0]) && isxdigit((int)de->d_name[1]))
371 if (type == FIND_ART)
372 if ((strlen(de->d_name) == 9) &&
373 isxdigit((int)de->d_name[0]) &&
374 isxdigit((int)de->d_name[1]) &&
375 isxdigit((int)de->d_name[2]) &&
376 isxdigit((int)de->d_name[3]) &&
377 isxdigit((int)de->d_name[5]) &&
378 isxdigit((int)de->d_name[6]) &&
379 isxdigit((int)de->d_name[7]) &&
380 isxdigit((int)de->d_name[8]) &&
381 (de->d_name[4] == '-'))
388 ARTHANDLE *timehash_next(const ARTHANDLE *article, const RETRTYPE amount) {
390 PRIV_TIMEHASH *newpriv;
397 length = strlen(innconf->patharticles) + 32;
398 path = xmalloc(length);
399 if (article == NULL) {
408 priv = *(PRIV_TIMEHASH *)article->private;
409 free(article->private);
410 free((void *)article);
411 if (priv.base != NULL) {
412 if (innconf->articlemmap)
413 munmap(priv.base, priv.len);
419 while (!priv.artdir || ((de = FindDir(priv.artdir, FIND_ART)) == NULL)) {
421 closedir(priv.artdir);
424 while (!priv.ter || ((priv.terde = FindDir(priv.ter, FIND_DIR)) == NULL)) {
429 while (!priv.sec || ((priv.secde = FindDir(priv.sec, FIND_DIR)) == NULL)) {
434 if (!priv.top || ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL)) {
442 snprintf(path, length, "%s", innconf->patharticles);
443 if ((priv.top = opendir(path)) == NULL) {
444 SMseterror(SMERR_UNDEFINED, NULL);
448 if ((priv.topde = FindDir(priv.top, FIND_TOPDIR)) == NULL) {
449 SMseterror(SMERR_UNDEFINED, NULL);
455 snprintf(path, length, "%s/%s", innconf->patharticles, priv.topde->d_name);
456 if ((priv.sec = opendir(path)) == NULL)
459 snprintf(path, length, "%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name);
460 if ((priv.ter = opendir(path)) == NULL)
463 snprintf(path, length, "%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name);
464 if ((priv.artdir = opendir(path)) == NULL)
469 snprintf(path, length, "%s/%s/%s/%s/%s", innconf->patharticles, priv.topde->d_name, priv.secde->d_name, priv.terde->d_name, de->d_name);
471 art = OpenArticle(path, amount);
472 if (art == (ARTHANDLE *)NULL) {
473 art = xmalloc(sizeof(ARTHANDLE));
474 art->type = TOKEN_TIMEHASH;
477 art->private = xmalloc(sizeof(PRIV_TIMEHASH));
478 newpriv = (PRIV_TIMEHASH *)art->private;
479 newpriv->base = NULL;
481 newpriv = (PRIV_TIMEHASH *)art->private;
482 newpriv->top = priv.top;
483 newpriv->sec = priv.sec;
484 newpriv->ter = priv.ter;
485 newpriv->artdir = priv.artdir;
486 newpriv->topde = priv.topde;
487 newpriv->secde = priv.secde;
488 newpriv->terde = priv.terde;
489 snprintf(path, length, "%s/%s/%s/%s", priv.topde->d_name, priv.secde->d_name, priv.terde->d_name, de->d_name);
490 art->token = PathToToken(path);
491 BreakToken(*art->token, (int *)&(art->arrived), &seqnum);
496 bool timehash_ctl(PROBETYPE type, TOKEN *token UNUSED, void *value) {
497 struct artngnum *ann;
501 if ((ann = (struct artngnum *)value) == NULL)
503 /* make SMprobe() call timehash_retrieve() */
512 timehash_flushcacheddata(FLUSHTYPE type UNUSED)
518 timehash_printfiles(FILE *file, TOKEN token, char **xref UNUSED,
524 BreakToken(token, &now, &seqnum);
525 path = MakePath(now, seqnum, token.class);
526 fprintf(file, "%s\n", path);
529 void timehash_shutdown(void) {