18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss
322e8b456SStefan Bühler #include "stat_cache.h"
404d76e7aSGlenn Strauss #include "log.h"
5bd32f670SGlenn Strauss #include "fdevent.h"
6b700a8caSGlenn Strauss #include "http_etag.h"
7156e1da2SGlenn Strauss #include "algo_splaytree.h"
85e134da0SJan Kneschke
95e134da0SJan Kneschke #include <sys/types.h>
105e134da0SJan Kneschke #include <sys/stat.h>
115e134da0SJan Kneschke
125e134da0SJan Kneschke #include <stdlib.h>
135e134da0SJan Kneschke #include <string.h>
145e134da0SJan Kneschke #include <errno.h>
155e134da0SJan Kneschke #include <unistd.h>
165e134da0SJan Kneschke #include <fcntl.h>
175e134da0SJan Kneschke
18455dc037SGlenn Strauss #if defined(HAVE_SYS_XATTR_H)
19455dc037SGlenn Strauss # include <sys/xattr.h>
20455dc037SGlenn Strauss #elif defined(HAVE_ATTR_ATTRIBUTES_H)
215e134da0SJan Kneschke # include <attr/attributes.h>
225e134da0SJan Kneschke #endif
235e134da0SJan Kneschke
244d55d4adSMoritz Wilhelmy #ifdef HAVE_SYS_EXTATTR_H
254d55d4adSMoritz Wilhelmy # include <sys/extattr.h>
264d55d4adSMoritz Wilhelmy #endif
274d55d4adSMoritz Wilhelmy
285e134da0SJan Kneschke #ifndef HAVE_LSTAT
295e134da0SJan Kneschke #define lstat stat
308cc189f4SGlenn Strauss #ifndef S_ISLNK
318cc189f4SGlenn Strauss #define S_ISLNK(mode) (0)
328cc189f4SGlenn Strauss #endif
335e134da0SJan Kneschke #endif
345e134da0SJan Kneschke
355e134da0SJan Kneschke /*
365e134da0SJan Kneschke * stat-cache
375e134da0SJan Kneschke *
385e134da0SJan Kneschke * - a splay-tree is used as we can use the caching effect of it
395e134da0SJan Kneschke */
405e134da0SJan Kneschke
419287c87dSGlenn Strauss enum {
425c717302SGlenn Strauss STAT_CACHE_ENGINE_SIMPLE = 0 /*(default)*/
435c717302SGlenn Strauss ,STAT_CACHE_ENGINE_NONE = 1
445c717302SGlenn Strauss ,STAT_CACHE_ENGINE_FAM = 2 /* same as STAT_CACHE_ENGINE_INOTIFY */
455c717302SGlenn Strauss ,STAT_CACHE_ENGINE_INOTIFY = 2 /* same as STAT_CACHE_ENGINE_FAM */
460b00b13aSGlenn Strauss ,STAT_CACHE_ENGINE_KQUEUE = 2 /* same as STAT_CACHE_ENGINE_FAM */
479287c87dSGlenn Strauss };
489287c87dSGlenn Strauss
498cc189f4SGlenn Strauss struct stat_cache_fam; /* declaration */
509287c87dSGlenn Strauss
51e1164797SGlenn Strauss typedef struct stat_cache {
5268d8d4c5SGlenn Strauss int stat_cache_engine;
538cc189f4SGlenn Strauss splay_tree *files; /* nodes of tree are (stat_cache_entry *) */
549287c87dSGlenn Strauss struct stat_cache_fam *scf;
559287c87dSGlenn Strauss } stat_cache;
569287c87dSGlenn Strauss
5768d8d4c5SGlenn Strauss static stat_cache sc;
5868d8d4c5SGlenn Strauss
599287c87dSGlenn Strauss
stat_cache_sptree_find(splay_tree ** const sptree,const char * const name,uint32_t len)60f89f9191SGlenn Strauss static void * stat_cache_sptree_find(splay_tree ** const sptree,
61f89f9191SGlenn Strauss const char * const name,
62d334eaf1SGlenn Strauss uint32_t len)
63f89f9191SGlenn Strauss {
64f85d1f90SGlenn Strauss const int ndx = splaytree_djbhash(name, len);
65f89f9191SGlenn Strauss *sptree = splaytree_splay(*sptree, ndx);
66f89f9191SGlenn Strauss return (*sptree && (*sptree)->key == ndx) ? (*sptree)->data : NULL;
67f89f9191SGlenn Strauss }
68f89f9191SGlenn Strauss
69f89f9191SGlenn Strauss
700b00b13aSGlenn Strauss #if defined(HAVE_SYS_INOTIFY_H) \
710b00b13aSGlenn Strauss || (defined(HAVE_SYS_EVENT_H) && defined(HAVE_KQUEUE))
725c717302SGlenn Strauss #ifndef HAVE_FAM_H
735c717302SGlenn Strauss #define HAVE_FAM_H
745c717302SGlenn Strauss #endif
755c717302SGlenn Strauss #endif
765c717302SGlenn Strauss
779287c87dSGlenn Strauss #ifdef HAVE_FAM_H
789287c87dSGlenn Strauss
798cc189f4SGlenn Strauss /* monitor changes in directories using FAM
808cc189f4SGlenn Strauss *
818cc189f4SGlenn Strauss * This implementation employing FAM monitors directories as they are used,
828cc189f4SGlenn Strauss * and maintains a reference count for cache use within stat_cache.c.
83c752d469SGlenn Strauss * A periodic job runs in lighttpd every 32 seconds, expiring entries unused
848cc189f4SGlenn Strauss * in last 64 seconds out of the cache and cancelling FAM monitoring. Items
858cc189f4SGlenn Strauss * within the cache are checked against the filesystem upon use if last stat()
868cc189f4SGlenn Strauss * was greater than or equal to 16 seconds ago.
878cc189f4SGlenn Strauss *
888cc189f4SGlenn Strauss * This implementation does not monitor every directory in a tree, and therefore
898cc189f4SGlenn Strauss * the cache may get out-of-sync with the filesystem. Delays in receiving and
908cc189f4SGlenn Strauss * processing events from FAM might also lead to stale cache entries.
918cc189f4SGlenn Strauss *
928cc189f4SGlenn Strauss * For many websites, a large number of files are seldom, if ever, modified,
938cc189f4SGlenn Strauss * and a common practice with images is to create a new file with a new name
948cc189f4SGlenn Strauss * when a new version is needed, in order for client browsers and CDNs to better
958cc189f4SGlenn Strauss * cache the content. Given this, most use will see little difference in
968cc189f4SGlenn Strauss * performance between server.stat-cache-engine = "fam" and "simple" (default).
978cc189f4SGlenn Strauss * The default server.stat-cache-engine = "simple" calls stat() on a target once
988cc189f4SGlenn Strauss * per second, and reuses that information until the next second. For use where
998cc189f4SGlenn Strauss * changes must be immediately visible, server.stat-cache-engine = "disable"
1008cc189f4SGlenn Strauss * should be used.
1018cc189f4SGlenn Strauss *
1028cc189f4SGlenn Strauss * When considering use of server.stat-cache-engine = "fam", there are a few
1038cc189f4SGlenn Strauss * additional limitations for this cache implementation using FAM.
1048cc189f4SGlenn Strauss * - symlinks to files located outside of the current directory do not result
1058cc189f4SGlenn Strauss * in changes to that file being monitored (unless that file is in a directory
1068cc189f4SGlenn Strauss * which is monitored as a result of a different request). symlinks can be
1078cc189f4SGlenn Strauss * chained and can be circular. This implementation *does not* readlink() or
1088cc189f4SGlenn Strauss * realpath() to resolve the chains to find and monitor the ultimate target
1098cc189f4SGlenn Strauss * directory. While symlinks to files located outside the current directory
1108cc189f4SGlenn Strauss * are not monitored, symlinks to directories *are* monitored, though chains
1118cc189f4SGlenn Strauss * of symlinks to directories do not result in monitoring of the directories
1128cc189f4SGlenn Strauss * containing intermediate symlinks to the target directory.
1138cc189f4SGlenn Strauss * - directory rename of a directory which is not currently being monitored will
1148cc189f4SGlenn Strauss * result in stale information in the cache if there is a subdirectory that is
1158cc189f4SGlenn Strauss * being monitored.
1168cc189f4SGlenn Strauss * Even though lighttpd will not receive FAM events in the above cases, lighttpd
1178cc189f4SGlenn Strauss * does re-validate the information in the cache upon use if the cache entry has
1188cc189f4SGlenn Strauss * not been checked in 16 seconds, so that is the upper limit for use of stale
1198cc189f4SGlenn Strauss * data.
1208cc189f4SGlenn Strauss *
1218cc189f4SGlenn Strauss * Use of server.stat-cache-engine = "fam" is discouraged for extremely volatile
1228cc189f4SGlenn Strauss * directories such as temporary directories (e.g. /tmp and maybe /var/tmp) due
1238cc189f4SGlenn Strauss * to the overhead of processing the additional noise generated from changes.
1248cc189f4SGlenn Strauss * Related, server.stat-cache-engine = "fam" is not recommended on trees of
1258cc189f4SGlenn Strauss * untrusted files where a malicious user could generate an excess of change
1268cc189f4SGlenn Strauss * events.
1278cc189f4SGlenn Strauss *
1288cc189f4SGlenn Strauss * Internal note: lighttpd walks the caches to prune trees in stat_cache when an
1298cc189f4SGlenn Strauss * event is received for a directory (or symlink to a directory) which has been
1308cc189f4SGlenn Strauss * deleted or renamed. The splaytree data structure is suboptimal for frequent
1318cc189f4SGlenn Strauss * changes of large directories trees where there have been a large number of
1328cc189f4SGlenn Strauss * different files recently accessed and part of the stat_cache.
1338cc189f4SGlenn Strauss */
1348cc189f4SGlenn Strauss
135dce44060SGlenn Strauss #if defined(HAVE_SYS_INOTIFY_H) \
136dce44060SGlenn Strauss && !(defined(HAVE_SYS_EVENT_H) && defined(HAVE_KQUEUE))
1375c717302SGlenn Strauss
1385c717302SGlenn Strauss #include <sys/inotify.h>
13954c07b54SGlenn Strauss #ifndef IN_EXCL_UNLINK /*(not defined in some very old glibc headers)*/
14054c07b54SGlenn Strauss #define IN_EXCL_UNLINK 0x04000000
14154c07b54SGlenn Strauss #endif
1425c717302SGlenn Strauss
1435c717302SGlenn Strauss /*(translate FAM API to inotify; this is specific to stat_cache.c use of FAM)*/
1445c717302SGlenn Strauss #define fam fd /*(translate struct stat_cache_fam scf->fam -> scf->fd)*/
1455c717302SGlenn Strauss typedef int FAMRequest; /*(fr)*/
1465c717302SGlenn Strauss #define FAMClose(fd) \
1475c717302SGlenn Strauss close(*(fd))
1485c717302SGlenn Strauss #define FAMCancelMonitor(fd, wd) \
1495c717302SGlenn Strauss inotify_rm_watch(*(fd), *(wd))
150adf7aea0SGlenn Strauss #define fam_watch_mask ( IN_ATTRIB | IN_CREATE | IN_DELETE | IN_DELETE_SELF \
1515c717302SGlenn Strauss | IN_MODIFY | IN_MOVE_SELF | IN_MOVED_FROM \
152adf7aea0SGlenn Strauss | IN_EXCL_UNLINK | IN_ONLYDIR )
1535c717302SGlenn Strauss /*(note: follows symlinks; not providing IN_DONT_FOLLOW)*/
1545c717302SGlenn Strauss #define FAMMonitorDirectory(fd, fn, wd, userData) \
1555c717302SGlenn Strauss ((*(wd) = inotify_add_watch(*(fd), (fn), (fam_watch_mask))) < 0)
1565c717302SGlenn Strauss typedef enum FAMCodes { /*(copied from fam.h to define arbitrary enum values)*/
1575c717302SGlenn Strauss FAMChanged=1,
1585c717302SGlenn Strauss FAMDeleted=2,
1595c717302SGlenn Strauss FAMCreated=5,
1605c717302SGlenn Strauss FAMMoved=6,
1615c717302SGlenn Strauss } FAMCodes;
1625c717302SGlenn Strauss
1630b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
164dce44060SGlenn Strauss #undef HAVE_SYS_INOTIFY_H
1650b00b13aSGlenn Strauss
1660b00b13aSGlenn Strauss #include <sys/event.h>
1670b00b13aSGlenn Strauss #include <sys/time.h>
1680b00b13aSGlenn Strauss
1690b00b13aSGlenn Strauss /*(translate FAM API to inotify; this is specific to stat_cache.c use of FAM)*/
1700b00b13aSGlenn Strauss #define fam fd /*(translate struct stat_cache_fam scf->fam -> scf->fd)*/
1710b00b13aSGlenn Strauss typedef int FAMRequest; /*(fr)*/
1720b00b13aSGlenn Strauss #define FAMClose(fd) \
1730b00b13aSGlenn Strauss (-1 != (*(fd)) ? close(*(fd)) : 0)
FAMCancelMonitor(const int * const fd,int * const wd)1740b00b13aSGlenn Strauss static int FAMCancelMonitor (const int * const fd, int * const wd)
1750b00b13aSGlenn Strauss {
1760b00b13aSGlenn Strauss if (-1 == *fd) return 0;
1770b00b13aSGlenn Strauss if (-1 == *wd) return 0;
1780b00b13aSGlenn Strauss struct timespec t0 = { 0, 0 };
1790b00b13aSGlenn Strauss struct kevent kev;
1800b00b13aSGlenn Strauss EV_SET(&kev, *wd, EVFILT_VNODE, EV_DELETE, 0, 0, 0);
1810b00b13aSGlenn Strauss int rc = kevent(*fd, &kev, 1, NULL, 0, &t0);
1820b00b13aSGlenn Strauss close(*wd);
1830b00b13aSGlenn Strauss *wd = -1;
1840b00b13aSGlenn Strauss return rc;
1850b00b13aSGlenn Strauss }
FAMMonitorDirectory(int * const fd,char * const fn,int * const wd,void * const userData)1860b00b13aSGlenn Strauss static int FAMMonitorDirectory (int * const fd, char * const fn, int * const wd, void * const userData)
1870b00b13aSGlenn Strauss {
1880b00b13aSGlenn Strauss *wd = fdevent_open_dirname(fn, 1); /*(note: follows symlinks)*/
1890b00b13aSGlenn Strauss if (-1 == *wd) return -1;
1900b00b13aSGlenn Strauss struct timespec t0 = { 0, 0 };
1910b00b13aSGlenn Strauss struct kevent kev;
1920b00b13aSGlenn Strauss unsigned short kev_flags = EV_ADD | EV_ENABLE | EV_CLEAR;
1930b00b13aSGlenn Strauss unsigned int kev_fflags = NOTE_ATTRIB | NOTE_EXTEND | NOTE_LINK | NOTE_WRITE
1940b00b13aSGlenn Strauss | NOTE_DELETE | NOTE_REVOKE | NOTE_RENAME;
1950b00b13aSGlenn Strauss EV_SET(&kev, *wd, EVFILT_VNODE, kev_flags, kev_fflags, 0, userData);
1960b00b13aSGlenn Strauss return kevent(*fd, &kev, 1, NULL, 0, &t0);
1970b00b13aSGlenn Strauss }
1980b00b13aSGlenn Strauss typedef enum FAMCodes { /*(copied from fam.h to define arbitrary enum values)*/
1990b00b13aSGlenn Strauss FAMChanged=1,
2000b00b13aSGlenn Strauss FAMDeleted=2,
2010b00b13aSGlenn Strauss FAMCreated=5,
2020b00b13aSGlenn Strauss FAMMoved=6,
2030b00b13aSGlenn Strauss } FAMCodes;
2040b00b13aSGlenn Strauss
2055c717302SGlenn Strauss #else
2065c717302SGlenn Strauss
2079287c87dSGlenn Strauss #include <fam.h>
2089287c87dSGlenn Strauss
2093e046ccaSGlenn Strauss #ifdef HAVE_FAMNOEXISTS
2103e046ccaSGlenn Strauss #ifndef LIGHTTPD_STATIC
211cd738d4dSGlenn Strauss #ifdef HAVE_DLFCN_H
2123e046ccaSGlenn Strauss #include <dlfcn.h>
2133e046ccaSGlenn Strauss #endif
2143e046ccaSGlenn Strauss #endif
215cd738d4dSGlenn Strauss #endif
2163e046ccaSGlenn Strauss
2175c717302SGlenn Strauss #endif
2185c717302SGlenn Strauss
2198cc189f4SGlenn Strauss typedef struct fam_dir_entry {
220937d83b6SGlenn Strauss buffer name;
2218cc189f4SGlenn Strauss int refcnt;
222cc497033SGlenn Strauss FAMRequest req;
223309c1693SGlenn Strauss unix_time64_t stat_ts;
2248cc189f4SGlenn Strauss dev_t st_dev;
2258cc189f4SGlenn Strauss ino_t st_ino;
2268cc189f4SGlenn Strauss struct fam_dir_entry *fam_parent;
2279287c87dSGlenn Strauss } fam_dir_entry;
2289287c87dSGlenn Strauss
2299287c87dSGlenn Strauss typedef struct stat_cache_fam {
2305c717302SGlenn Strauss splay_tree *dirs; /* indexed by path; node data is fam_dir_entry */
2315c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
2325c717302SGlenn Strauss splay_tree *wds; /* indexed by inotify watch descriptor */
2330b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
2345c717302SGlenn Strauss #else
235e1164797SGlenn Strauss FAMConnection fam;
2365c717302SGlenn Strauss #endif
23766bdd96dSGlenn Strauss log_error_st *errh;
23866bdd96dSGlenn Strauss fdevents *ev;
2399113011dSGlenn Strauss fdnode *fdn;
2409113011dSGlenn Strauss int fd;
2419287c87dSGlenn Strauss } stat_cache_fam;
2429287c87dSGlenn Strauss
243b38817b6SGlenn Strauss __attribute_returns_nonnull__
fam_dir_entry_init(const char * name,size_t len)2448cc189f4SGlenn Strauss static fam_dir_entry * fam_dir_entry_init(const char *name, size_t len)
2458cc189f4SGlenn Strauss {
2465e14db43SGlenn Strauss fam_dir_entry * const fam_dir = ck_calloc(1, sizeof(*fam_dir));
247937d83b6SGlenn Strauss buffer_copy_string_len(&fam_dir->name, name, len);
2488cc189f4SGlenn Strauss fam_dir->refcnt = 0;
2490b00b13aSGlenn Strauss #if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
2500b00b13aSGlenn Strauss fam_dir->req = -1;
2510b00b13aSGlenn Strauss #endif
2529287c87dSGlenn Strauss
2539287c87dSGlenn Strauss return fam_dir;
2549287c87dSGlenn Strauss }
2559287c87dSGlenn Strauss
fam_dir_entry_free(fam_dir_entry * fam_dir)2568cc189f4SGlenn Strauss static void fam_dir_entry_free(fam_dir_entry *fam_dir)
2578cc189f4SGlenn Strauss {
2589287c87dSGlenn Strauss if (!fam_dir) return;
259937d83b6SGlenn Strauss /*(fam_dir->fam_parent might be invalid pointer here; ignore)*/
260937d83b6SGlenn Strauss free(fam_dir->name.ptr);
2610b00b13aSGlenn Strauss #if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
2620b00b13aSGlenn Strauss if (-1 != fam_dir->req)
2630b00b13aSGlenn Strauss close(fam_dir->req);
2640b00b13aSGlenn Strauss #endif
2659287c87dSGlenn Strauss free(fam_dir);
2669287c87dSGlenn Strauss }
2679287c87dSGlenn Strauss
fam_dir_invalidate_node(fam_dir_entry * fam_dir)2688cc189f4SGlenn Strauss static void fam_dir_invalidate_node(fam_dir_entry *fam_dir)
269f89f9191SGlenn Strauss {
2708cc189f4SGlenn Strauss fam_dir->stat_ts = 0;
2718cc189f4SGlenn Strauss if (fam_dir->fam_parent) {
2728cc189f4SGlenn Strauss --fam_dir->fam_parent->refcnt;
2738cc189f4SGlenn Strauss fam_dir->fam_parent = NULL;
274f89f9191SGlenn Strauss }
275f89f9191SGlenn Strauss }
2769287c87dSGlenn Strauss
277f89f9191SGlenn Strauss /*
278f89f9191SGlenn Strauss * walk though splay_tree and collect contents of dir tree.
279f89f9191SGlenn Strauss * remove tagged entries in a second loop
280f89f9191SGlenn Strauss */
281f89f9191SGlenn Strauss
fam_dir_tag_refcnt(splay_tree * t,int * keys,int * ndx)2828cc189f4SGlenn Strauss static void fam_dir_tag_refcnt(splay_tree *t, int *keys, int *ndx)
283f89f9191SGlenn Strauss {
284520bffcdSGlenn Strauss if (*ndx == 512) return; /*(must match num array entries in keys[])*/
2858cc189f4SGlenn Strauss if (t->left) fam_dir_tag_refcnt(t->left, keys, ndx);
2868cc189f4SGlenn Strauss if (t->right) fam_dir_tag_refcnt(t->right, keys, ndx);
287520bffcdSGlenn Strauss if (*ndx == 512) return; /*(must match num array entries in keys[])*/
288f89f9191SGlenn Strauss
2898cc189f4SGlenn Strauss fam_dir_entry * const fam_dir = t->data;
2908cc189f4SGlenn Strauss if (0 == fam_dir->refcnt) {
2918cc189f4SGlenn Strauss fam_dir_invalidate_node(fam_dir);
292f89f9191SGlenn Strauss keys[(*ndx)++] = t->key;
293f89f9191SGlenn Strauss }
2948cc189f4SGlenn Strauss }
295f89f9191SGlenn Strauss
296e11514b0SGlenn Strauss __attribute_noinline__
fam_dir_periodic_cleanup(void)2976516c5a2SGlenn Strauss static void fam_dir_periodic_cleanup(void) {
298e11514b0SGlenn Strauss stat_cache_fam * const scf = sc.scf;
299f89f9191SGlenn Strauss int max_ndx, i;
300520bffcdSGlenn Strauss int keys[512]; /* 2k size on stack */
301520bffcdSGlenn Strauss #if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
302520bffcdSGlenn Strauss struct kevent kevl[512]; /* 32k size on stack to batch kevent EV_DELETE */
303520bffcdSGlenn Strauss #endif
304f89f9191SGlenn Strauss do {
305e11514b0SGlenn Strauss if (!scf->dirs) break;
306f89f9191SGlenn Strauss max_ndx = 0;
3078cc189f4SGlenn Strauss fam_dir_tag_refcnt(scf->dirs, keys, &max_ndx);
308f89f9191SGlenn Strauss for (i = 0; i < max_ndx; ++i) {
309f89f9191SGlenn Strauss const int ndx = keys[i];
310f89f9191SGlenn Strauss splay_tree *node = scf->dirs = splaytree_splay(scf->dirs, ndx);
311f89f9191SGlenn Strauss if (node && node->key == ndx) {
3128cc189f4SGlenn Strauss fam_dir_entry *fam_dir = node->data;
313f89f9191SGlenn Strauss scf->dirs = splaytree_delete(scf->dirs, ndx);
3145c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
3155c717302SGlenn Strauss scf->wds = splaytree_delete(scf->wds, fam_dir->req);
3160b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
3170b00b13aSGlenn Strauss /* batch process kevent removal; defer cancel */
3180b00b13aSGlenn Strauss EV_SET(kevl+i, fam_dir->req, EVFILT_VNODE, EV_DELETE, 0, 0, 0);
3190b00b13aSGlenn Strauss fam_dir->req = -1; /*(make FAMCancelMonitor() a no-op)*/
3205c717302SGlenn Strauss #endif
3218cc189f4SGlenn Strauss FAMCancelMonitor(&scf->fam, &fam_dir->req);
3228cc189f4SGlenn Strauss fam_dir_entry_free(fam_dir);
323f89f9191SGlenn Strauss }
324f89f9191SGlenn Strauss }
3250b00b13aSGlenn Strauss #if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
326520bffcdSGlenn Strauss /* batch process: kevent() to submit EV_DELETE, then close dir fds */
327520bffcdSGlenn Strauss if (0 == max_ndx) break;
3280b00b13aSGlenn Strauss struct timespec t0 = { 0, 0 };
3290b00b13aSGlenn Strauss kevent(scf->fd, kevl, max_ndx, NULL, 0, &t0);
3300b00b13aSGlenn Strauss for (i = 0; i < max_ndx; ++i)
3310b00b13aSGlenn Strauss close((int)kevl[i].ident);
3320b00b13aSGlenn Strauss #endif
333f89f9191SGlenn Strauss } while (max_ndx == sizeof(keys)/sizeof(int));
334f89f9191SGlenn Strauss }
335f89f9191SGlenn Strauss
fam_dir_invalidate_tree(splay_tree * t,const char * name,size_t len)3368cc189f4SGlenn Strauss static void fam_dir_invalidate_tree(splay_tree *t, const char *name, size_t len)
3378cc189f4SGlenn Strauss {
33828f1867cSGlenn Strauss #ifdef __clang_analyzer__
33928f1867cSGlenn Strauss force_assert(name);
34028f1867cSGlenn Strauss #endif
3418cc189f4SGlenn Strauss /*force_assert(t);*/
3428cc189f4SGlenn Strauss if (t->left) fam_dir_invalidate_tree(t->left, name, len);
3438cc189f4SGlenn Strauss if (t->right) fam_dir_invalidate_tree(t->right, name, len);
3448cc189f4SGlenn Strauss
3458cc189f4SGlenn Strauss fam_dir_entry * const fam_dir = t->data;
34628f1867cSGlenn Strauss #ifdef __clang_analyzer__
34728f1867cSGlenn Strauss force_assert(fam_dir);
34828f1867cSGlenn Strauss #endif
349937d83b6SGlenn Strauss const buffer * const b = &fam_dir->name;
350af3df29aSGlenn Strauss size_t blen = buffer_clen(b);
3518cc189f4SGlenn Strauss if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len))
3528cc189f4SGlenn Strauss fam_dir_invalidate_node(fam_dir);
3538cc189f4SGlenn Strauss }
3548cc189f4SGlenn Strauss
35557470365SGlenn Strauss /* declarations */
356d334eaf1SGlenn Strauss static void stat_cache_delete_tree(const char *name, uint32_t len);
35768d8d4c5SGlenn Strauss static void stat_cache_invalidate_dir_tree(const char *name, size_t len);
3585c717302SGlenn Strauss static void stat_cache_handle_fdevent_fn(stat_cache_fam * const scf, fam_dir_entry * const fam_dir, const char * const fn, const uint32_t fnlen, int code);
35957470365SGlenn Strauss
stat_cache_handle_fdevent_in(stat_cache_fam * scf)36068d8d4c5SGlenn Strauss static void stat_cache_handle_fdevent_in(stat_cache_fam *scf)
36160a4b5f1SGlenn Strauss {
3625c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
3635c717302SGlenn Strauss /*(inotify pads in->len to align struct following in->name[])*/
3645c717302SGlenn Strauss char buf[4096]
3655c717302SGlenn Strauss __attribute__ ((__aligned__(__alignof__(struct inotify_event))));
3665c717302SGlenn Strauss int rd;
3675c717302SGlenn Strauss do {
3685c717302SGlenn Strauss rd = (int)read(scf->fd, buf, sizeof(buf));
3695c717302SGlenn Strauss if (rd <= 0) {
3705c717302SGlenn Strauss if (-1 == rd && errno != EINTR && errno != EAGAIN) {
3715c717302SGlenn Strauss log_perror(scf->errh, __FILE__, __LINE__, "inotify error");
3725c717302SGlenn Strauss /* TODO: could flush cache, close scf->fd, and re-open inotify*/
3735c717302SGlenn Strauss }
3745c717302SGlenn Strauss break;
3755c717302SGlenn Strauss }
3765c717302SGlenn Strauss for (int i = 0; i < rd; ) {
3775c717302SGlenn Strauss struct inotify_event * const in =
3785c717302SGlenn Strauss (struct inotify_event *)((uintptr_t)buf + i);
37901d49a28SGlenn Strauss uint32_t len = in->len;
380881d0507SGlenn Strauss if (len > sizeof(buf)) break; /*(should not happen)*/
38101d49a28SGlenn Strauss i += sizeof(struct inotify_event) + len;
38201d49a28SGlenn Strauss if (i > rd) break; /*(should not happen (partial record))*/
3835c717302SGlenn Strauss if (in->mask & IN_CREATE)
3845c717302SGlenn Strauss continue; /*(see comment below for FAMCreated)*/
3855c717302SGlenn Strauss if (in->mask & IN_Q_OVERFLOW) {
3865c717302SGlenn Strauss log_error(scf->errh, __FILE__, __LINE__,
3875c717302SGlenn Strauss "inotify queue overflow");
3885c717302SGlenn Strauss continue;
3895c717302SGlenn Strauss }
3905c717302SGlenn Strauss /* ignore events which may have been pending for
3915c717302SGlenn Strauss * paths recently cancelled via FAMCancelMonitor() */
3925c717302SGlenn Strauss scf->wds = splaytree_splay(scf->wds, in->wd);
3935c717302SGlenn Strauss if (!scf->wds || scf->wds->key != in->wd)
3945c717302SGlenn Strauss continue;
3955c717302SGlenn Strauss fam_dir_entry *fam_dir = scf->wds->data;
3965c717302SGlenn Strauss if (NULL == fam_dir) /*(should not happen)*/
3975c717302SGlenn Strauss continue;
3985c717302SGlenn Strauss if (fam_dir->req != in->wd) /*(should not happen)*/
3995c717302SGlenn Strauss continue;
4005c717302SGlenn Strauss /*(specific to use here in stat_cache.c)*/
4015c717302SGlenn Strauss int code = 0;
4025c717302SGlenn Strauss if (in->mask & (IN_ATTRIB | IN_MODIFY))
4035c717302SGlenn Strauss code = FAMChanged;
4045c717302SGlenn Strauss else if (in->mask & (IN_DELETE | IN_DELETE_SELF | IN_UNMOUNT))
4055c717302SGlenn Strauss code = FAMDeleted;
4065c717302SGlenn Strauss else if (in->mask & (IN_MOVE_SELF | IN_MOVED_FROM))
4075c717302SGlenn Strauss code = FAMMoved;
4085c717302SGlenn Strauss
40901d49a28SGlenn Strauss if (len) {
41001d49a28SGlenn Strauss do { --len; } while (len && in->name[len-1] == '\0');
4115c717302SGlenn Strauss }
41201d49a28SGlenn Strauss stat_cache_handle_fdevent_fn(scf, fam_dir, in->name, len, code);
4135c717302SGlenn Strauss }
4145c717302SGlenn Strauss } while (rd + sizeof(struct inotify_event) + NAME_MAX + 1 > sizeof(buf));
4150b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
4160b00b13aSGlenn Strauss struct kevent kevl[256];
4170b00b13aSGlenn Strauss struct timespec t0 = { 0, 0 };
4180b00b13aSGlenn Strauss int n;
4190b00b13aSGlenn Strauss do {
4200b00b13aSGlenn Strauss n = kevent(scf->fd, NULL, 0, kevl, sizeof(kevl)/sizeof(*kevl), &t0);
4210b00b13aSGlenn Strauss if (n <= 0) break;
4220b00b13aSGlenn Strauss for (int i = 0; i < n; ++i) {
4230b00b13aSGlenn Strauss const struct kevent * const kev = kevl+i;
4240b00b13aSGlenn Strauss /* ignore events which may have been pending for
4250b00b13aSGlenn Strauss * paths recently cancelled via FAMCancelMonitor() */
4260b00b13aSGlenn Strauss int ndx = (int)(intptr_t)kev->udata;
4270b00b13aSGlenn Strauss scf->dirs = splaytree_splay(scf->dirs, ndx);
4280b00b13aSGlenn Strauss if (!scf->dirs || scf->dirs->key != ndx)
4290b00b13aSGlenn Strauss continue;
4300b00b13aSGlenn Strauss fam_dir_entry *fam_dir = scf->dirs->data;
4310b00b13aSGlenn Strauss if (fam_dir->req != (int)kev->ident)
4320b00b13aSGlenn Strauss continue;
4330b00b13aSGlenn Strauss /*(specific to use here in stat_cache.c)*/
4340b00b13aSGlenn Strauss /* note: stat_cache only monitors on directories,
4350b00b13aSGlenn Strauss * so events here are only on directories
4360b00b13aSGlenn Strauss * note: changes are treated as FAMDeleted since
4370b00b13aSGlenn Strauss * it is unknown which file in dir was changed
4380b00b13aSGlenn Strauss * This is not efficient, but this stat_cache mechanism also
4390b00b13aSGlenn Strauss * should not be used on frequently modified directories. */
4400b00b13aSGlenn Strauss int code = 0;
4410b00b13aSGlenn Strauss if (kev->fflags & (NOTE_WRITE|NOTE_ATTRIB|NOTE_EXTEND|NOTE_LINK))
4420b00b13aSGlenn Strauss code = FAMDeleted; /*(not FAMChanged; see comment above)*/
4430b00b13aSGlenn Strauss else if (kev->fflags & (NOTE_DELETE|NOTE_REVOKE))
4440b00b13aSGlenn Strauss code = FAMDeleted;
4450b00b13aSGlenn Strauss else if (kev->fflags & NOTE_RENAME)
4460b00b13aSGlenn Strauss code = FAMMoved;
4470b00b13aSGlenn Strauss if (kev->flags & EV_ERROR) /*(not expected; treat as FAMDeleted)*/
4480b00b13aSGlenn Strauss code = FAMDeleted;
4490b00b13aSGlenn Strauss stat_cache_handle_fdevent_fn(scf, fam_dir, NULL, 0, code);
4500b00b13aSGlenn Strauss }
4510b00b13aSGlenn Strauss } while (n == sizeof(kevl)/sizeof(*kevl));
4525c717302SGlenn Strauss #else
453f89f9191SGlenn Strauss for (int i = 0, ndx; i || (i = FAMPending(&scf->fam)) > 0; --i) {
4549287c87dSGlenn Strauss FAMEvent fe;
455a3d17152SGlenn Strauss if (FAMNextEvent(&scf->fam, &fe) < 0) break;
456c7eaa502SGlenn Strauss
457c7eaa502SGlenn Strauss /* ignore events which may have been pending for
458c7eaa502SGlenn Strauss * paths recently cancelled via FAMCancelMonitor() */
459f89f9191SGlenn Strauss ndx = (int)(intptr_t)fe.userdata;
460f89f9191SGlenn Strauss scf->dirs = splaytree_splay(scf->dirs, ndx);
461f89f9191SGlenn Strauss if (!scf->dirs || scf->dirs->key != ndx) {
46238ce7906SGlenn Strauss continue;
46338ce7906SGlenn Strauss }
46438ce7906SGlenn Strauss fam_dir_entry *fam_dir = scf->dirs->data;
465c7eaa502SGlenn Strauss if (FAMREQUEST_GETREQNUM(&fam_dir->req)
466c7eaa502SGlenn Strauss != FAMREQUEST_GETREQNUM(&fe.fr)) {
467c7eaa502SGlenn Strauss continue;
468c7eaa502SGlenn Strauss }
4699287c87dSGlenn Strauss
4705c717302SGlenn Strauss uint32_t fnlen = (fe.code != FAMCreated && fe.filename[0] != '/')
4715c717302SGlenn Strauss ? (uint32_t)strlen(fe.filename)
4725c717302SGlenn Strauss : 0;
4735c717302SGlenn Strauss stat_cache_handle_fdevent_fn(scf, fam_dir, fe.filename, fnlen, fe.code);
4745c717302SGlenn Strauss }
4755c717302SGlenn Strauss #endif
4765c717302SGlenn Strauss }
4775c717302SGlenn Strauss
stat_cache_handle_fdevent_fn(stat_cache_fam * const scf,fam_dir_entry * fam_dir,const char * const fn,const uint32_t fnlen,int code)4785c717302SGlenn Strauss static void stat_cache_handle_fdevent_fn(stat_cache_fam * const scf, fam_dir_entry *fam_dir, const char * const fn, const uint32_t fnlen, int code)
4795c717302SGlenn Strauss {
4805c717302SGlenn Strauss if (fnlen) {
481937d83b6SGlenn Strauss buffer * const n = &fam_dir->name;
4828cc189f4SGlenn Strauss fam_dir_entry *fam_link;
4835c717302SGlenn Strauss uint32_t len;
4845c717302SGlenn Strauss switch (code) {
4851971da13SGlenn Strauss case FAMCreated:
4868cc189f4SGlenn Strauss /* file created in monitored dir modifies dir and
4878cc189f4SGlenn Strauss * we should get a separate FAMChanged event for dir.
4888cc189f4SGlenn Strauss * Therefore, ignore file FAMCreated event here.
4898cc189f4SGlenn Strauss * Also, if FAMNoExists() is used, might get spurious
4908cc189f4SGlenn Strauss * FAMCreated events as changes are made e.g. in monitored
4918cc189f4SGlenn Strauss * sub-sub-sub dirs and the library discovers new (already
4928cc189f4SGlenn Strauss * existing) dir entries */
4935c717302SGlenn Strauss return;
4941971da13SGlenn Strauss case FAMChanged:
4951971da13SGlenn Strauss /* file changed in monitored dir does not modify dir */
4961971da13SGlenn Strauss case FAMDeleted:
4971971da13SGlenn Strauss case FAMMoved:
4981971da13SGlenn Strauss /* file deleted or moved in monitored dir modifies dir,
4991971da13SGlenn Strauss * but FAM provides separate notification for that */
5008cc189f4SGlenn Strauss
50157470365SGlenn Strauss /* temporarily append filename to dir in fam_dir->name to
50257470365SGlenn Strauss * construct path, then delete stat_cache entry (if any)*/
503af3df29aSGlenn Strauss len = buffer_clen(n);
5041212f609SGlenn Strauss buffer_append_path_len(n, fn, fnlen);
50557470365SGlenn Strauss /* (alternatively, could chose to stat() and update)*/
506af3df29aSGlenn Strauss stat_cache_invalidate_entry(BUF_PTR_LEN(n));
5078cc189f4SGlenn Strauss
5088cc189f4SGlenn Strauss fam_link = /*(check if might be symlink to monitored dir)*/
509af3df29aSGlenn Strauss stat_cache_sptree_find(&scf->dirs, BUF_PTR_LEN(n));
510937d83b6SGlenn Strauss if (fam_link && !buffer_is_equal(&fam_link->name, n))
5118cc189f4SGlenn Strauss fam_link = NULL;
5128cc189f4SGlenn Strauss
513af3df29aSGlenn Strauss buffer_truncate(n, len);
5148cc189f4SGlenn Strauss
5158cc189f4SGlenn Strauss if (fam_link) {
5168cc189f4SGlenn Strauss /* replaced symlink changes containing dir */
517937d83b6SGlenn Strauss stat_cache_invalidate_entry(n->ptr, len);
5188cc189f4SGlenn Strauss /* handle symlink to dir as deleted dir below */
5195c717302SGlenn Strauss code = FAMDeleted;
5208cc189f4SGlenn Strauss fam_dir = fam_link;
5211971da13SGlenn Strauss break;
5221971da13SGlenn Strauss }
5235c717302SGlenn Strauss return;
5248cc189f4SGlenn Strauss default:
5255c717302SGlenn Strauss return;
5268cc189f4SGlenn Strauss }
5271971da13SGlenn Strauss }
5281971da13SGlenn Strauss
5295c717302SGlenn Strauss switch(code) {
5309287c87dSGlenn Strauss case FAMChanged:
531af3df29aSGlenn Strauss stat_cache_invalidate_entry(BUF_PTR_LEN(&fam_dir->name));
5328772e85cSGlenn Strauss break;
5339287c87dSGlenn Strauss case FAMDeleted:
5349287c87dSGlenn Strauss case FAMMoved:
535af3df29aSGlenn Strauss stat_cache_delete_tree(BUF_PTR_LEN(&fam_dir->name));
5368cc189f4SGlenn Strauss fam_dir_invalidate_node(fam_dir);
5379bb01a49SGlenn Strauss if (scf->dirs)
538937d83b6SGlenn Strauss fam_dir_invalidate_tree(scf->dirs,
539af3df29aSGlenn Strauss BUF_PTR_LEN(&fam_dir->name));
54068d8d4c5SGlenn Strauss fam_dir_periodic_cleanup();
5419287c87dSGlenn Strauss break;
5429287c87dSGlenn Strauss default:
5439287c87dSGlenn Strauss break;
5449287c87dSGlenn Strauss }
5459287c87dSGlenn Strauss }
5469287c87dSGlenn Strauss
stat_cache_handle_fdevent(void * ctx,int revent)54705cc88ddSGlenn Strauss static handler_t stat_cache_handle_fdevent(void *ctx, int revent)
54860a4b5f1SGlenn Strauss {
54966bdd96dSGlenn Strauss stat_cache_fam * const scf = ctx; /* sc.scf */
55060a4b5f1SGlenn Strauss
55160a4b5f1SGlenn Strauss if (revent & FDEVENT_IN) {
55268d8d4c5SGlenn Strauss stat_cache_handle_fdevent_in(scf);
55360a4b5f1SGlenn Strauss }
55460a4b5f1SGlenn Strauss
555d5d02583SGlenn Strauss if (revent & (FDEVENT_HUP|FDEVENT_RDHUP)) {
5569287c87dSGlenn Strauss /* fam closed the connection */
55766bdd96dSGlenn Strauss log_error(scf->errh, __FILE__, __LINE__,
55884fb3344SGlenn Strauss "FAM connection closed; disabling stat_cache.");
55984fb3344SGlenn Strauss /* (although effectively STAT_CACHE_ENGINE_NONE,
56084fb3344SGlenn Strauss * do not change here so that periodic jobs clean up memory)*/
56168d8d4c5SGlenn Strauss /*sc.stat_cache_engine = STAT_CACHE_ENGINE_NONE; */
56266bdd96dSGlenn Strauss fdevent_fdnode_event_del(scf->ev, scf->fdn);
563*f0786a75SGlenn Strauss fdevent_unregister(scf->ev, scf->fdn);
5649113011dSGlenn Strauss scf->fdn = NULL;
5659287c87dSGlenn Strauss
5669287c87dSGlenn Strauss FAMClose(&scf->fam);
5679113011dSGlenn Strauss scf->fd = -1;
5689287c87dSGlenn Strauss }
5699287c87dSGlenn Strauss
5709287c87dSGlenn Strauss return HANDLER_GO_ON;
5719287c87dSGlenn Strauss }
5729287c87dSGlenn Strauss
stat_cache_init_fam(fdevents * ev,log_error_st * errh)57366bdd96dSGlenn Strauss static stat_cache_fam * stat_cache_init_fam(fdevents *ev, log_error_st *errh) {
5745e14db43SGlenn Strauss stat_cache_fam *scf = ck_calloc(1, sizeof(*scf));
5759113011dSGlenn Strauss scf->fd = -1;
57666bdd96dSGlenn Strauss scf->ev = ev;
57766bdd96dSGlenn Strauss scf->errh = errh;
5789287c87dSGlenn Strauss
5795c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
580fc38f2aaSGlenn Strauss #if !defined(IN_NONBLOCK) || !defined(IN_CLOEXEC)
581fc38f2aaSGlenn Strauss scf->fd = inotify_init();
582fc38f2aaSGlenn Strauss if (scf->fd >= 0 && 0 != fdevent_fcntl_set_nb_cloexec(scf->fd)) {
583fc38f2aaSGlenn Strauss close(scf->fd);
584fc38f2aaSGlenn Strauss scf->fd = -1;
585fc38f2aaSGlenn Strauss }
586fc38f2aaSGlenn Strauss #else
5875c717302SGlenn Strauss scf->fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
588fc38f2aaSGlenn Strauss #endif
5895c717302SGlenn Strauss if (scf->fd < 0) {
5905c717302SGlenn Strauss log_perror(errh, __FILE__, __LINE__, "inotify_init1()");
59101d49a28SGlenn Strauss free(scf);
5925c717302SGlenn Strauss return NULL;
5935c717302SGlenn Strauss }
5940b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
5950b00b13aSGlenn Strauss #ifdef __NetBSD__
5960b00b13aSGlenn Strauss scf->fd = kqueue1(O_NONBLOCK|O_CLOEXEC|O_NOSIGPIPE);
5970b00b13aSGlenn Strauss #else
5980b00b13aSGlenn Strauss scf->fd = kqueue();
5990b00b13aSGlenn Strauss if (scf->fd >= 0) fdevent_setfd_cloexec(scf->fd);
6000b00b13aSGlenn Strauss #endif
6010b00b13aSGlenn Strauss if (scf->fd < 0) {
6020b00b13aSGlenn Strauss log_perror(errh, __FILE__, __LINE__, "kqueue()");
60301d49a28SGlenn Strauss free(scf);
6040b00b13aSGlenn Strauss return NULL;
6050b00b13aSGlenn Strauss }
6065c717302SGlenn Strauss #else
6079287c87dSGlenn Strauss /* setup FAM */
6089287c87dSGlenn Strauss if (0 != FAMOpen2(&scf->fam, "lighttpd")) {
60966bdd96dSGlenn Strauss log_error(errh, __FILE__, __LINE__,
610c752d469SGlenn Strauss "could not open a fam connection, dying.");
61101d49a28SGlenn Strauss free(scf);
6129287c87dSGlenn Strauss return NULL;
6139287c87dSGlenn Strauss }
6149287c87dSGlenn Strauss #ifdef HAVE_FAMNOEXISTS
6153e046ccaSGlenn Strauss #ifdef LIGHTTPD_STATIC
6169287c87dSGlenn Strauss FAMNoExists(&scf->fam);
6173e046ccaSGlenn Strauss #else
6183e046ccaSGlenn Strauss int (*FAMNoExists_fn)(FAMConnection *);
6193e046ccaSGlenn Strauss FAMNoExists_fn =
6203e046ccaSGlenn Strauss (int (*)(FAMConnection *))(intptr_t)dlsym(RTLD_DEFAULT,"FAMNoExists");
6213e046ccaSGlenn Strauss if (FAMNoExists_fn) FAMNoExists_fn(&scf->fam);
6223e046ccaSGlenn Strauss #endif
623e8498bbfSGaël PORTAY #endif
624e1164797SGlenn Strauss
6259113011dSGlenn Strauss scf->fd = FAMCONNECTION_GETFD(&scf->fam);
6269113011dSGlenn Strauss fdevent_setfd_cloexec(scf->fd);
6275c717302SGlenn Strauss #endif
62866bdd96dSGlenn Strauss scf->fdn = fdevent_register(scf->ev, scf->fd, stat_cache_handle_fdevent, scf);
62966bdd96dSGlenn Strauss fdevent_fdnode_event_set(scf->ev, scf->fdn, FDEVENT_IN | FDEVENT_RDHUP);
6309287c87dSGlenn Strauss
6319287c87dSGlenn Strauss return scf;
6329287c87dSGlenn Strauss }
6339287c87dSGlenn Strauss
stat_cache_free_fam(stat_cache_fam * scf)6349287c87dSGlenn Strauss static void stat_cache_free_fam(stat_cache_fam *scf) {
6359287c87dSGlenn Strauss if (NULL == scf) return;
6369287c87dSGlenn Strauss
6375c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
6385c717302SGlenn Strauss while (scf->wds) {
6395c717302SGlenn Strauss splay_tree *node = scf->wds;
6405c717302SGlenn Strauss scf->wds = splaytree_delete(scf->wds, node->key);
6415c717302SGlenn Strauss }
6420b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
6430b00b13aSGlenn Strauss /*(quicker cleanup to close kqueue() before cancel per entry)*/
6440b00b13aSGlenn Strauss close(scf->fd);
6450b00b13aSGlenn Strauss scf->fd = -1;
6465c717302SGlenn Strauss #endif
6479287c87dSGlenn Strauss while (scf->dirs) {
6488cc189f4SGlenn Strauss /*(skip entry invalidation and FAMCancelMonitor())*/
6499287c87dSGlenn Strauss splay_tree *node = scf->dirs;
6508cc189f4SGlenn Strauss fam_dir_entry_free((fam_dir_entry *)node->data);
6519287c87dSGlenn Strauss scf->dirs = splaytree_delete(scf->dirs, node->key);
6529287c87dSGlenn Strauss }
6539287c87dSGlenn Strauss
6549113011dSGlenn Strauss if (-1 != scf->fd) {
6559113011dSGlenn Strauss /*scf->fdn already cleaned up in fdevent_free()*/
6569287c87dSGlenn Strauss FAMClose(&scf->fam);
6579113011dSGlenn Strauss /*scf->fd = -1;*/
6589287c87dSGlenn Strauss }
6599287c87dSGlenn Strauss
6609287c87dSGlenn Strauss free(scf);
6619287c87dSGlenn Strauss }
6629287c87dSGlenn Strauss
fam_dir_monitor(stat_cache_fam * scf,char * fn,uint32_t dirlen,struct stat * st)663d334eaf1SGlenn Strauss static fam_dir_entry * fam_dir_monitor(stat_cache_fam *scf, char *fn, uint32_t dirlen, struct stat *st)
6648cc189f4SGlenn Strauss {
66584fb3344SGlenn Strauss if (NULL == scf->fdn) return NULL; /* FAM connection closed; do nothing */
6668cc189f4SGlenn Strauss const int fn_is_dir = S_ISDIR(st->st_mode);
6678cc189f4SGlenn Strauss /*force_assert(0 != dirlen);*/
6688cc189f4SGlenn Strauss /*force_assert(fn[0] == '/');*/
6698cc189f4SGlenn Strauss /* consistency: ensure fn does not end in '/' unless root "/"
6708cc189f4SGlenn Strauss * FAM events will not end in '/', so easier to match this way */
6718cc189f4SGlenn Strauss if (fn[dirlen-1] == '/') --dirlen;
6728cc189f4SGlenn Strauss if (0 == dirlen) dirlen = 1; /* root dir ("/") */
6738cc189f4SGlenn Strauss /* Note: paths are expected to be normalized before calling stat_cache,
6748cc189f4SGlenn Strauss * e.g. without repeated '/' */
6758cc189f4SGlenn Strauss if (!fn_is_dir) {
6768cc189f4SGlenn Strauss while (fn[--dirlen] != '/') ;
6778cc189f4SGlenn Strauss if (0 == dirlen) dirlen = 1; /*(should not happen for file)*/
6789287c87dSGlenn Strauss }
679f85d1f90SGlenn Strauss int dir_ndx = splaytree_djbhash(fn, dirlen);
6808cc189f4SGlenn Strauss fam_dir_entry *fam_dir = NULL;
6819287c87dSGlenn Strauss
6828cc189f4SGlenn Strauss scf->dirs = splaytree_splay(scf->dirs, dir_ndx);
6838cc189f4SGlenn Strauss if (NULL != scf->dirs && scf->dirs->key == dir_ndx) {
6848cc189f4SGlenn Strauss fam_dir = scf->dirs->data;
685937d83b6SGlenn Strauss if (!buffer_eq_slen(&fam_dir->name, fn, dirlen)) {
68618faa091SGlenn Strauss /* hash collision; preserve existing
68718faa091SGlenn Strauss * do not monitor new to avoid cache thrashing */
6888cc189f4SGlenn Strauss return NULL;
6898cc189f4SGlenn Strauss }
6908cc189f4SGlenn Strauss /* directory already registered */
69118faa091SGlenn Strauss }
6929287c87dSGlenn Strauss
693309c1693SGlenn Strauss const unix_time64_t cur_ts = log_monotonic_secs;
6948cc189f4SGlenn Strauss struct stat lst;
6958cc189f4SGlenn Strauss int ck_dir = fn_is_dir;
696409bba80SGlenn Strauss if (!fn_is_dir && (NULL==fam_dir || cur_ts - fam_dir->stat_ts >= 16)) {
6978cc189f4SGlenn Strauss ck_dir = 1;
6988cc189f4SGlenn Strauss /*(temporarily modify fn)*/
6998cc189f4SGlenn Strauss fn[dirlen] = '\0';
7008cc189f4SGlenn Strauss if (0 != lstat(fn, &lst)) {
7018cc189f4SGlenn Strauss fn[dirlen] = '/';
7028cc189f4SGlenn Strauss return NULL;
7038cc189f4SGlenn Strauss }
7048cc189f4SGlenn Strauss if (!S_ISLNK(lst.st_mode)) {
7058cc189f4SGlenn Strauss st = &lst;
7068cc189f4SGlenn Strauss }
7078cc189f4SGlenn Strauss else if (0 != stat(fn, st)) { /*st passed in now is stat() of dir*/
7088cc189f4SGlenn Strauss fn[dirlen] = '/';
7098cc189f4SGlenn Strauss return NULL;
7108cc189f4SGlenn Strauss }
7118cc189f4SGlenn Strauss fn[dirlen] = '/';
7128cc189f4SGlenn Strauss }
7139287c87dSGlenn Strauss
7148cc189f4SGlenn Strauss int ck_lnk = (NULL == fam_dir);
7158cc189f4SGlenn Strauss if (ck_dir && NULL != fam_dir) {
7168cc189f4SGlenn Strauss /* check stat() matches device and inode, just in case an external event
7178cc189f4SGlenn Strauss * not being monitored occurs (e.g. rename of unmonitored parent dir)*/
7188cc189f4SGlenn Strauss if (st->st_dev != fam_dir->st_dev || st->st_ino != fam_dir->st_ino) {
7198cc189f4SGlenn Strauss ck_lnk = 1;
7208cc189f4SGlenn Strauss /*(modifies scf->dirs but no need to re-splay for dir_ndx since
7218cc189f4SGlenn Strauss * fam_dir is not NULL and so splaytree_insert not called below)*/
7228cc189f4SGlenn Strauss if (scf->dirs) fam_dir_invalidate_tree(scf->dirs, fn, dirlen);
7238cc189f4SGlenn Strauss if (!fn_is_dir) /*(if dir, caller is updating stat_cache_entry)*/
72468d8d4c5SGlenn Strauss stat_cache_update_entry(fn, dirlen, st, NULL);
7258cc189f4SGlenn Strauss /*(must not delete tree since caller is holding a valid node)*/
72668d8d4c5SGlenn Strauss stat_cache_invalidate_dir_tree(fn, dirlen);
7275c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
7285c717302SGlenn Strauss scf->wds = splaytree_delete(scf->wds, fam_dir->req);
7295c717302SGlenn Strauss #endif
7308cc189f4SGlenn Strauss if (0 != FAMCancelMonitor(&scf->fam, &fam_dir->req)
731937d83b6SGlenn Strauss || 0 != FAMMonitorDirectory(&scf->fam, fam_dir->name.ptr,
7328cc189f4SGlenn Strauss &fam_dir->req,
7338cc189f4SGlenn Strauss (void *)(intptr_t)dir_ndx)) {
7348cc189f4SGlenn Strauss fam_dir->stat_ts = 0; /* invalidate */
7358cc189f4SGlenn Strauss return NULL;
7368cc189f4SGlenn Strauss }
7378cc189f4SGlenn Strauss fam_dir->st_dev = st->st_dev;
7388cc189f4SGlenn Strauss fam_dir->st_ino = st->st_ino;
7395c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
7405c717302SGlenn Strauss scf->wds = splaytree_insert(scf->wds, fam_dir->req, fam_dir);
7415c717302SGlenn Strauss #endif
7428cc189f4SGlenn Strauss }
743409bba80SGlenn Strauss fam_dir->stat_ts = cur_ts;
7448cc189f4SGlenn Strauss }
7459287c87dSGlenn Strauss
7468cc189f4SGlenn Strauss if (NULL == fam_dir) {
7478cc189f4SGlenn Strauss fam_dir = fam_dir_entry_init(fn, dirlen);
7488cc189f4SGlenn Strauss
749937d83b6SGlenn Strauss if (0 != FAMMonitorDirectory(&scf->fam,fam_dir->name.ptr,&fam_dir->req,
7508cc189f4SGlenn Strauss (void *)(intptr_t)dir_ndx)) {
7510b00b13aSGlenn Strauss #if defined(HAVE_SYS_INOTIFY_H) \
7520b00b13aSGlenn Strauss || (defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE)
7535c717302SGlenn Strauss log_perror(scf->errh, __FILE__, __LINE__,
7545c717302SGlenn Strauss "monitoring dir failed: %s file: %s",
755937d83b6SGlenn Strauss fam_dir->name.ptr, fn);
7565c717302SGlenn Strauss #else
75766bdd96dSGlenn Strauss log_error(scf->errh, __FILE__, __LINE__,
758010c2894SGlenn Strauss "monitoring dir failed: %s file: %s %s",
759937d83b6SGlenn Strauss fam_dir->name.ptr, fn, FamErrlist[FAMErrno]);
7605c717302SGlenn Strauss #endif
7618cc189f4SGlenn Strauss fam_dir_entry_free(fam_dir);
7628cc189f4SGlenn Strauss return NULL;
7638cc189f4SGlenn Strauss }
7649287c87dSGlenn Strauss
7658cc189f4SGlenn Strauss scf->dirs = splaytree_insert(scf->dirs, dir_ndx, fam_dir);
7665c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
7675c717302SGlenn Strauss scf->wds = splaytree_insert(scf->wds, fam_dir->req, fam_dir);
7685c717302SGlenn Strauss #endif
769409bba80SGlenn Strauss fam_dir->stat_ts= cur_ts;
7708cc189f4SGlenn Strauss fam_dir->st_dev = st->st_dev;
7718cc189f4SGlenn Strauss fam_dir->st_ino = st->st_ino;
7728cc189f4SGlenn Strauss }
7738cc189f4SGlenn Strauss
7748cc189f4SGlenn Strauss if (ck_lnk) {
7758cc189f4SGlenn Strauss if (fn_is_dir) {
7768cc189f4SGlenn Strauss /*(temporarily modify fn)*/
7778cc189f4SGlenn Strauss char e = fn[dirlen];
7788cc189f4SGlenn Strauss fn[dirlen] = '\0';
7798cc189f4SGlenn Strauss if (0 != lstat(fn, &lst)) {
7808cc189f4SGlenn Strauss fn[dirlen] = e;
7818cc189f4SGlenn Strauss return NULL;
7828cc189f4SGlenn Strauss }
7838cc189f4SGlenn Strauss fn[dirlen] = e;
7848cc189f4SGlenn Strauss }
7858cc189f4SGlenn Strauss if (fam_dir->fam_parent) {
7868cc189f4SGlenn Strauss --fam_dir->fam_parent->refcnt;
7878cc189f4SGlenn Strauss fam_dir->fam_parent = NULL;
7888cc189f4SGlenn Strauss }
7898cc189f4SGlenn Strauss if (S_ISLNK(lst.st_mode)) {
79068d8d4c5SGlenn Strauss fam_dir->fam_parent = fam_dir_monitor(scf, fn, dirlen, &lst);
7919287c87dSGlenn Strauss }
7929287c87dSGlenn Strauss }
7939287c87dSGlenn Strauss
7948cc189f4SGlenn Strauss ++fam_dir->refcnt;
7958cc189f4SGlenn Strauss return fam_dir;
7969287c87dSGlenn Strauss }
7979287c87dSGlenn Strauss
7989287c87dSGlenn Strauss #endif
7999287c87dSGlenn Strauss
8009287c87dSGlenn Strauss
801b38817b6SGlenn Strauss __attribute_malloc__
8025e14db43SGlenn Strauss __attribute_noinline__
803b38817b6SGlenn Strauss __attribute_returns_nonnull__
stat_cache_entry_init(void)8045e134da0SJan Kneschke static stat_cache_entry * stat_cache_entry_init(void) {
8055e14db43SGlenn Strauss stat_cache_entry *sce = ck_calloc(1, sizeof(*sce));
806d8e5e21eSGlenn Strauss sce->fd = -1;
8077f8ab9ddSGlenn Strauss sce->refcnt = 1;
8085e134da0SJan Kneschke return sce;
8095e134da0SJan Kneschke }
8105e134da0SJan Kneschke
stat_cache_entry_free(void * data)8115e134da0SJan Kneschke static void stat_cache_entry_free(void *data) {
8125e134da0SJan Kneschke stat_cache_entry *sce = data;
8135e134da0SJan Kneschke if (!sce) return;
8145e134da0SJan Kneschke
8157f8ab9ddSGlenn Strauss if (--sce->refcnt) return;
8167f8ab9ddSGlenn Strauss
8178cc189f4SGlenn Strauss #ifdef HAVE_FAM_H
8188cc189f4SGlenn Strauss /*(decrement refcnt only;
8198cc189f4SGlenn Strauss * defer cancelling FAM monitor on dir even if refcnt reaches zero)*/
8208cc189f4SGlenn Strauss if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt;
8218cc189f4SGlenn Strauss #endif
8228cc189f4SGlenn Strauss
82368d8d4c5SGlenn Strauss free(sce->name.ptr);
82468d8d4c5SGlenn Strauss free(sce->etag.ptr);
82568d8d4c5SGlenn Strauss if (sce->content_type.size) free(sce->content_type.ptr);
826d8e5e21eSGlenn Strauss if (sce->fd >= 0) close(sce->fd);
8275e134da0SJan Kneschke
8285e134da0SJan Kneschke free(sce);
8295e134da0SJan Kneschke }
8305e134da0SJan Kneschke
stat_cache_entry_refchg(void * data,int mod)8317f8ab9ddSGlenn Strauss void stat_cache_entry_refchg(void *data, int mod) {
8327f8ab9ddSGlenn Strauss /*(expect mod == -1 or mod == 1)*/
8337f8ab9ddSGlenn Strauss stat_cache_entry * const sce = data;
8347f8ab9ddSGlenn Strauss if (mod < 0 && 1 == sce->refcnt)
8357f8ab9ddSGlenn Strauss stat_cache_entry_free(data);
8367f8ab9ddSGlenn Strauss else
8377f8ab9ddSGlenn Strauss sce->refcnt += mod;
8387f8ab9ddSGlenn Strauss }
8397f8ab9ddSGlenn Strauss
84068d8d4c5SGlenn Strauss #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
84168d8d4c5SGlenn Strauss
84268d8d4c5SGlenn Strauss static const char *attrname = "Content-Type";
84368d8d4c5SGlenn Strauss static char attrval[128];
84468d8d4c5SGlenn Strauss static buffer attrb = { attrval, 0, 0 };
84568d8d4c5SGlenn Strauss
stat_cache_attr_get(const char * name)846455dc037SGlenn Strauss static int stat_cache_attr_get(const char *name) {
84768d8d4c5SGlenn Strauss #if defined(HAVE_XATTR)
848455dc037SGlenn Strauss #if defined(HAVE_SYS_XATTR_H)
849455dc037SGlenn Strauss ssize_t attrlen;
850455dc037SGlenn Strauss if (0 < (attrlen = getxattr(name, attrname,
851455dc037SGlenn Strauss attrval, sizeof(attrval)-1)))
852455dc037SGlenn Strauss #else
85368d8d4c5SGlenn Strauss int attrlen = sizeof(attrval)-1;
85468d8d4c5SGlenn Strauss if (0 == attr_get(name, attrname, attrval, &attrlen, 0))
855455dc037SGlenn Strauss #endif
85668d8d4c5SGlenn Strauss #elif defined(HAVE_EXTATTR)
85768d8d4c5SGlenn Strauss ssize_t attrlen;
85868d8d4c5SGlenn Strauss if (0 < (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, attrname,
85968d8d4c5SGlenn Strauss attrval, sizeof(attrval)-1)))
86068d8d4c5SGlenn Strauss #endif
86168d8d4c5SGlenn Strauss {
86268d8d4c5SGlenn Strauss attrval[attrlen] = '\0';
86368d8d4c5SGlenn Strauss attrb.used = (uint32_t)(attrlen + 1);
86468d8d4c5SGlenn Strauss return 1;
86568d8d4c5SGlenn Strauss }
86668d8d4c5SGlenn Strauss return 0;
867d1e70da8SJan Kneschke }
868d1e70da8SJan Kneschke
8695e134da0SJan Kneschke #endif
87068d8d4c5SGlenn Strauss
stat_cache_init(fdevents * ev,log_error_st * errh)87166bdd96dSGlenn Strauss int stat_cache_init(fdevents *ev, log_error_st *errh) {
87268d8d4c5SGlenn Strauss #ifdef HAVE_FAM_H
87368d8d4c5SGlenn Strauss if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
87466bdd96dSGlenn Strauss sc.scf = stat_cache_init_fam(ev, errh);
87568d8d4c5SGlenn Strauss if (NULL == sc.scf) return 0;
87668d8d4c5SGlenn Strauss }
87766bdd96dSGlenn Strauss #else
87866bdd96dSGlenn Strauss UNUSED(ev);
87966bdd96dSGlenn Strauss UNUSED(errh);
88068d8d4c5SGlenn Strauss #endif
88168d8d4c5SGlenn Strauss
88268d8d4c5SGlenn Strauss return 1;
88368d8d4c5SGlenn Strauss }
88468d8d4c5SGlenn Strauss
stat_cache_free(void)88568d8d4c5SGlenn Strauss void stat_cache_free(void) {
88668d8d4c5SGlenn Strauss splay_tree *sptree = sc.files;
88768d8d4c5SGlenn Strauss while (sptree) {
88868d8d4c5SGlenn Strauss stat_cache_entry_free(sptree->data);
88968d8d4c5SGlenn Strauss sptree = splaytree_delete(sptree, sptree->key);
89068d8d4c5SGlenn Strauss }
89168d8d4c5SGlenn Strauss sc.files = NULL;
89268d8d4c5SGlenn Strauss
89368d8d4c5SGlenn Strauss #ifdef HAVE_FAM_H
89468d8d4c5SGlenn Strauss stat_cache_free_fam(sc.scf);
89568d8d4c5SGlenn Strauss sc.scf = NULL;
89668d8d4c5SGlenn Strauss #endif
89768d8d4c5SGlenn Strauss
89868d8d4c5SGlenn Strauss #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
89968d8d4c5SGlenn Strauss attrname = "Content-Type";
90068d8d4c5SGlenn Strauss #endif
90168d8d4c5SGlenn Strauss
90268d8d4c5SGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; /*(default)*/
90368d8d4c5SGlenn Strauss }
90468d8d4c5SGlenn Strauss
stat_cache_xattrname(const char * name)90568d8d4c5SGlenn Strauss void stat_cache_xattrname (const char *name) {
90668d8d4c5SGlenn Strauss #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
90768d8d4c5SGlenn Strauss attrname = name;
90868d8d4c5SGlenn Strauss #else
90968d8d4c5SGlenn Strauss UNUSED(name);
91068d8d4c5SGlenn Strauss #endif
9115e134da0SJan Kneschke }
9125e134da0SJan Kneschke
stat_cache_choose_engine(const buffer * stat_cache_string,log_error_st * errh)91366bdd96dSGlenn Strauss int stat_cache_choose_engine (const buffer *stat_cache_string, log_error_st *errh) {
914af3df29aSGlenn Strauss if (buffer_is_blank(stat_cache_string))
91568d8d4c5SGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE;
91668d8d4c5SGlenn Strauss else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("simple")))
91768d8d4c5SGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE;
9185c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
9195c717302SGlenn Strauss else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("inotify")))
9205c717302SGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_INOTIFY;
9215c717302SGlenn Strauss /*(STAT_CACHE_ENGINE_FAM == STAT_CACHE_ENGINE_INOTIFY)*/
9220b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
9230b00b13aSGlenn Strauss else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("kqueue")))
9240b00b13aSGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_KQUEUE;
9250b00b13aSGlenn Strauss /*(STAT_CACHE_ENGINE_FAM == STAT_CACHE_ENGINE_KQUEUE)*/
9265c717302SGlenn Strauss #endif
9279287c87dSGlenn Strauss #ifdef HAVE_FAM_H
92868d8d4c5SGlenn Strauss else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("fam")))
92968d8d4c5SGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_FAM;
9309287c87dSGlenn Strauss #endif
931730c932eSGlenn Strauss else if (buffer_eq_slen(stat_cache_string, CONST_STR_LEN("disable"))
932730c932eSGlenn Strauss || buffer_eq_slen(stat_cache_string, CONST_STR_LEN("none")))
93368d8d4c5SGlenn Strauss sc.stat_cache_engine = STAT_CACHE_ENGINE_NONE;
93468d8d4c5SGlenn Strauss else {
93566bdd96dSGlenn Strauss log_error(errh, __FILE__, __LINE__,
9369287c87dSGlenn Strauss "server.stat-cache-engine can be one of \"disable\", \"simple\","
9375c717302SGlenn Strauss #ifdef HAVE_SYS_INOTIFY_H
9385c717302SGlenn Strauss " \"inotify\","
9390b00b13aSGlenn Strauss #elif defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE
9400b00b13aSGlenn Strauss " \"kqueue\","
9415c717302SGlenn Strauss #endif
9429287c87dSGlenn Strauss #ifdef HAVE_FAM_H
9439287c87dSGlenn Strauss " \"fam\","
9449287c87dSGlenn Strauss #endif
945010c2894SGlenn Strauss " but not: %s", stat_cache_string->ptr);
9469287c87dSGlenn Strauss return -1;
9479287c87dSGlenn Strauss }
9489287c87dSGlenn Strauss return 0;
9499287c87dSGlenn Strauss }
9509287c87dSGlenn Strauss
stat_cache_mimetype_by_ext(const array * const mimetypes,const char * const name,const uint32_t nlen)951d334eaf1SGlenn Strauss const buffer * stat_cache_mimetype_by_ext(const array * const mimetypes, const char * const name, const uint32_t nlen)
9520cc7556aSGlenn Strauss {
95368d8d4c5SGlenn Strauss const char * const end = name + nlen; /*(end of string)*/
95468d8d4c5SGlenn Strauss const uint32_t used = mimetypes->used;
9550cc7556aSGlenn Strauss if (used < 16) {
95668d8d4c5SGlenn Strauss for (uint32_t i = 0; i < used; ++i) {
9570cc7556aSGlenn Strauss /* suffix match */
95868d8d4c5SGlenn Strauss const data_string *ds = (data_string *)mimetypes->data[i];
959af3df29aSGlenn Strauss const size_t klen = buffer_clen(&ds->key);
960ad9b7e00SGlenn Strauss if (klen <= nlen && buffer_eq_icase_ssn(end-klen, ds->key.ptr, klen))
961601c572cSGlenn Strauss return &ds->value;
9620cc7556aSGlenn Strauss }
9630cc7556aSGlenn Strauss }
9640cc7556aSGlenn Strauss else {
9650cc7556aSGlenn Strauss const char *s;
9660cc7556aSGlenn Strauss const data_string *ds;
9670cc7556aSGlenn Strauss if (nlen) {
9680cc7556aSGlenn Strauss for (s = end-1; s != name && *s != '/'; --s) ; /*(like memrchr())*/
9690cc7556aSGlenn Strauss if (*s == '/') ++s;
9700cc7556aSGlenn Strauss }
9710cc7556aSGlenn Strauss else {
9720cc7556aSGlenn Strauss s = name;
9730cc7556aSGlenn Strauss }
9740cc7556aSGlenn Strauss /* search for basename, then longest .ext2.ext1, then .ext1, then "" */
97568d8d4c5SGlenn Strauss ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s);
976601c572cSGlenn Strauss if (NULL != ds) return &ds->value;
9770cc7556aSGlenn Strauss while (++s < end) {
9780cc7556aSGlenn Strauss while (*s != '.' && ++s != end) ;
9799e46b8eaSGlenn Strauss if (s == end) break;
9809e46b8eaSGlenn Strauss /* search ".ext" then "ext" */
98168d8d4c5SGlenn Strauss ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s);
982601c572cSGlenn Strauss if (NULL != ds) return &ds->value;
9839e46b8eaSGlenn Strauss /* repeat search without leading '.' to handle situation where
9849e46b8eaSGlenn Strauss * admin configured mimetype.assign keys without leading '.' */
9859e46b8eaSGlenn Strauss if (++s < end) {
9869e46b8eaSGlenn Strauss if (*s == '.') { --s; continue; }
98768d8d4c5SGlenn Strauss ds = (const data_string *)array_get_element_klen(mimetypes, s, end - s);
988601c572cSGlenn Strauss if (NULL != ds) return &ds->value;
9890cc7556aSGlenn Strauss }
9900cc7556aSGlenn Strauss }
9919e46b8eaSGlenn Strauss /* search for ""; catchall */
99268d8d4c5SGlenn Strauss ds = (const data_string *)array_get_element_klen(mimetypes, CONST_STR_LEN(""));
993601c572cSGlenn Strauss if (NULL != ds) return &ds->value;
9949e46b8eaSGlenn Strauss }
9950cc7556aSGlenn Strauss
9960cc7556aSGlenn Strauss return NULL;
9970cc7556aSGlenn Strauss }
9980cc7556aSGlenn Strauss
99968d8d4c5SGlenn Strauss #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
100068d8d4c5SGlenn Strauss
stat_cache_mimetype_by_xattr(const char * const name)100168d8d4c5SGlenn Strauss const buffer * stat_cache_mimetype_by_xattr(const char * const name)
100268d8d4c5SGlenn Strauss {
100368d8d4c5SGlenn Strauss return stat_cache_attr_get(name) ? &attrb : NULL;
100468d8d4c5SGlenn Strauss }
100568d8d4c5SGlenn Strauss
stat_cache_content_type_get_by_xattr(stat_cache_entry * sce,const array * mimetypes,int use_xattr)100668d8d4c5SGlenn Strauss const buffer * stat_cache_content_type_get_by_xattr(stat_cache_entry *sce, const array *mimetypes, int use_xattr)
1007b1df38abSGlenn Strauss {
1008b1df38abSGlenn Strauss /*(invalid caching if user config has multiple, different
10097c7f8c46SGlenn Strauss * r->conf.mimetypes for same extension (not expected))*/
1010af3df29aSGlenn Strauss if (!buffer_is_blank(&sce->content_type)) return &sce->content_type;
1011b1df38abSGlenn Strauss
101268d8d4c5SGlenn Strauss if (!S_ISREG(sce->st.st_mode)) return NULL;
101368d8d4c5SGlenn Strauss
101468d8d4c5SGlenn Strauss /* cache mimetype */
101568d8d4c5SGlenn Strauss const buffer *mtype =
101668d8d4c5SGlenn Strauss (use_xattr) ? stat_cache_mimetype_by_xattr(sce->name.ptr) : NULL;
101768d8d4c5SGlenn Strauss if (NULL == mtype)
1018af3df29aSGlenn Strauss mtype = stat_cache_mimetype_by_ext(mimetypes, BUF_PTR_LEN(&sce->name));
101968d8d4c5SGlenn Strauss if (NULL != mtype) {
102068d8d4c5SGlenn Strauss if (sce->content_type.size) {
102168d8d4c5SGlenn Strauss buffer_copy_buffer(&sce->content_type, mtype);
1022b1df38abSGlenn Strauss }
102368d8d4c5SGlenn Strauss else if (mtype == &attrb) {
102468d8d4c5SGlenn Strauss sce->content_type.ptr = NULL;
102568d8d4c5SGlenn Strauss buffer_copy_buffer(&sce->content_type, mtype);
102668d8d4c5SGlenn Strauss }
102768d8d4c5SGlenn Strauss else {
102868d8d4c5SGlenn Strauss /*(copy pointers from mimetypes array; avoid allocation)*/
102968d8d4c5SGlenn Strauss sce->content_type.ptr = mtype->ptr;
103068d8d4c5SGlenn Strauss sce->content_type.used = mtype->used;
103168d8d4c5SGlenn Strauss /*(leave sce->content_type.size = 0 to flag not-allocated)*/
103268d8d4c5SGlenn Strauss }
103368d8d4c5SGlenn Strauss }
103468d8d4c5SGlenn Strauss else
103568d8d4c5SGlenn Strauss buffer_clear(&sce->content_type);
103668d8d4c5SGlenn Strauss
103768d8d4c5SGlenn Strauss return &sce->content_type;
103868d8d4c5SGlenn Strauss }
103968d8d4c5SGlenn Strauss
104068d8d4c5SGlenn Strauss #else
104168d8d4c5SGlenn Strauss
stat_cache_content_type_get_by_ext(stat_cache_entry * sce,const array * mimetypes)104268d8d4c5SGlenn Strauss const buffer * stat_cache_content_type_get_by_ext(stat_cache_entry *sce, const array *mimetypes)
104368d8d4c5SGlenn Strauss {
104468d8d4c5SGlenn Strauss /*(invalid caching if user config has multiple, different
10457c7f8c46SGlenn Strauss * r->conf.mimetypes for same extension (not expected))*/
1046af3df29aSGlenn Strauss if (!buffer_is_blank(&sce->content_type)) return &sce->content_type;
104768d8d4c5SGlenn Strauss
104868d8d4c5SGlenn Strauss if (!S_ISREG(sce->st.st_mode)) return NULL;
104968d8d4c5SGlenn Strauss
105068d8d4c5SGlenn Strauss /* cache mimetype */
105168d8d4c5SGlenn Strauss const buffer * const mtype =
1052af3df29aSGlenn Strauss stat_cache_mimetype_by_ext(mimetypes, BUF_PTR_LEN(&sce->name));
105368d8d4c5SGlenn Strauss if (NULL != mtype) {
105468d8d4c5SGlenn Strauss /*(copy pointers from mimetypes array; avoid allocation)*/
105568d8d4c5SGlenn Strauss sce->content_type.ptr = mtype->ptr;
105668d8d4c5SGlenn Strauss sce->content_type.used = mtype->used;
105768d8d4c5SGlenn Strauss /*(leave sce->content_type.size = 0 to flag not-allocated)*/
105868d8d4c5SGlenn Strauss }
105968d8d4c5SGlenn Strauss else
106068d8d4c5SGlenn Strauss buffer_clear(&sce->content_type);
106168d8d4c5SGlenn Strauss
106268d8d4c5SGlenn Strauss return &sce->content_type;
106368d8d4c5SGlenn Strauss }
106468d8d4c5SGlenn Strauss
1065b1df38abSGlenn Strauss #endif
1066b1df38abSGlenn Strauss
stat_cache_etag_get(stat_cache_entry * sce,int flags)1067ed62e354SGlenn Strauss const buffer * stat_cache_etag_get(stat_cache_entry *sce, int flags) {
10687c7f8c46SGlenn Strauss /*(invalid caching if user cfg has multiple, different r->conf.etag_flags
1069b1df38abSGlenn Strauss * for same path (not expected, since etag flags should be by filesystem))*/
1070af3df29aSGlenn Strauss if (!buffer_is_blank(&sce->etag)) return &sce->etag;
1071b1df38abSGlenn Strauss
1072b1df38abSGlenn Strauss if (S_ISREG(sce->st.st_mode) || S_ISDIR(sce->st.st_mode)) {
107368d8d4c5SGlenn Strauss if (0 == flags) return NULL;
1074b700a8caSGlenn Strauss http_etag_create(&sce->etag, &sce->st, flags);
107568d8d4c5SGlenn Strauss return &sce->etag;
1076b1df38abSGlenn Strauss }
1077b1df38abSGlenn Strauss
1078b1df38abSGlenn Strauss return NULL;
1079b1df38abSGlenn Strauss }
1080b1df38abSGlenn Strauss
1081d8e5e21eSGlenn Strauss __attribute_pure__
stat_cache_stat_eq(const struct stat * const sta,const struct stat * const stb)1082d8e5e21eSGlenn Strauss static int stat_cache_stat_eq(const struct stat * const sta, const struct stat * const stb) {
1083d8e5e21eSGlenn Strauss return
1084d8e5e21eSGlenn Strauss #ifdef st_mtime /* use high-precision timestamp if available */
1085d8e5e21eSGlenn Strauss #if defined(__APPLE__) && defined(__MACH__)
1086d8e5e21eSGlenn Strauss sta->st_mtimespec.tv_nsec == stb->st_mtimespec.tv_nsec
1087d8e5e21eSGlenn Strauss #else
1088d8e5e21eSGlenn Strauss sta->st_mtim.tv_nsec == stb->st_mtim.tv_nsec
1089d8e5e21eSGlenn Strauss #endif
10901d73fc23SGlenn Strauss #else
10911d73fc23SGlenn Strauss 1
1092d8e5e21eSGlenn Strauss #endif
1093d8e5e21eSGlenn Strauss && sta->st_mtime == stb->st_mtime
1094d8e5e21eSGlenn Strauss && sta->st_size == stb->st_size
1095d8e5e21eSGlenn Strauss && sta->st_ino == stb->st_ino
1096d8e5e21eSGlenn Strauss && sta->st_dev == stb->st_dev;
1097d8e5e21eSGlenn Strauss }
1098d8e5e21eSGlenn Strauss
stat_cache_update_entry(const char * name,uint32_t len,const struct stat * st,const buffer * etagb)1099d334eaf1SGlenn Strauss void stat_cache_update_entry(const char *name, uint32_t len,
1100df070173SGlenn Strauss const struct stat *st, const buffer *etagb)
110157470365SGlenn Strauss {
110268d8d4c5SGlenn Strauss if (sc.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return;
110357470365SGlenn Strauss force_assert(0 != len);
110457470365SGlenn Strauss if (name[len-1] == '/') { if (0 == --len) len = 1; }
110568d8d4c5SGlenn Strauss splay_tree **sptree = &sc.files;
110657470365SGlenn Strauss stat_cache_entry *sce =
110757470365SGlenn Strauss stat_cache_sptree_find(sptree, name, len);
110868d8d4c5SGlenn Strauss if (sce && buffer_is_equal_string(&sce->name, name, len)) {
1109d8e5e21eSGlenn Strauss if (!stat_cache_stat_eq(&sce->st, st)) {
1110d8e5e21eSGlenn Strauss /* etagb might be NULL to clear etag (invalidate) */
1111af3df29aSGlenn Strauss buffer_clear(&sce->etag);
1112af3df29aSGlenn Strauss if (etagb)
1113af3df29aSGlenn Strauss buffer_copy_string_len(&sce->etag, BUF_PTR_LEN(etagb));
11148cc189f4SGlenn Strauss #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
111568d8d4c5SGlenn Strauss buffer_clear(&sce->content_type);
11168cc189f4SGlenn Strauss #endif
1117d8e5e21eSGlenn Strauss if (sce->fd >= 0) {
11187f8ab9ddSGlenn Strauss if (1 == sce->refcnt) {
1119d8e5e21eSGlenn Strauss close(sce->fd);
1120d8e5e21eSGlenn Strauss sce->fd = -1;
1121d8e5e21eSGlenn Strauss }
11227f8ab9ddSGlenn Strauss else {
11237f8ab9ddSGlenn Strauss --sce->refcnt; /* stat_cache_entry_free(sce); */
11247f8ab9ddSGlenn Strauss (*sptree)->data = sce = stat_cache_entry_init();
11257f8ab9ddSGlenn Strauss buffer_copy_string_len(&sce->name, name, len);
11267f8ab9ddSGlenn Strauss }
11277f8ab9ddSGlenn Strauss }
1128d8e5e21eSGlenn Strauss sce->st = *st;
1129d8e5e21eSGlenn Strauss }
1130dbe3e236SGlenn Strauss sce->stat_ts = log_monotonic_secs;
113157470365SGlenn Strauss }
113257470365SGlenn Strauss }
113357470365SGlenn Strauss
stat_cache_delete_entry(const char * name,uint32_t len)1134d334eaf1SGlenn Strauss void stat_cache_delete_entry(const char *name, uint32_t len)
113557470365SGlenn Strauss {
113668d8d4c5SGlenn Strauss if (sc.stat_cache_engine == STAT_CACHE_ENGINE_NONE) return;
113757470365SGlenn Strauss force_assert(0 != len);
113857470365SGlenn Strauss if (name[len-1] == '/') { if (0 == --len) len = 1; }
113968d8d4c5SGlenn Strauss splay_tree **sptree = &sc.files;
114057470365SGlenn Strauss stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len);
114168d8d4c5SGlenn Strauss if (sce && buffer_is_equal_string(&sce->name, name, len)) {
114257470365SGlenn Strauss stat_cache_entry_free(sce);
114357470365SGlenn Strauss *sptree = splaytree_delete(*sptree, (*sptree)->key);
114457470365SGlenn Strauss }
114557470365SGlenn Strauss }
114657470365SGlenn Strauss
stat_cache_invalidate_entry(const char * name,uint32_t len)1147d334eaf1SGlenn Strauss void stat_cache_invalidate_entry(const char *name, uint32_t len)
114857470365SGlenn Strauss {
114968d8d4c5SGlenn Strauss splay_tree **sptree = &sc.files;
115057470365SGlenn Strauss stat_cache_entry *sce = stat_cache_sptree_find(sptree, name, len);
115168d8d4c5SGlenn Strauss if (sce && buffer_is_equal_string(&sce->name, name, len)) {
115257470365SGlenn Strauss sce->stat_ts = 0;
11538cc189f4SGlenn Strauss #ifdef HAVE_FAM_H
11548cc189f4SGlenn Strauss if (sce->fam_dir != NULL) {
11558cc189f4SGlenn Strauss --((fam_dir_entry *)sce->fam_dir)->refcnt;
11568cc189f4SGlenn Strauss sce->fam_dir = NULL;
11578cc189f4SGlenn Strauss }
11588cc189f4SGlenn Strauss #endif
115957470365SGlenn Strauss }
116057470365SGlenn Strauss }
116157470365SGlenn Strauss
1162146ea6baSGlenn Strauss #ifdef HAVE_FAM_H
1163146ea6baSGlenn Strauss
stat_cache_invalidate_dir_tree_walk(splay_tree * t,const char * name,size_t len)11648cc189f4SGlenn Strauss static void stat_cache_invalidate_dir_tree_walk(splay_tree *t,
11658cc189f4SGlenn Strauss const char *name, size_t len)
11668cc189f4SGlenn Strauss {
11678cc189f4SGlenn Strauss if (t->left) stat_cache_invalidate_dir_tree_walk(t->left, name, len);
11688cc189f4SGlenn Strauss if (t->right) stat_cache_invalidate_dir_tree_walk(t->right, name, len);
11698cc189f4SGlenn Strauss
1170af3df29aSGlenn Strauss const buffer * const b = &((stat_cache_entry *)t->data)->name;
1171af3df29aSGlenn Strauss const size_t blen = buffer_clen(b);
11728cc189f4SGlenn Strauss if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len)) {
11738cc189f4SGlenn Strauss stat_cache_entry *sce = t->data;
11748cc189f4SGlenn Strauss sce->stat_ts = 0;
11758cc189f4SGlenn Strauss if (sce->fam_dir != NULL) {
11768cc189f4SGlenn Strauss --((fam_dir_entry *)sce->fam_dir)->refcnt;
11778cc189f4SGlenn Strauss sce->fam_dir = NULL;
11788cc189f4SGlenn Strauss }
11798cc189f4SGlenn Strauss }
11808cc189f4SGlenn Strauss }
11818cc189f4SGlenn Strauss
stat_cache_invalidate_dir_tree(const char * name,size_t len)118268d8d4c5SGlenn Strauss static void stat_cache_invalidate_dir_tree(const char *name, size_t len)
11838cc189f4SGlenn Strauss {
118468d8d4c5SGlenn Strauss splay_tree * const sptree = sc.files;
11858cc189f4SGlenn Strauss if (sptree) stat_cache_invalidate_dir_tree_walk(sptree, name, len);
11868cc189f4SGlenn Strauss }
11878cc189f4SGlenn Strauss
11888cc189f4SGlenn Strauss #endif
11898cc189f4SGlenn Strauss
119057470365SGlenn Strauss /*
119157470365SGlenn Strauss * walk though splay_tree and collect contents of dir tree.
119257470365SGlenn Strauss * remove tagged entries in a second loop
119357470365SGlenn Strauss */
119457470365SGlenn Strauss
stat_cache_tag_dir_tree(splay_tree * t,const char * name,size_t len,int * keys,int * ndx)119557470365SGlenn Strauss static void stat_cache_tag_dir_tree(splay_tree *t, const char *name, size_t len,
119657470365SGlenn Strauss int *keys, int *ndx)
119757470365SGlenn Strauss {
119857470365SGlenn Strauss if (*ndx == 8192) return; /*(must match num array entries in keys[])*/
119957470365SGlenn Strauss if (t->left) stat_cache_tag_dir_tree(t->left, name, len, keys, ndx);
120057470365SGlenn Strauss if (t->right) stat_cache_tag_dir_tree(t->right, name, len, keys, ndx);
120157470365SGlenn Strauss if (*ndx == 8192) return; /*(must match num array entries in keys[])*/
120257470365SGlenn Strauss
1203af3df29aSGlenn Strauss const buffer * const b = &((stat_cache_entry *)t->data)->name;
1204af3df29aSGlenn Strauss const size_t blen = buffer_clen(b);
120557470365SGlenn Strauss if (blen > len && b->ptr[len] == '/' && 0 == memcmp(b->ptr, name, len))
120657470365SGlenn Strauss keys[(*ndx)++] = t->key;
120757470365SGlenn Strauss }
120857470365SGlenn Strauss
1209e11514b0SGlenn Strauss __attribute_noinline__
stat_cache_prune_dir_tree(const char * name,size_t len)121068d8d4c5SGlenn Strauss static void stat_cache_prune_dir_tree(const char *name, size_t len)
121157470365SGlenn Strauss {
1212e11514b0SGlenn Strauss splay_tree *sptree = sc.files;
121357470365SGlenn Strauss int max_ndx, i;
121457470365SGlenn Strauss int keys[8192]; /* 32k size on stack */
121557470365SGlenn Strauss do {
1216e11514b0SGlenn Strauss if (!sptree) break;
121757470365SGlenn Strauss max_ndx = 0;
121868d8d4c5SGlenn Strauss stat_cache_tag_dir_tree(sptree, name, len, keys, &max_ndx);
121957470365SGlenn Strauss for (i = 0; i < max_ndx; ++i) {
122057470365SGlenn Strauss const int ndx = keys[i];
122168d8d4c5SGlenn Strauss splay_tree *node = sptree = splaytree_splay(sptree, ndx);
122257470365SGlenn Strauss if (node && node->key == ndx) {
122357470365SGlenn Strauss stat_cache_entry_free(node->data);
122468d8d4c5SGlenn Strauss sptree = splaytree_delete(sptree, ndx);
122557470365SGlenn Strauss }
122657470365SGlenn Strauss }
122757470365SGlenn Strauss } while (max_ndx == sizeof(keys)/sizeof(int));
122868d8d4c5SGlenn Strauss sc.files = sptree;
122957470365SGlenn Strauss }
123057470365SGlenn Strauss
stat_cache_delete_tree(const char * name,uint32_t len)1231d334eaf1SGlenn Strauss static void stat_cache_delete_tree(const char *name, uint32_t len)
123257470365SGlenn Strauss {
123368d8d4c5SGlenn Strauss stat_cache_delete_entry(name, len);
123468d8d4c5SGlenn Strauss stat_cache_prune_dir_tree(name, len);
123557470365SGlenn Strauss }
123657470365SGlenn Strauss
stat_cache_delete_dir(const char * name,uint32_t len)1237d334eaf1SGlenn Strauss void stat_cache_delete_dir(const char *name, uint32_t len)
123857470365SGlenn Strauss {
123957470365SGlenn Strauss force_assert(0 != len);
124057470365SGlenn Strauss if (name[len-1] == '/') { if (0 == --len) len = 1; }
124168d8d4c5SGlenn Strauss stat_cache_delete_tree(name, len);
124257470365SGlenn Strauss #ifdef HAVE_FAM_H
124368d8d4c5SGlenn Strauss if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
124468d8d4c5SGlenn Strauss splay_tree **sptree = &sc.scf->dirs;
12459bb01a49SGlenn Strauss fam_dir_entry *fam_dir = stat_cache_sptree_find(sptree, name, len);
1246937d83b6SGlenn Strauss if (fam_dir && buffer_eq_slen(&fam_dir->name, name, len))
12478cc189f4SGlenn Strauss fam_dir_invalidate_node(fam_dir);
12489bb01a49SGlenn Strauss if (*sptree) fam_dir_invalidate_tree(*sptree, name, len);
124968d8d4c5SGlenn Strauss fam_dir_periodic_cleanup();
125057470365SGlenn Strauss }
125157470365SGlenn Strauss #endif
125257470365SGlenn Strauss }
1253657a024dSMarcus Rückert
12545e134da0SJan Kneschke /***
12555e134da0SJan Kneschke *
12565e134da0SJan Kneschke *
12575e134da0SJan Kneschke *
12585e134da0SJan Kneschke * returns:
12595e134da0SJan Kneschke * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
12605e134da0SJan Kneschke * - HANDLER_ERROR on stat() failed -> see errno for problem
12615e134da0SJan Kneschke */
12625e134da0SJan Kneschke
stat_cache_get_entry(const buffer * const name)1263ac1fee6bSGlenn Strauss stat_cache_entry * stat_cache_get_entry(const buffer * const name) {
12645e134da0SJan Kneschke stat_cache_entry *sce = NULL;
12655e134da0SJan Kneschke
1266baa5f043SGlenn Strauss /* consistency: ensure lookup name does not end in '/' unless root "/"
1267baa5f043SGlenn Strauss * (but use full path given with stat(), even with trailing '/') */
1268baa5f043SGlenn Strauss int final_slash = 0;
1269af3df29aSGlenn Strauss size_t len = buffer_clen(name);
1270baa5f043SGlenn Strauss force_assert(0 != len);
1271baa5f043SGlenn Strauss if (name->ptr[len-1] == '/') { final_slash = 1; if (0 == --len) len = 1; }
1272baa5f043SGlenn Strauss /* Note: paths are expected to be normalized before calling stat_cache,
1273baa5f043SGlenn Strauss * e.g. without repeated '/' */
1274baa5f043SGlenn Strauss
1275abe61d04SGlenn Strauss if (name->ptr[0] != '/') {
1276abe61d04SGlenn Strauss errno = EINVAL;
1277abe61d04SGlenn Strauss return NULL;
1278abe61d04SGlenn Strauss }
1279baa5f043SGlenn Strauss
12805e134da0SJan Kneschke /*
12815e134da0SJan Kneschke * check if the directory for this file has changed
12825e134da0SJan Kneschke */
12835e134da0SJan Kneschke
1284309c1693SGlenn Strauss const unix_time64_t cur_ts = log_monotonic_secs;
128550bdb55dSGlenn Strauss
1286ac1fee6bSGlenn Strauss const int file_ndx = splaytree_djbhash(name->ptr, len);
1287af04e0b0SGlenn Strauss splay_tree *sptree = sc.files = splaytree_splay(sc.files, file_ndx);
12885e134da0SJan Kneschke
128968d8d4c5SGlenn Strauss if (sptree && (sptree->key == file_ndx)) {
12905e134da0SJan Kneschke /* we have seen this file already and
12915e134da0SJan Kneschke * don't stat() it again in the same second */
12925e134da0SJan Kneschke
129368d8d4c5SGlenn Strauss sce = sptree->data;
12945e134da0SJan Kneschke
1295ecb30c4eSJan Kneschke /* check if the name is the same, we might have a collision */
1296ecb30c4eSJan Kneschke
129768d8d4c5SGlenn Strauss if (buffer_is_equal_string(&sce->name, name->ptr, len)) {
129868d8d4c5SGlenn Strauss if (sc.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
1299409bba80SGlenn Strauss if (sce->stat_ts == cur_ts) {
1300baa5f043SGlenn Strauss if (final_slash && !S_ISDIR(sce->st.st_mode)) {
1301baa5f043SGlenn Strauss errno = ENOTDIR;
130268d8d4c5SGlenn Strauss return NULL;
1303baa5f043SGlenn Strauss }
130468d8d4c5SGlenn Strauss return sce;
13055e134da0SJan Kneschke }
13065e134da0SJan Kneschke }
13075e134da0SJan Kneschke #ifdef HAVE_FAM_H
130868d8d4c5SGlenn Strauss else if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM
13098cc189f4SGlenn Strauss && sce->fam_dir) { /* entry is in monitored dir */
13108cc189f4SGlenn Strauss /* re-stat() periodically, even if monitoring for changes
13118cc189f4SGlenn Strauss * (due to limitations in stat_cache.c use of FAM)
13128cc189f4SGlenn Strauss * (gaps due to not continually monitoring an entire tree) */
1313409bba80SGlenn Strauss if (cur_ts - sce->stat_ts < 16) {
1314baa5f043SGlenn Strauss if (final_slash && !S_ISDIR(sce->st.st_mode)) {
1315baa5f043SGlenn Strauss errno = ENOTDIR;
131668d8d4c5SGlenn Strauss return NULL;
1317baa5f043SGlenn Strauss }
131868d8d4c5SGlenn Strauss return sce;
13195e134da0SJan Kneschke }
13205e134da0SJan Kneschke }
13215e134da0SJan Kneschke #endif
13228cc189f4SGlenn Strauss } else {
13238cc189f4SGlenn Strauss /* collision, forget about the entry */
13248cc189f4SGlenn Strauss sce = NULL;
13258cc189f4SGlenn Strauss }
13268cc189f4SGlenn Strauss }
1327478cb34bSJan Kneschke
1328ac1fee6bSGlenn Strauss struct stat st;
1329478cb34bSJan Kneschke if (-1 == stat(name->ptr, &st)) {
133068d8d4c5SGlenn Strauss return NULL;
1331478cb34bSJan Kneschke }
1332478cb34bSJan Kneschke
1333ac1fee6bSGlenn Strauss if (NULL == sce) {
1334ac1fee6bSGlenn Strauss
133557066345SStefan Bühler /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
1336ac1fee6bSGlenn Strauss if (final_slash && S_ISREG(st.st_mode)) {
133757066345SStefan Bühler errno = ENOTDIR;
133868d8d4c5SGlenn Strauss return NULL;
133957066345SStefan Bühler }
1340ecb30c4eSJan Kneschke
13415e134da0SJan Kneschke sce = stat_cache_entry_init();
134268d8d4c5SGlenn Strauss buffer_copy_string_len(&sce->name, name->ptr, len);
13435e134da0SJan Kneschke
134469f890e2SStefan Bühler /* already splayed file_ndx */
134568d8d4c5SGlenn Strauss if (NULL != sptree && sptree->key == file_ndx) {
134669f890e2SStefan Bühler /* hash collision: replace old entry */
134768d8d4c5SGlenn Strauss stat_cache_entry_free(sptree->data);
134868d8d4c5SGlenn Strauss sptree->data = sce;
134969f890e2SStefan Bühler } else {
13509a2404ceSGlenn Strauss /*sptree =*/ sc.files = splaytree_insert(sptree, file_ndx, sce);
135169f890e2SStefan Bühler }
1352b1df38abSGlenn Strauss
1353b1df38abSGlenn Strauss } else {
1354b1df38abSGlenn Strauss
135568d8d4c5SGlenn Strauss buffer_clear(&sce->etag);
1356b1df38abSGlenn Strauss #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
135768d8d4c5SGlenn Strauss buffer_clear(&sce->content_type);
1358b1df38abSGlenn Strauss #endif
1359b1df38abSGlenn Strauss
1360d8e5e21eSGlenn Strauss /* close fd if file changed */
1361ac1fee6bSGlenn Strauss if (sce->fd >= 0 && !stat_cache_stat_eq(&sce->st, &st)) {
13627f8ab9ddSGlenn Strauss if (1 == sce->refcnt) {
1363d8e5e21eSGlenn Strauss close(sce->fd);
1364d8e5e21eSGlenn Strauss sce->fd = -1;
1365d8e5e21eSGlenn Strauss }
13667f8ab9ddSGlenn Strauss else {
13677f8ab9ddSGlenn Strauss --sce->refcnt; /* stat_cache_entry_free(sce); */
13687f8ab9ddSGlenn Strauss sptree->data = sce = stat_cache_entry_init();
13697f8ab9ddSGlenn Strauss buffer_copy_string_len(&sce->name, name->ptr, len);
13707f8ab9ddSGlenn Strauss }
13717f8ab9ddSGlenn Strauss }
1372d8e5e21eSGlenn Strauss }
1373d8e5e21eSGlenn Strauss
13748cc189f4SGlenn Strauss sce->st = st; /*(copy prior to calling fam_dir_monitor())*/
13755e134da0SJan Kneschke
13765e134da0SJan Kneschke #ifdef HAVE_FAM_H
137768d8d4c5SGlenn Strauss if (sc.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
13788cc189f4SGlenn Strauss if (sce->fam_dir) --((fam_dir_entry *)sce->fam_dir)->refcnt;
13798cc189f4SGlenn Strauss sce->fam_dir =
1380af3df29aSGlenn Strauss fam_dir_monitor(sc.scf, name->ptr, len, &st);
13818cc189f4SGlenn Strauss #if 0 /*(performed below)*/
13828cc189f4SGlenn Strauss if (NULL != sce->fam_dir) {
13838cc189f4SGlenn Strauss /*(may have been invalidated by dir change)*/
1384409bba80SGlenn Strauss sce->stat_ts = cur_ts;
13858cc189f4SGlenn Strauss }
13868cc189f4SGlenn Strauss #endif
13875e134da0SJan Kneschke }
13885e134da0SJan Kneschke #endif
138951978868SJan Kneschke
1390409bba80SGlenn Strauss sce->stat_ts = cur_ts;
139168d8d4c5SGlenn Strauss return sce;
13925e134da0SJan Kneschke }
13935e134da0SJan Kneschke
stat_cache_get_entry_open(const buffer * const name,const int symlinks)1394d8e5e21eSGlenn Strauss stat_cache_entry * stat_cache_get_entry_open(const buffer * const name, const int symlinks) {
1395d8e5e21eSGlenn Strauss stat_cache_entry * const sce = stat_cache_get_entry(name);
1396d8e5e21eSGlenn Strauss if (NULL == sce) return NULL;
1397d8e5e21eSGlenn Strauss if (sce->fd >= 0) return sce;
1398d22e94acSGlenn Strauss if (sce->st.st_size > 0) {
1399d8e5e21eSGlenn Strauss sce->fd = stat_cache_open_rdonly_fstat(name, &sce->st, symlinks);
1400d22e94acSGlenn Strauss buffer_clear(&sce->etag);
1401d22e94acSGlenn Strauss }
1402d8e5e21eSGlenn Strauss return sce; /* (note: sce->fd might still be -1 if open() failed) */
1403d8e5e21eSGlenn Strauss }
1404d8e5e21eSGlenn Strauss
stat_cache_path_stat(const buffer * const name)1405fe021118SGlenn Strauss const stat_cache_st * stat_cache_path_stat (const buffer * const name) {
1406fe021118SGlenn Strauss const stat_cache_entry * const sce = stat_cache_get_entry(name);
1407fe021118SGlenn Strauss return sce ? &sce->st : NULL;
1408fe021118SGlenn Strauss }
1409fe021118SGlenn Strauss
stat_cache_path_isdir(const buffer * name)14107d368cd7SGlenn Strauss int stat_cache_path_isdir(const buffer *name) {
14117d368cd7SGlenn Strauss const stat_cache_entry * const sce = stat_cache_get_entry(name);
14127d368cd7SGlenn Strauss return (sce && (S_ISDIR(sce->st.st_mode) ? 1 : (errno = ENOTDIR, 0)));
14137d368cd7SGlenn Strauss }
14147d368cd7SGlenn Strauss
stat_cache_path_contains_symlink(const buffer * name,log_error_st * errh)141568d8d4c5SGlenn Strauss int stat_cache_path_contains_symlink(const buffer *name, log_error_st *errh) {
141673bfee63SGlenn Strauss /* caller should check for symlinks only if we should block symlinks. */
141773bfee63SGlenn Strauss
141873bfee63SGlenn Strauss /* catch the obvious symlinks
141973bfee63SGlenn Strauss *
142073bfee63SGlenn Strauss * this is not a secure check as we still have a race-condition between
142173bfee63SGlenn Strauss * the stat() and the open. We can only solve this by
142273bfee63SGlenn Strauss * 1. open() the file
142373bfee63SGlenn Strauss * 2. fstat() the fd
142473bfee63SGlenn Strauss *
142573bfee63SGlenn Strauss * and keeping the file open for the rest of the time. But this can
142673bfee63SGlenn Strauss * only be done at network level.
142773bfee63SGlenn Strauss * */
142873bfee63SGlenn Strauss
142973bfee63SGlenn Strauss #ifdef HAVE_LSTAT
143073bfee63SGlenn Strauss /* we assume "/" can not be symlink,
143173bfee63SGlenn Strauss * so skip the symlink stuff if path is "/" */
1432af3df29aSGlenn Strauss size_t len = buffer_clen(name);
143373bfee63SGlenn Strauss force_assert(0 != len);
143473bfee63SGlenn Strauss force_assert(name->ptr[0] == '/');
143573bfee63SGlenn Strauss if (1 == len) return 0;
143673bfee63SGlenn Strauss #ifndef PATH_MAX
143773bfee63SGlenn Strauss #define PATH_MAX 4096
143873bfee63SGlenn Strauss #endif
143973bfee63SGlenn Strauss if (len >= PATH_MAX) return -1;
144073bfee63SGlenn Strauss
144173bfee63SGlenn Strauss char buf[PATH_MAX];
144273bfee63SGlenn Strauss memcpy(buf, name->ptr, len);
144373bfee63SGlenn Strauss char *s_cur = buf+len;
144473bfee63SGlenn Strauss do {
144573bfee63SGlenn Strauss *s_cur = '\0';
144673bfee63SGlenn Strauss struct stat st;
144773bfee63SGlenn Strauss if (0 == lstat(buf, &st)) {
144873bfee63SGlenn Strauss if (S_ISLNK(st.st_mode)) return 1;
144973bfee63SGlenn Strauss }
145073bfee63SGlenn Strauss else {
145168d8d4c5SGlenn Strauss log_perror(errh, __FILE__, __LINE__, "lstat failed for: %s", buf);
145273bfee63SGlenn Strauss return -1;
145373bfee63SGlenn Strauss }
14542781a3beSGlenn Strauss } while ((s_cur = strrchr(buf, '/')) > buf); /*(&buf[0]==buf; NULL < buf)*/
145573bfee63SGlenn Strauss #endif
145673bfee63SGlenn Strauss
145773bfee63SGlenn Strauss return 0;
145873bfee63SGlenn Strauss }
145973bfee63SGlenn Strauss
stat_cache_open_rdonly_fstat(const buffer * name,struct stat * st,int symlinks)14600fcd5143SGlenn Strauss int stat_cache_open_rdonly_fstat (const buffer *name, struct stat *st, int symlinks) {
1461a65c57a5SGlenn Strauss /*(Note: O_NOFOLLOW affects only the final path segment, the target file,
1462a65c57a5SGlenn Strauss * not any intermediate symlinks along the path)*/
146337bd124aSGlenn Strauss const int fd = fdevent_open_cloexec(name->ptr, symlinks, O_RDONLY, 0);
1464a65c57a5SGlenn Strauss if (fd >= 0) {
1465a65c57a5SGlenn Strauss if (0 == fstat(fd, st)) {
1466a65c57a5SGlenn Strauss return fd;
1467a65c57a5SGlenn Strauss } else {
1468b7370a6dSGlenn Strauss const int errnum = errno;
1469a65c57a5SGlenn Strauss close(fd);
1470b7370a6dSGlenn Strauss errno = errnum;
1471a65c57a5SGlenn Strauss }
1472a65c57a5SGlenn Strauss }
1473a65c57a5SGlenn Strauss return -1;
1474a65c57a5SGlenn Strauss }
1475a65c57a5SGlenn Strauss
14765e134da0SJan Kneschke /**
1477f89f9191SGlenn Strauss * remove stat() from cache which haven't been stat()ed for
1478f89f9191SGlenn Strauss * more than 2 seconds
14795e134da0SJan Kneschke *
14805e134da0SJan Kneschke *
14815e134da0SJan Kneschke * walk though the stat-cache, collect the ids which are too old
14825e134da0SJan Kneschke * and remove them in a second loop
14835e134da0SJan Kneschke */
14845e134da0SJan Kneschke
stat_cache_tag_old_entries(splay_tree * const t,int * const keys,int * const ndx,const time_t max_age,const unix_time64_t cur_ts)1485309c1693SGlenn Strauss static void stat_cache_tag_old_entries(splay_tree * const t, int * const keys, int * const ndx, const time_t max_age, const unix_time64_t cur_ts) {
1486e11514b0SGlenn Strauss if (*ndx == 8192) return; /*(must match num array entries in keys[])*/
1487e11514b0SGlenn Strauss if (t->left)
1488409bba80SGlenn Strauss stat_cache_tag_old_entries(t->left, keys, ndx, max_age, cur_ts);
1489e11514b0SGlenn Strauss if (t->right)
1490409bba80SGlenn Strauss stat_cache_tag_old_entries(t->right, keys, ndx, max_age, cur_ts);
1491e11514b0SGlenn Strauss if (*ndx == 8192) return; /*(must match num array entries in keys[])*/
14925e134da0SJan Kneschke
1493409bba80SGlenn Strauss const stat_cache_entry * const sce = t->data;
1494e11514b0SGlenn Strauss if (cur_ts - sce->stat_ts > max_age)
14955e134da0SJan Kneschke keys[(*ndx)++] = t->key;
14965e134da0SJan Kneschke }
14975e134da0SJan Kneschke
stat_cache_periodic_cleanup(const time_t max_age,const unix_time64_t cur_ts)1498309c1693SGlenn Strauss static void stat_cache_periodic_cleanup(const time_t max_age, const unix_time64_t cur_ts) {
149968d8d4c5SGlenn Strauss splay_tree *sptree = sc.files;
1500e11514b0SGlenn Strauss int max_ndx, i;
1501e11514b0SGlenn Strauss int keys[8192]; /* 32k size on stack */
1502e11514b0SGlenn Strauss do {
1503e11514b0SGlenn Strauss if (!sptree) break;
1504e11514b0SGlenn Strauss max_ndx = 0;
150568d8d4c5SGlenn Strauss stat_cache_tag_old_entries(sptree, keys, &max_ndx, max_age, cur_ts);
1506e11514b0SGlenn Strauss for (i = 0; i < max_ndx; ++i) {
15075e134da0SJan Kneschke int ndx = keys[i];
150868d8d4c5SGlenn Strauss sptree = splaytree_splay(sptree, ndx);
150968d8d4c5SGlenn Strauss if (sptree && sptree->key == ndx) {
151068d8d4c5SGlenn Strauss stat_cache_entry_free(sptree->data);
151168d8d4c5SGlenn Strauss sptree = splaytree_delete(sptree, ndx);
1512b795fd36SJan Kneschke }
15135e134da0SJan Kneschke }
1514e11514b0SGlenn Strauss } while (max_ndx == sizeof(keys)/sizeof(int));
151568d8d4c5SGlenn Strauss sc.files = sptree;
15165e134da0SJan Kneschke }
151747d006aeSGlenn Strauss
stat_cache_trigger_cleanup(void)151868d8d4c5SGlenn Strauss void stat_cache_trigger_cleanup(void) {
151947d006aeSGlenn Strauss time_t max_age = 2;
152047d006aeSGlenn Strauss
15218cc189f4SGlenn Strauss #ifdef HAVE_FAM_H
152268d8d4c5SGlenn Strauss if (STAT_CACHE_ENGINE_FAM == sc.stat_cache_engine) {
1523dbe3e236SGlenn Strauss if (log_monotonic_secs & 0x1F) return;
15248cc189f4SGlenn Strauss /* once every 32 seconds (0x1F == 31) */
15258cc189f4SGlenn Strauss max_age = 32;
152668d8d4c5SGlenn Strauss fam_dir_periodic_cleanup();
15278cc189f4SGlenn Strauss /* By doing this before stat_cache_periodic_cleanup(),
15288cc189f4SGlenn Strauss * entries used within the next max_age secs will remain
15298cc189f4SGlenn Strauss * monitored, instead of effectively flushing and
15308cc189f4SGlenn Strauss * rebuilding the FAM monitoring every max_age seconds */
15318cc189f4SGlenn Strauss }
15328cc189f4SGlenn Strauss #endif
15338cc189f4SGlenn Strauss
1534dbe3e236SGlenn Strauss stat_cache_periodic_cleanup(max_age, log_monotonic_secs);
153547d006aeSGlenn Strauss }
1536