1 #include "first.h" 2 3 #include "stat_cache.h" 4 #include "log.h" 5 #include "fdevent.h" 6 #include "etag.h" 7 #include "algo_splaytree.h" 8 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 12 #include <stdlib.h> 13 #include <string.h> 14 #include <errno.h> 15 #include <unistd.h> 16 #include <fcntl.h> 17 18 #if defined(HAVE_SYS_XATTR_H) 19 # include <sys/xattr.h> 20 #elif defined(HAVE_ATTR_ATTRIBUTES_H) 21 # include <attr/attributes.h> 22 #endif 23 24 #ifdef HAVE_SYS_EXTATTR_H 25 # include <sys/extattr.h> 26 #endif 27 28 #ifndef HAVE_LSTAT 29 #define lstat stat 30 #ifndef S_ISLNK 31 #define S_ISLNK(mode) (0) 32 #endif 33 #endif 34 35 /* 36 * stat-cache 37 * 38 * - a splay-tree is used as we can use the caching effect of it 39 */ 40 41 enum { 42 STAT_CACHE_ENGINE_SIMPLE, /*(default)*/ 43 STAT_CACHE_ENGINE_NONE, 44 STAT_CACHE_ENGINE_FAM 45 }; 46 47 struct stat_cache_fam; /* declaration */ 48 49 typedef struct stat_cache { 50 int stat_cache_engine; 51 splay_tree *files; /* nodes of tree are (stat_cache_entry *) */ 52 struct stat_cache_fam *scf; 53 } stat_cache; 54 55 static stat_cache sc; 56 57 58 static void * stat_cache_sptree_find(splay_tree ** const sptree, 59 const char * const name, 60 uint32_t len) 61 { 62 const int ndx = splaytree_djbhash(name, len); 63 *sptree = splaytree_splay(*sptree, ndx); 64 return (*sptree && (*sptree)->key == ndx) ? (*sptree)->data : NULL; 65 } 66 67 68 #ifdef HAVE_FAM_H 69 70 /* monitor changes in directories using FAM 71 * 72 * This implementation employing FAM monitors directories as they are used, 73 * and maintains a reference count for cache use within stat_cache.c. 74 * A periodic job runs in lighttpd every 32 seconds, expiring entries unused 75 * in last 64 seconds out of the cache and cancelling FAM monitoring. Items 76 * within the cache are checked against the filesystem upon use if last stat() 77 * was greater than or equal to 16 seconds ago. 78 * 79 * This implementation does not monitor every directory in a tree, and therefore 80 * the cache may get out-of-sync with the filesystem. Delays in receiving and 81 * processing events from FAM might also lead to stale cache entries. 82 * 83 * For many websites, a large number of files are seldom, if ever, modified, 84 * and a common practice with images is to create a new file with a new name 85 * when a new version is needed, in order for client browsers and CDNs to better 86 * cache the content. Given this, most use will see little difference in 87 * performance between server.stat-cache-engine = "fam" and "simple" (default). 88 * The default server.stat-cache-engine = "simple" calls stat() on a target once 89 * per second, and reuses that information until the next second. For use where 90 * changes must be immediately visible, server.stat-cache-engine = "disable" 91 * should be used. 92 * 93 * When considering use of server.stat-cache-engine = "fam", there are a few 94 * additional limitations for this cache implementation using FAM. 95 * - symlinks to files located outside of the current directory do not result 96 * in changes to that file being monitored (unless that file is in a directory 97 * which is monitored as a result of a different request). symlinks can be 98 * chained and can be circular. This implementation *does not* readlink() or 99 * realpath() to resolve the chains to find and monitor the ultimate target 100 * directory. While symlinks to files located outside the current directory 101 * are not monitored, symlinks to directories *are* monitored, though chains 102 * of symlinks to directories do not result in monitoring of the directories 103 * containing intermediate symlinks to the target directory. 104 * - directory rename of a directory which is not currently being monitored will 105 * result in stale information in the cache if there is a subdirectory that is 106 * being monitored. 107 * Even though lighttpd will not receive FAM events in the above cases, lighttpd 108 * does re-validate the information in the cache upon use if the cache entry has 109 * not been checked in 16 seconds, so that is the upper limit for use of stale 110 * data. 111 * 112 * Use of server.stat-cache-engine = "fam" is discouraged for extremely volatile 113 * directories such as temporary directories (e.g. /tmp and maybe /var/tmp) due 114 * to the overhead of processing the additional noise generated from changes. 115 * Related, server.stat-cache-engine = "fam" is not recommended on trees of 116 * untrusted files where a malicious user could generate an excess of change 117 * events. 118 * 119 * Internal note: lighttpd walks the caches to prune trees in stat_cache when an 120 * event is received for a directory (or symlink to a directory) which has been 121 * deleted or renamed. The splaytree data structure is suboptimal for frequent 122 * changes of large directories trees where there have been a large number of 123 * different files recently accessed and part of the stat_cache. 124 */ 125 126 #include <fam.h> 127 128 #ifdef HAVE_FAMNOEXISTS 129 #ifndef LIGHTTPD_STATIC 130 #include <dlfcn.h> 131 #endif 132 #endif 133 134 typedef struct fam_dir_entry { 135 buffer *name; 136 int refcnt; 137 FAMRequest req; 138 time_t stat_ts; 139 dev_t st_dev; 140 ino_t st_ino; 141 struct fam_dir_entry *fam_parent; 142 } fam_dir_entry; 143 144 typedef struct stat_cache_fam { 145 splay_tree *dirs; /* the nodes of the tree are fam_dir_entry */ 146 FAMConnection fam; 147 log_error_st *errh; 148 fdevents *ev; 149 fdnode *fdn; 150 int fd; 151 } stat_cache_fam; 152 153 static fam_dir_entry * fam_dir_entry_init(const char *name, size_t len) 154 { 155 fam_dir_entry * const fam_dir = calloc(1, sizeof(*fam_dir)); 156 force_assert(NULL != fam_dir); 157 158 fam_dir->name = buffer_init(); 159 buffer_copy_string_len(fam_dir->name, name, len); 160 fam_dir->refcnt = 0; 161 162 return fam_dir; 163 } 164 165 static void fam_dir_entry_free(fam_dir_entry *fam_dir) 166 { 167 if (!fam_dir) return; 168 /*(fam_dir->parent might be invalid pointer here; ignore)*/ 169 buffer_free(fam_dir->name); 170 free(fam_dir); 171 } 172 173 static void fam_dir_invalidate_node(fam_dir_entry *fam_dir) 174 { 175 fam_dir->stat_ts = 0; 176 if (fam_dir->fam_parent) { 177 --fam_dir->fam_parent->refcnt; 178 fam_dir->fam_parent = NULL; 179 } 180 } 181 182 /* 183 * walk though splay_tree and collect contents of dir tree. 184 * remove tagged entries in a second loop 185 */ 186 187 static void fam_dir_tag_refcnt(splay_tree *t, int *keys, int *ndx) 188 { 189 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 190 if (t->left) fam_dir_tag_refcnt(t->left, keys, ndx); 191 if (t->right) fam_dir_tag_refcnt(t->right, keys, ndx); 192 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 193 194 fam_dir_entry * const fam_dir = t->data; 195 if (0 == fam_dir->refcnt) { 196 fam_dir_invalidate_node(fam_dir); 197 keys[(*ndx)++] = t->key; 198 } 199 } 200 201 __attribute_noinline__ 202 static void fam_dir_periodic_cleanup() { 203 stat_cache_fam * const scf = sc.scf; 204 int max_ndx, i; 205 int keys[8192]; /* 32k size on stack */ 206 do { 207 if (!scf->dirs) break; 208 max_ndx = 0; 209 fam_dir_tag_refcnt(scf->dirs, keys, &max_ndx); 210 for (i = 0; i < max_ndx; ++i) { 211 const int ndx = keys[i]; 212 splay_tree *node = scf->dirs = splaytree_splay(scf->dirs, ndx); 213 if (node && node->key == ndx) { 214 fam_dir_entry *fam_dir = node->data; 215 scf->dirs = splaytree_delete(scf->dirs, ndx); 216 FAMCancelMonitor(&scf->fam, &fam_dir->req); 217 fam_dir_entry_free(fam_dir); 218 } 219 } 220 } while (max_ndx == sizeof(keys)/sizeof(int)); 221 } 222 223 static void fam_dir_invalidate_tree(splay_tree *t, const char *name, size_t len) 224 { 225 #ifdef __clang_analyzer__ 226 force_assert(name); 227 #endif 228 /*force_assert(t);*/ 229 if (t->left) fam_dir_invalidate_tree(t->left, name, len); 230 if (t->right) fam_dir_invalidate_tree(t->right, name, len); 231 232 fam_dir_entry * const fam_dir = t->data; 233 #ifdef __clang_analyzer__ 234 force_assert(fam_dir); 235 #endif 236 buffer *b = fam_dir->name; 237 size_t blen = buffer_string_length(b); 238 if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len)) 239 fam_dir_invalidate_node(fam_dir); 240 } 241 242 /* declarations */ 243 static void stat_cache_delete_tree(const char *name, uint32_t len); 244 static void stat_cache_invalidate_dir_tree(const char *name, size_t len); 245 246 static void stat_cache_handle_fdevent_in(stat_cache_fam *scf) 247 { 248 for (int i = 0, ndx; i || (i = FAMPending(&scf->fam)) > 0; --i) { 249 FAMEvent fe; 250 if (FAMNextEvent(&scf->fam, &fe) < 0) break; 251 252 /* ignore events which may have been pending for 253 * paths recently cancelled via FAMCancelMonitor() */ 254 ndx = (int)(intptr_t)fe.userdata; 255 scf->dirs = splaytree_splay(scf->dirs, ndx); 256 if (!scf->dirs || scf->dirs->key != ndx) { 257 continue; 258 } 259 fam_dir_entry *fam_dir = scf->dirs->data; 260 if (FAMREQUEST_GETREQNUM(&fam_dir->req) 261 != FAMREQUEST_GETREQNUM(&fe.fr)) { 262 continue; 263 } 264 265 if (fe.filename[0] != '/') { 266 buffer * const n = fam_dir->name; 267 fam_dir_entry *fam_link; 268 size_t len; 269 switch(fe.code) { 270 case FAMCreated: 271 /* file created in monitored dir modifies dir and 272 * we should get a separate FAMChanged event for dir. 273 * Therefore, ignore file FAMCreated event here. 274 * Also, if FAMNoExists() is used, might get spurious 275 * FAMCreated events as changes are made e.g. in monitored 276 * sub-sub-sub dirs and the library discovers new (already 277 * existing) dir entries */ 278 continue; 279 case FAMChanged: 280 /* file changed in monitored dir does not modify dir */ 281 case FAMDeleted: 282 case FAMMoved: 283 /* file deleted or moved in monitored dir modifies dir, 284 * but FAM provides separate notification for that */ 285 286 /* temporarily append filename to dir in fam_dir->name to 287 * construct path, then delete stat_cache entry (if any)*/ 288 len = buffer_string_length(n); 289 buffer_append_string_len(n, CONST_STR_LEN("/")); 290 buffer_append_string_len(n,fe.filename,strlen(fe.filename)); 291 /* (alternatively, could chose to stat() and update)*/ 292 stat_cache_invalidate_entry(CONST_BUF_LEN(n)); 293 294 fam_link = /*(check if might be symlink to monitored dir)*/ 295 stat_cache_sptree_find(&scf->dirs, CONST_BUF_LEN(n)); 296 if (fam_link && !buffer_is_equal(fam_link->name, n)) 297 fam_link = NULL; 298 299 buffer_string_set_length(n, len); 300 301 if (fam_link) { 302 /* replaced symlink changes containing dir */ 303 stat_cache_invalidate_entry(CONST_BUF_LEN(n)); 304 /* handle symlink to dir as deleted dir below */ 305 fe.code = FAMDeleted; 306 fam_dir = fam_link; 307 break; 308 } 309 continue; 310 default: 311 continue; 312 } 313 } 314 315 switch(fe.code) { 316 case FAMChanged: 317 stat_cache_invalidate_entry(CONST_BUF_LEN(fam_dir->name)); 318 break; 319 case FAMDeleted: 320 case FAMMoved: 321 stat_cache_delete_tree(CONST_BUF_LEN(fam_dir->name)); 322 fam_dir_invalidate_node(fam_dir); 323 if (scf->dirs) 324 fam_dir_invalidate_tree(scf->dirs,CONST_BUF_LEN(fam_dir->name)); 325 fam_dir_periodic_cleanup(); 326 break; 327 default: 328 break; 329 } 330 } 331 } 332 333 static handler_t stat_cache_handle_fdevent(void *ctx, int revent) 334 { 335 stat_cache_fam * const scf = ctx; /* sc.scf */ 336 337 if (revent & FDEVENT_IN) { 338 stat_cache_handle_fdevent_in(scf); 339 } 340 341 if (revent & (FDEVENT_HUP|FDEVENT_RDHUP)) { 342 /* fam closed the connection */ 343 log_error(scf->errh, __FILE__, __LINE__, 344 "FAM connection closed; disabling stat_cache."); 345 /* (although effectively STAT_CACHE_ENGINE_NONE, 346 * do not change here so that periodic jobs clean up memory)*/ 347 /*sc.stat_cache_engine = STAT_CACHE_ENGINE_NONE; */ 348 fdevent_fdnode_event_del(scf->ev, scf->fdn); 349 fdevent_unregister(scf->ev, scf->fd); 350 scf->fdn = NULL; 351 352 FAMClose(&scf->fam); 353 scf->fd = -1; 354 } 355 356 return HANDLER_GO_ON; 357 } 358 359 static stat_cache_fam * stat_cache_init_fam(fdevents *ev, log_error_st *errh) { 360 stat_cache_fam *scf = calloc(1, sizeof(*scf)); 361 force_assert(scf); 362 scf->fd = -1; 363 scf->ev = ev; 364 scf->errh = errh; 365 366 /* setup FAM */ 367 if (0 != FAMOpen2(&scf->fam, "lighttpd")) { 368 log_error(errh, __FILE__, __LINE__, 369 "could not open a fam connection, dying."); 370 return NULL; 371 } 372 #ifdef HAVE_FAMNOEXISTS 373 #ifdef LIGHTTPD_STATIC 374 FAMNoExists(&scf->fam); 375 #else 376 int (*FAMNoExists_fn)(FAMConnection *); 377 FAMNoExists_fn = 378 (int (*)(FAMConnection *))(intptr_t)dlsym(RTLD_DEFAULT,"FAMNoExists"); 379 if (FAMNoExists_fn) FAMNoExists_fn(&scf->fam); 380 #endif 381 #endif 382 383 scf->fd = FAMCONNECTION_GETFD(&scf->fam); 384 fdevent_setfd_cloexec(scf->fd); 385 scf->fdn = fdevent_register(scf->ev, scf->fd, stat_cache_handle_fdevent, scf); 386 fdevent_fdnode_event_set(scf->ev, scf->fdn, FDEVENT_IN | FDEVENT_RDHUP); 387 388 return scf; 389 } 390 391 static void stat_cache_free_fam(stat_cache_fam *scf) { 392 if (NULL == scf) return; 393 394 while (scf->dirs) { 395 /*(skip entry invalidation and FAMCancelMonitor())*/ 396 splay_tree *node = scf->dirs; 397 fam_dir_entry_free((fam_dir_entry *)node->data); 398 scf->dirs = splaytree_delete(scf->dirs, node->key); 399 } 400 401 if (-1 != scf->fd) { 402 /*scf->fdn already cleaned up in fdevent_free()*/ 403 FAMClose(&scf->fam); 404 /*scf->fd = -1;*/ 405 } 406 407 free(scf); 408 } 409 410 static fam_dir_entry * fam_dir_monitor(stat_cache_fam *scf, char *fn, uint32_t dirlen, struct stat *st) 411 { 412 if (NULL == scf->fdn) return NULL; /* FAM connection closed; do nothing */ 413 const int fn_is_dir = S_ISDIR(st->st_mode); 414 /*force_assert(0 != dirlen);*/ 415 /*force_assert(fn[0] == '/');*/ 416 /* consistency: ensure fn does not end in '/' unless root "/" 417 * FAM events will not end in '/', so easier to match this way */ 418 if (fn[dirlen-1] == '/') --dirlen; 419 if (0 == dirlen) dirlen = 1; /* root dir ("/") */ 420 /* Note: paths are expected to be normalized before calling stat_cache, 421 * e.g. without repeated '/' */ 422 if (!fn_is_dir) { 423 while (fn[--dirlen] != '/') ; 424 if (0 == dirlen) dirlen = 1; /*(should not happen for file)*/ 425 } 426 int dir_ndx = splaytree_djbhash(fn, dirlen); 427 fam_dir_entry *fam_dir = NULL; 428 429 scf->dirs = splaytree_splay(scf->dirs, dir_ndx); 430 if (NULL != scf->dirs && scf->dirs->key == dir_ndx) { 431 fam_dir = scf->dirs->data; 432 if (!buffer_is_equal_string(fam_dir->name, fn, dirlen)) { 433 /* hash collision; preserve existing 434 * do not monitor new to avoid cache thrashing */ 435 return NULL; 436 } 437 /* directory already registered */ 438 } 439 440 const time_t cur_ts = log_epoch_secs; 441 struct stat lst; 442 int ck_dir = fn_is_dir; 443 if (!fn_is_dir && (NULL==fam_dir || cur_ts - fam_dir->stat_ts >= 16)) { 444 ck_dir = 1; 445 /*(temporarily modify fn)*/ 446 fn[dirlen] = '\0'; 447 if (0 != lstat(fn, &lst)) { 448 fn[dirlen] = '/'; 449 return NULL; 450 } 451 if (!S_ISLNK(lst.st_mode)) { 452 st = &lst; 453 } 454 else if (0 != stat(fn, st)) { /*st passed in now is stat() of dir*/ 455 fn[dirlen] = '/'; 456 return NULL; 457 } 458 fn[dirlen] = '/'; 459 } 460 461 int ck_lnk = (NULL == fam_dir); 462 if (ck_dir && NULL != fam_dir) { 463 /* check stat() matches device and inode, just in case an external event 464 * not being monitored occurs (e.g. rename of unmonitored parent dir)*/ 465 if (st->st_dev != fam_dir->st_dev || st->st_ino != fam_dir->st_ino) { 466 ck_lnk = 1; 467 /*(modifies scf->dirs but no need to re-splay for dir_ndx since 468 * fam_dir is not NULL and so splaytree_insert not called below)*/ 469 if (scf->dirs) fam_dir_invalidate_tree(scf->dirs, fn, dirlen); 470 if (!fn_is_dir) /*(if dir, caller is updating stat_cache_entry)*/ 471 stat_cache_update_entry(fn, dirlen, st, NULL); 472 /*(must not delete tree since caller is holding a valid node)*/ 473 stat_cache_invalidate_dir_tree(fn, dirlen); 474 if (0 != FAMCancelMonitor(&scf->fam, &fam_dir->req) 475 || 0 != FAMMonitorDirectory(&scf->fam, fam_dir->name->ptr, 476 &fam_dir->req, 477 (void *)(intptr_t)dir_ndx)) { 478 fam_dir->stat_ts = 0; /* invalidate */ 479 return NULL; 480 } 481 fam_dir->st_dev = st->st_dev; 482 fam_dir->st_ino = st->st_ino; 483 } 484 fam_dir->stat_ts = cur_ts; 485 } 486 487 if (NULL == fam_dir) { 488 fam_dir = fam_dir_entry_init(fn, dirlen); 489 490 if (0 != FAMMonitorDirectory(&scf->fam,fam_dir->name->ptr,&fam_dir->req, 491 (void *)(intptr_t)dir_ndx)) { 492 log_error(scf->errh, __FILE__, __LINE__, 493 "monitoring dir failed: %s file: %s %s", 494 fam_dir->name->ptr, fn, FamErrlist[FAMErrno]); 495 fam_dir_entry_free(fam_dir); 496 return NULL; 497 } 498 499 scf->dirs = splaytree_insert(scf->dirs, dir_ndx, fam_dir); 500 fam_dir->stat_ts= cur_ts; 501 fam_dir->st_dev = st->st_dev; 502 fam_dir->st_ino = st->st_ino; 503 } 504 505 if (ck_lnk) { 506 if (fn_is_dir) { 507 /*(temporarily modify fn)*/ 508 char e = fn[dirlen]; 509 fn[dirlen] = '\0'; 510 if (0 != lstat(fn, &lst)) { 511 fn[dirlen] = e; 512 return NULL; 513 } 514 fn[dirlen] = e; 515 } 516 if (fam_dir->fam_parent) { 517 --fam_dir->fam_parent->refcnt; 518 fam_dir->fam_parent = NULL; 519 } 520 if (S_ISLNK(lst.st_mode)) { 521 fam_dir->fam_parent = fam_dir_monitor(scf, fn, dirlen, &lst); 522 } 523 } 524 525 ++fam_dir->refcnt; 526 return fam_dir; 527 } 528 529 #endif 530 531 532 static stat_cache_entry * stat_cache_entry_init(void) { 533 stat_cache_entry *sce = calloc(1, sizeof(*sce)); 534 force_assert(NULL != sce); 535 sce->fd = -1; 536 sce->refcnt = 1; 537 return sce; 538 } 539 540 static void stat_cache_entry_free(void *data) { 541 stat_cache_entry *sce = data; 542 if (!sce) return; 543 544 if (--sce->refcnt) return; 545 546 #ifdef HAVE_FAM_H 547 /*(decrement refcnt only; 548 * defer cancelling FAM monitor on dir even if refcnt reaches zero)*/ 549 if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt; 550 #endif 551 552 free(sce->name.ptr); 553 free(sce->etag.ptr); 554 if (sce->content_type.size) free(sce->content_type.ptr); 555 if (sce->fd >= 0) close(sce->fd); 556 557 free(sce); 558 } 559 560 void stat_cache_entry_refchg(void *data, int mod) { 561 /*(expect mod == -1 or mod == 1)*/ 562 stat_cache_entry * const sce = data; 563 if (mod < 0 && 1 == sce->refcnt) 564 stat_cache_entry_free(data); 565 else 566 sce->refcnt += mod; 567 } 568 569 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 570 571 static const char *attrname = "Content-Type"; 572 static char attrval[128]; 573 static buffer attrb = { attrval, 0, 0 }; 574 575 static int stat_cache_attr_get(const char *name) { 576 #if defined(HAVE_XATTR) 577 #if defined(HAVE_SYS_XATTR_H) 578 ssize_t attrlen; 579 if (0 < (attrlen = getxattr(name, attrname, 580 attrval, sizeof(attrval)-1))) 581 #else 582 int attrlen = sizeof(attrval)-1; 583 if (0 == attr_get(name, attrname, attrval, &attrlen, 0)) 584 #endif 585 #elif defined(HAVE_EXTATTR) 586 ssize_t attrlen; 587 if (0 < (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, attrname, 588 attrval, sizeof(attrval)-1))) 589 #endif 590 { 591 attrval[attrlen] = '\0'; 592 attrb.used = (uint32_t)(attrlen + 1); 593 return 1; 594 } 595 return 0; 596 } 597 598 #endif 599 600 int stat_cache_init(fdevents *ev, log_error_st *errh) { 601 #ifdef HAVE_FAM_H 602 if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 603 sc.scf = stat_cache_init_fam(ev, errh); 604 if (NULL == sc.scf) return 0; 605 } 606 #else 607 UNUSED(ev); 608 UNUSED(errh); 609 #endif 610 611 return 1; 612 } 613 614 void stat_cache_free(void) { 615 splay_tree *sptree = sc.files; 616 while (sptree) { 617 stat_cache_entry_free(sptree->data); 618 sptree = splaytree_delete(sptree, sptree->key); 619 } 620 sc.files = NULL; 621 622 #ifdef HAVE_FAM_H 623 stat_cache_free_fam(sc.scf); 624 sc.scf = NULL; 625 #endif 626 627 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 628 attrname = "Content-Type"; 629 #endif 630 631 sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; /*(default)*/ 632 } 633 634 void stat_cache_xattrname (const char *name) { 635 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 636 attrname = name; 637 #else 638 UNUSED(name); 639 #endif 640 } 641 642 int stat_cache_choose_engine (const buffer *stat_cache_string, log_error_st *errh) { 643 if (buffer_string_is_empty(stat_cache_string)) 644 sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; 645 else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("simple"))) 646 sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; 647 #ifdef HAVE_FAM_H 648 else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("fam"))) 649 sc.stat_cache_engine = STAT_CACHE_ENGINE_FAM; 650 #endif 651 else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("disable"))) 652 sc.stat_cache_engine = STAT_CACHE_ENGINE_NONE; 653 else { 654 log_error(errh, __FILE__, __LINE__, 655 "server.stat-cache-engine can be one of \"disable\", \"simple\"," 656 #ifdef HAVE_FAM_H 657 " \"fam\"," 658 #endif 659 " but not: %s", stat_cache_string->ptr); 660 return -1; 661 } 662 return 0; 663 } 664 665 const buffer * stat_cache_mimetype_by_ext(const array * const mimetypes, const char * const name, const uint32_t nlen) 666 { 667 const char * const end = name + nlen; /*(end of string)*/ 668 const uint32_t used = mimetypes->used; 669 if (used < 16) { 670 for (uint32_t i = 0; i < used; ++i) { 671 /* suffix match */ 672 const data_string *ds = (data_string *)mimetypes->data[i]; 673 const size_t klen = buffer_string_length(&ds->key); 674 if (klen <= nlen && buffer_eq_icase_ssn(end-klen, ds->key.ptr, klen)) 675 return &ds->value; 676 } 677 } 678 else { 679 const char *s; 680 const data_string *ds; 681 if (nlen) { 682 for (s = end-1; s != name && *s != '/'; --s) ; /*(like memrchr())*/ 683 if (*s == '/') ++s; 684 } 685 else { 686 s = name; 687 } 688 /* search for basename, then longest .ext2.ext1, then .ext1, then "" */ 689 ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s); 690 if (NULL != ds) return &ds->value; 691 while (++s < end) { 692 while (*s != '.' && ++s != end) ; 693 if (s == end) break; 694 /* search ".ext" then "ext" */ 695 ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s); 696 if (NULL != ds) return &ds->value; 697 /* repeat search without leading '.' to handle situation where 698 * admin configured mimetype.assign keys without leading '.' */ 699 if (++s < end) { 700 if (*s == '.') { --s; continue; } 701 ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s); 702 if (NULL != ds) return &ds->value; 703 } 704 } 705 /* search for ""; catchall */ 706 ds = (const data_string *)array_get_element_klen(mimetypes, CONST_STR_LEN("")); 707 if (NULL != ds) return &ds->value; 708 } 709 710 return NULL; 711 } 712 713 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 714 715 const buffer * stat_cache_mimetype_by_xattr(const char * const name) 716 { 717 return stat_cache_attr_get(name) ? &attrb : NULL; 718 } 719 720 const buffer * stat_cache_content_type_get_by_xattr(stat_cache_entry *sce, const array *mimetypes, int use_xattr) 721 { 722 /*(invalid caching if user config has multiple, different 723 * r->conf.mimetypes for same extension (not expected))*/ 724 if (!buffer_string_is_empty(&sce->content_type)) return &sce->content_type; 725 726 if (!S_ISREG(sce->st.st_mode)) return NULL; 727 728 /* cache mimetype */ 729 const buffer *mtype = 730 (use_xattr) ? stat_cache_mimetype_by_xattr(sce->name.ptr) : NULL; 731 if (NULL == mtype) 732 mtype = stat_cache_mimetype_by_ext(mimetypes,CONST_BUF_LEN(&sce->name)); 733 if (NULL != mtype) { 734 if (sce->content_type.size) { 735 buffer_copy_buffer(&sce->content_type, mtype); 736 } 737 else if (mtype == &attrb) { 738 sce->content_type.ptr = NULL; 739 buffer_copy_buffer(&sce->content_type, mtype); 740 } 741 else { 742 /*(copy pointers from mimetypes array; avoid allocation)*/ 743 sce->content_type.ptr = mtype->ptr; 744 sce->content_type.used = mtype->used; 745 /*(leave sce->content_type.size = 0 to flag not-allocated)*/ 746 } 747 } 748 else 749 buffer_clear(&sce->content_type); 750 751 return &sce->content_type; 752 } 753 754 #else 755 756 const buffer * stat_cache_content_type_get_by_ext(stat_cache_entry *sce, const array *mimetypes) 757 { 758 /*(invalid caching if user config has multiple, different 759 * r->conf.mimetypes for same extension (not expected))*/ 760 if (!buffer_string_is_empty(&sce->content_type)) return &sce->content_type; 761 762 if (!S_ISREG(sce->st.st_mode)) return NULL; 763 764 /* cache mimetype */ 765 const buffer * const mtype = 766 stat_cache_mimetype_by_ext(mimetypes, CONST_BUF_LEN(&sce->name)); 767 if (NULL != mtype) { 768 /*(copy pointers from mimetypes array; avoid allocation)*/ 769 sce->content_type.ptr = mtype->ptr; 770 sce->content_type.used = mtype->used; 771 /*(leave sce->content_type.size = 0 to flag not-allocated)*/ 772 } 773 else 774 buffer_clear(&sce->content_type); 775 776 return &sce->content_type; 777 } 778 779 #endif 780 781 const buffer * stat_cache_etag_get(stat_cache_entry *sce, int flags) { 782 /*(invalid caching if user cfg has multiple, different r->conf.etag_flags 783 * for same path (not expected, since etag flags should be by filesystem))*/ 784 if (!buffer_string_is_empty(&sce->etag)) return &sce->etag; 785 786 if (S_ISREG(sce->st.st_mode) || S_ISDIR(sce->st.st_mode)) { 787 if (0 == flags) return NULL; 788 etag_create(&sce->etag, &sce->st, flags); 789 return &sce->etag; 790 } 791 792 return NULL; 793 } 794 795 __attribute_pure__ 796 static int stat_cache_stat_eq(const struct stat * const sta, const struct stat * const stb) { 797 return 798 #ifdef st_mtime /* use high-precision timestamp if available */ 799 #if defined(__APPLE__) && defined(__MACH__) 800 sta->st_mtimespec.tv_nsec == stb->st_mtimespec.tv_nsec 801 #else 802 sta->st_mtim.tv_nsec == stb->st_mtim.tv_nsec 803 #endif 804 #endif 805 && sta->st_mtime == stb->st_mtime 806 && sta->st_size == stb->st_size 807 && sta->st_ino == stb->st_ino 808 && sta->st_dev == stb->st_dev; 809 } 810 811 void stat_cache_update_entry(const char *name, uint32_t len, 812 struct stat *st, buffer *etagb) 813 { 814 if (sc.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return; 815 force_assert(0 != len); 816 if (name[len-1] == '/') { if (0 == --len) len = 1; } 817 splay_tree **sptree = &sc.files; 818 stat_cache_entry *sce = 819 stat_cache_sptree_find(sptree, name, len); 820 if (sce && buffer_is_equal_string(&sce->name, name, len)) { 821 if (!stat_cache_stat_eq(&sce->st, st)) { 822 /* etagb might be NULL to clear etag (invalidate) */ 823 buffer_copy_string_len(&sce->etag, CONST_BUF_LEN(etagb)); 824 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 825 buffer_clear(&sce->content_type); 826 #endif 827 if (sce->fd >= 0) { 828 if (1 == sce->refcnt) { 829 close(sce->fd); 830 sce->fd = -1; 831 } 832 else { 833 --sce->refcnt; /* stat_cache_entry_free(sce); */ 834 (*sptree)->data = sce = stat_cache_entry_init(); 835 buffer_copy_string_len(&sce->name, name, len); 836 } 837 } 838 sce->st = *st; 839 } 840 sce->stat_ts = log_epoch_secs; 841 } 842 } 843 844 void stat_cache_delete_entry(const char *name, uint32_t len) 845 { 846 if (sc.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return; 847 force_assert(0 != len); 848 if (name[len-1] == '/') { if (0 == --len) len = 1; } 849 splay_tree **sptree = &sc.files; 850 stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len); 851 if (sce && buffer_is_equal_string(&sce->name, name, len)) { 852 stat_cache_entry_free(sce); 853 *sptree = splaytree_delete(*sptree, (*sptree)->key); 854 } 855 } 856 857 void stat_cache_invalidate_entry(const char *name, uint32_t len) 858 { 859 splay_tree **sptree = &sc.files; 860 stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len); 861 if (sce && buffer_is_equal_string(&sce->name, name, len)) { 862 sce->stat_ts = 0; 863 #ifdef HAVE_FAM_H 864 if (sce->fam_dir != NULL) { 865 --((fam_dir_entry *)sce->fam_dir)->refcnt; 866 sce->fam_dir = NULL; 867 } 868 #endif 869 } 870 } 871 872 #ifdef HAVE_FAM_H 873 874 static void stat_cache_invalidate_dir_tree_walk(splay_tree *t, 875 const char *name, size_t len) 876 { 877 if (t->left) stat_cache_invalidate_dir_tree_walk(t->left, name, len); 878 if (t->right) stat_cache_invalidate_dir_tree_walk(t->right, name, len); 879 880 buffer *b = &((stat_cache_entry *)t->data)->name; 881 size_t blen = buffer_string_length(b); 882 if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len)) { 883 stat_cache_entry *sce = t->data; 884 sce->stat_ts = 0; 885 if (sce->fam_dir != NULL) { 886 --((fam_dir_entry *)sce->fam_dir)->refcnt; 887 sce->fam_dir = NULL; 888 } 889 } 890 } 891 892 static void stat_cache_invalidate_dir_tree(const char *name, size_t len) 893 { 894 splay_tree * const sptree = sc.files; 895 if (sptree) stat_cache_invalidate_dir_tree_walk(sptree, name, len); 896 } 897 898 #endif 899 900 /* 901 * walk though splay_tree and collect contents of dir tree. 902 * remove tagged entries in a second loop 903 */ 904 905 static void stat_cache_tag_dir_tree(splay_tree *t, const char *name, size_t len, 906 int *keys, int *ndx) 907 { 908 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 909 if (t->left) stat_cache_tag_dir_tree(t->left, name, len, keys, ndx); 910 if (t->right) stat_cache_tag_dir_tree(t->right, name, len, keys, ndx); 911 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 912 913 buffer *b = &((stat_cache_entry *)t->data)->name; 914 size_t blen = buffer_string_length(b); 915 if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len)) 916 keys[(*ndx)++] = t->key; 917 } 918 919 __attribute_noinline__ 920 static void stat_cache_prune_dir_tree(const char *name, size_t len) 921 { 922 splay_tree *sptree = sc.files; 923 int max_ndx, i; 924 int keys[8192]; /* 32k size on stack */ 925 do { 926 if (!sptree) break; 927 max_ndx = 0; 928 stat_cache_tag_dir_tree(sptree, name, len, keys, &max_ndx); 929 for (i = 0; i < max_ndx; ++i) { 930 const int ndx = keys[i]; 931 splay_tree *node = sptree = splaytree_splay(sptree, ndx); 932 if (node && node->key == ndx) { 933 stat_cache_entry_free(node->data); 934 sptree = splaytree_delete(sptree, ndx); 935 } 936 } 937 } while (max_ndx == sizeof(keys)/sizeof(int)); 938 sc.files = sptree; 939 } 940 941 static void stat_cache_delete_tree(const char *name, uint32_t len) 942 { 943 stat_cache_delete_entry(name, len); 944 stat_cache_prune_dir_tree(name, len); 945 } 946 947 void stat_cache_delete_dir(const char *name, uint32_t len) 948 { 949 force_assert(0 != len); 950 if (name[len-1] == '/') { if (0 == --len) len = 1; } 951 stat_cache_delete_tree(name, len); 952 #ifdef HAVE_FAM_H 953 if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 954 splay_tree **sptree = &sc.scf->dirs; 955 fam_dir_entry *fam_dir = stat_cache_sptree_find(sptree, name, len); 956 if (fam_dir && buffer_is_equal_string(fam_dir->name, name, len)) 957 fam_dir_invalidate_node(fam_dir); 958 if (*sptree) fam_dir_invalidate_tree(*sptree, name, len); 959 fam_dir_periodic_cleanup(); 960 } 961 #endif 962 } 963 964 /*** 965 * 966 * 967 * 968 * returns: 969 * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) 970 * - HANDLER_ERROR on stat() failed -> see errno for problem 971 */ 972 973 stat_cache_entry * stat_cache_get_entry(const buffer *name) { 974 stat_cache_entry *sce = NULL; 975 struct stat st; 976 int file_ndx; 977 978 /* consistency: ensure lookup name does not end in '/' unless root "/" 979 * (but use full path given with stat(), even with trailing '/') */ 980 int final_slash = 0; 981 size_t len = buffer_string_length(name); 982 force_assert(0 != len); 983 if (name->ptr[len-1] == '/') { final_slash = 1; if (0 == --len) len = 1; } 984 /* Note: paths are expected to be normalized before calling stat_cache, 985 * e.g. without repeated '/' */ 986 987 if (name->ptr[0] != '/') { 988 errno = EINVAL; 989 return NULL; 990 } 991 992 /* 993 * check if the directory for this file has changed 994 */ 995 996 const time_t cur_ts = log_epoch_secs; 997 998 file_ndx = splaytree_djbhash(name->ptr, len); 999 splay_tree *sptree = sc.files = splaytree_splay(sc.files, file_ndx); 1000 1001 if (sptree && (sptree->key == file_ndx)) { 1002 /* we have seen this file already and 1003 * don't stat() it again in the same second */ 1004 1005 sce = sptree->data; 1006 1007 /* check if the name is the same, we might have a collision */ 1008 1009 if (buffer_is_equal_string(&sce->name, name->ptr, len)) { 1010 if (sc.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { 1011 if (sce->stat_ts == cur_ts) { 1012 if (final_slash && !S_ISDIR(sce->st.st_mode)) { 1013 errno = ENOTDIR; 1014 return NULL; 1015 } 1016 return sce; 1017 } 1018 } 1019 #ifdef HAVE_FAM_H 1020 else if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM 1021 && sce->fam_dir) { /* entry is in monitored dir */ 1022 /* re-stat() periodically, even if monitoring for changes 1023 * (due to limitations in stat_cache.c use of FAM) 1024 * (gaps due to not continually monitoring an entire tree) */ 1025 if (cur_ts - sce->stat_ts < 16) { 1026 if (final_slash && !S_ISDIR(sce->st.st_mode)) { 1027 errno = ENOTDIR; 1028 return NULL; 1029 } 1030 return sce; 1031 } 1032 } 1033 #endif 1034 } else { 1035 /* collision, forget about the entry */ 1036 sce = NULL; 1037 } 1038 } 1039 1040 if (-1 == stat(name->ptr, &st)) { 1041 return NULL; 1042 } 1043 1044 if (S_ISREG(st.st_mode)) { 1045 /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ 1046 if (name->ptr[buffer_string_length(name) - 1] == '/') { 1047 errno = ENOTDIR; 1048 return NULL; 1049 } 1050 } 1051 1052 if (NULL == sce) { 1053 1054 sce = stat_cache_entry_init(); 1055 buffer_copy_string_len(&sce->name, name->ptr, len); 1056 1057 /* already splayed file_ndx */ 1058 if (NULL != sptree && sptree->key == file_ndx) { 1059 /* hash collision: replace old entry */ 1060 stat_cache_entry_free(sptree->data); 1061 sptree->data = sce; 1062 } else { 1063 sptree = sc.files = splaytree_insert(sptree, file_ndx, sce); 1064 } 1065 1066 } else { 1067 1068 buffer_clear(&sce->etag); 1069 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 1070 buffer_clear(&sce->content_type); 1071 #endif 1072 1073 } 1074 1075 if (sce->fd >= 0) { 1076 /* close fd if file changed */ 1077 if (!stat_cache_stat_eq(&sce->st, &st)) { 1078 if (1 == sce->refcnt) { 1079 close(sce->fd); 1080 sce->fd = -1; 1081 } 1082 else { 1083 --sce->refcnt; /* stat_cache_entry_free(sce); */ 1084 sptree->data = sce = stat_cache_entry_init(); 1085 buffer_copy_string_len(&sce->name, name->ptr, len); 1086 } 1087 } 1088 } 1089 1090 sce->st = st; /*(copy prior to calling fam_dir_monitor())*/ 1091 1092 #ifdef HAVE_FAM_H 1093 if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { 1094 if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt; 1095 sce->fam_dir = 1096 fam_dir_monitor(sc.scf, CONST_BUF_LEN(name), &st); 1097 #if 0 /*(performed below)*/ 1098 if (NULL != sce->fam_dir) { 1099 /*(may have been invalidated by dir change)*/ 1100 sce->stat_ts = cur_ts; 1101 } 1102 #endif 1103 } 1104 #endif 1105 1106 sce->stat_ts = cur_ts; 1107 return sce; 1108 } 1109 1110 stat_cache_entry * stat_cache_get_entry_open(const buffer * const name, const int symlinks) { 1111 stat_cache_entry * const sce = stat_cache_get_entry(name); 1112 if (NULL == sce) return NULL; 1113 if (sce->fd >= 0) return sce; 1114 if (sce->st.st_size > 0) 1115 sce->fd = stat_cache_open_rdonly_fstat(name, &sce->st, symlinks); 1116 return sce; /* (note: sce->fd might still be -1 if open() failed) */ 1117 } 1118 1119 const stat_cache_st * stat_cache_path_stat (const buffer * const name) { 1120 const stat_cache_entry * const sce = stat_cache_get_entry(name); 1121 return sce ? &sce->st : NULL; 1122 } 1123 1124 int stat_cache_path_isdir(const buffer *name) { 1125 const stat_cache_entry * const sce = stat_cache_get_entry(name); 1126 return (sce && (S_ISDIR(sce->st.st_mode) ? 1 : (errno = ENOTDIR, 0))); 1127 } 1128 1129 int stat_cache_path_contains_symlink(const buffer *name, log_error_st *errh) { 1130 /* caller should check for symlinks only if we should block symlinks. */ 1131 1132 /* catch the obvious symlinks 1133 * 1134 * this is not a secure check as we still have a race-condition between 1135 * the stat() and the open. We can only solve this by 1136 * 1. open() the file 1137 * 2. fstat() the fd 1138 * 1139 * and keeping the file open for the rest of the time. But this can 1140 * only be done at network level. 1141 * */ 1142 1143 #ifdef HAVE_LSTAT 1144 /* we assume "/" can not be symlink, 1145 * so skip the symlink stuff if path is "/" */ 1146 size_t len = buffer_string_length(name); 1147 force_assert(0 != len); 1148 force_assert(name->ptr[0] == '/'); 1149 if (1 == len) return 0; 1150 #ifndef PATH_MAX 1151 #define PATH_MAX 4096 1152 #endif 1153 if (len >= PATH_MAX) return -1; 1154 1155 char buf[PATH_MAX]; 1156 memcpy(buf, name->ptr, len); 1157 char *s_cur = buf+len; 1158 do { 1159 *s_cur = '\0'; 1160 struct stat st; 1161 if (0 == lstat(buf, &st)) { 1162 if (S_ISLNK(st.st_mode)) return 1; 1163 } 1164 else { 1165 log_perror(errh, __FILE__, __LINE__, "lstat failed for: %s", buf); 1166 return -1; 1167 } 1168 } while ((s_cur = strrchr(buf, '/')) > buf); /*(&buf[0]==buf; NULL < buf)*/ 1169 #endif 1170 1171 return 0; 1172 } 1173 1174 int stat_cache_open_rdonly_fstat (const buffer *name, struct stat *st, int symlinks) { 1175 /*(Note: O_NOFOLLOW affects only the final path segment, the target file, 1176 * not any intermediate symlinks along the path)*/ 1177 const int fd = fdevent_open_cloexec(name->ptr, symlinks, O_RDONLY, 0); 1178 if (fd >= 0) { 1179 if (0 == fstat(fd, st)) { 1180 return fd; 1181 } else { 1182 const int errnum = errno; 1183 close(fd); 1184 errno = errnum; 1185 } 1186 } 1187 return -1; 1188 } 1189 1190 /** 1191 * remove stat() from cache which haven't been stat()ed for 1192 * more than 2 seconds 1193 * 1194 * 1195 * walk though the stat-cache, collect the ids which are too old 1196 * and remove them in a second loop 1197 */ 1198 1199 static void stat_cache_tag_old_entries(splay_tree * const t, int * const keys, int * const ndx, const time_t max_age, const time_t cur_ts) { 1200 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 1201 if (t->left) 1202 stat_cache_tag_old_entries(t->left, keys, ndx, max_age, cur_ts); 1203 if (t->right) 1204 stat_cache_tag_old_entries(t->right, keys, ndx, max_age, cur_ts); 1205 if (*ndx == 8192) return; /*(must match num array entries in keys[])*/ 1206 1207 const stat_cache_entry * const sce = t->data; 1208 if (cur_ts - sce->stat_ts > max_age) 1209 keys[(*ndx)++] = t->key; 1210 } 1211 1212 static void stat_cache_periodic_cleanup(const time_t max_age, const time_t cur_ts) { 1213 splay_tree *sptree = sc.files; 1214 int max_ndx, i; 1215 int keys[8192]; /* 32k size on stack */ 1216 do { 1217 if (!sptree) break; 1218 max_ndx = 0; 1219 stat_cache_tag_old_entries(sptree, keys, &max_ndx, max_age, cur_ts); 1220 for (i = 0; i < max_ndx; ++i) { 1221 int ndx = keys[i]; 1222 sptree = splaytree_splay(sptree, ndx); 1223 if (sptree && sptree->key == ndx) { 1224 stat_cache_entry_free(sptree->data); 1225 sptree = splaytree_delete(sptree, ndx); 1226 } 1227 } 1228 } while (max_ndx == sizeof(keys)/sizeof(int)); 1229 sc.files = sptree; 1230 } 1231 1232 void stat_cache_trigger_cleanup(void) { 1233 time_t max_age = 2; 1234 1235 #ifdef HAVE_FAM_H 1236 if (STAT_CACHE_ENGINE_FAM == sc.stat_cache_engine) { 1237 if (log_epoch_secs & 0x1F) return; 1238 /* once every 32 seconds (0x1F == 31) */ 1239 max_age = 32; 1240 fam_dir_periodic_cleanup(); 1241 /* By doing this before stat_cache_periodic_cleanup(), 1242 * entries used within the next max_age secs will remain 1243 * monitored, instead of effectively flushing and 1244 * rebuilding the FAM monitoring every max_age seconds */ 1245 } 1246 #endif 1247 1248 stat_cache_periodic_cleanup(max_age, log_epoch_secs); 1249 } 1250