xref: /lighttpd1.4/src/stat_cache.c (revision 9287c87d)
18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss 
322e8b456SStefan Bühler #include "log.h"
422e8b456SStefan Bühler #include "stat_cache.h"
522e8b456SStefan Bühler #include "fdevent.h"
622e8b456SStefan Bühler #include "etag.h"
7e1164797SGlenn Strauss #include "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 
185e134da0SJan Kneschke #ifdef HAVE_ATTR_ATTRIBUTES_H
195e134da0SJan Kneschke # include <attr/attributes.h>
205e134da0SJan Kneschke #endif
215e134da0SJan Kneschke 
224d55d4adSMoritz Wilhelmy #ifdef HAVE_SYS_EXTATTR_H
234d55d4adSMoritz Wilhelmy # include <sys/extattr.h>
244d55d4adSMoritz Wilhelmy #endif
254d55d4adSMoritz Wilhelmy 
265e134da0SJan Kneschke #ifndef HAVE_LSTAT
275e134da0SJan Kneschke # define lstat stat
285e134da0SJan Kneschke #endif
295e134da0SJan Kneschke 
305e134da0SJan Kneschke /*
315e134da0SJan Kneschke  * stat-cache
325e134da0SJan Kneschke  *
335e134da0SJan Kneschke  * we cache the stat() calls in our own storage
345e134da0SJan Kneschke  * the directories are cached in FAM
355e134da0SJan Kneschke  *
365e134da0SJan Kneschke  * if we get a change-event from FAM, we increment the version in the FAM->dir mapping
375e134da0SJan Kneschke  *
385e134da0SJan Kneschke  * if the stat()-cache is queried we check if the version id for the directory is the
395e134da0SJan Kneschke  * same and return immediatly.
405e134da0SJan Kneschke  *
415e134da0SJan Kneschke  *
425e134da0SJan Kneschke  * What we need:
435e134da0SJan Kneschke  *
445e134da0SJan Kneschke  * - for each stat-cache entry we need a fast indirect lookup on the directory name
455e134da0SJan Kneschke  * - for each FAMRequest we have to find the version in the directory cache (index as userdata)
465e134da0SJan Kneschke  *
475e134da0SJan Kneschke  * stat <<-> directory <-> FAMRequest
485e134da0SJan Kneschke  *
495e134da0SJan Kneschke  * if file is deleted, directory is dirty, file is rechecked ...
505e134da0SJan Kneschke  * if directory is deleted, directory mapping is removed
515e134da0SJan Kneschke  *
525e134da0SJan Kneschke  * */
535e134da0SJan Kneschke 
545e134da0SJan Kneschke /* the directory name is too long to always compare on it
555e134da0SJan Kneschke  * - we need a hash
565e134da0SJan Kneschke  * - the hash-key is used as sorting criteria for a tree
575e134da0SJan Kneschke  * - a splay-tree is used as we can use the caching effect of it
585e134da0SJan Kneschke  */
595e134da0SJan Kneschke 
605e134da0SJan Kneschke /* we want to cleanup the stat-cache every few seconds, let's say 10
615e134da0SJan Kneschke  *
625e134da0SJan Kneschke  * - remove entries which are outdated since 30s
635e134da0SJan Kneschke  * - remove entries which are fresh but havn't been used since 60s
645e134da0SJan Kneschke  * - if we don't have a stat-cache entry for a directory, release it from the monitor
655e134da0SJan Kneschke  */
665e134da0SJan Kneschke 
67*9287c87dSGlenn Strauss 
68*9287c87dSGlenn Strauss enum {
69*9287c87dSGlenn Strauss   STAT_CACHE_ENGINE_UNSET,
70*9287c87dSGlenn Strauss   STAT_CACHE_ENGINE_NONE,
71*9287c87dSGlenn Strauss   STAT_CACHE_ENGINE_SIMPLE,
72*9287c87dSGlenn Strauss   STAT_CACHE_ENGINE_FAM
73*9287c87dSGlenn Strauss };
74*9287c87dSGlenn Strauss 
75*9287c87dSGlenn Strauss #ifdef HAVE_FAM_H
76*9287c87dSGlenn Strauss struct stat_cache_fam;
77*9287c87dSGlenn Strauss #endif
78*9287c87dSGlenn Strauss 
79e1164797SGlenn Strauss typedef struct stat_cache {
80e1164797SGlenn Strauss 	splay_tree *files; /* the nodes of the tree are stat_cache_entry's */
81*9287c87dSGlenn Strauss 	buffer *hash_key;  /* temp-store for the hash-key */
82e1164797SGlenn Strauss       #ifdef HAVE_FAM_H
83*9287c87dSGlenn Strauss 	struct stat_cache_fam *scf;
84*9287c87dSGlenn Strauss       #endif
85*9287c87dSGlenn Strauss } stat_cache;
86*9287c87dSGlenn Strauss 
87*9287c87dSGlenn Strauss 
88*9287c87dSGlenn Strauss /* the famous DJB hash function for strings */
89*9287c87dSGlenn Strauss static uint32_t hashme(buffer *str) {
90*9287c87dSGlenn Strauss 	uint32_t hash = 5381;
91*9287c87dSGlenn Strauss 	const char *s;
92*9287c87dSGlenn Strauss 	for (s = str->ptr; *s; s++) {
93*9287c87dSGlenn Strauss 		hash = ((hash << 5) + hash) + *s;
94*9287c87dSGlenn Strauss 	}
95*9287c87dSGlenn Strauss 
96*9287c87dSGlenn Strauss 	hash &= ~(((uint32_t)1) << 31); /* strip the highest bit */
97*9287c87dSGlenn Strauss 
98*9287c87dSGlenn Strauss 	return hash;
99*9287c87dSGlenn Strauss }
100*9287c87dSGlenn Strauss 
101*9287c87dSGlenn Strauss 
102*9287c87dSGlenn Strauss #ifdef HAVE_FAM_H
103*9287c87dSGlenn Strauss 
104*9287c87dSGlenn Strauss #include <fam.h>
105*9287c87dSGlenn Strauss 
106*9287c87dSGlenn Strauss typedef struct {
107*9287c87dSGlenn Strauss 	FAMRequest *req;
108*9287c87dSGlenn Strauss 	buffer *name;
109*9287c87dSGlenn Strauss 	int version;
110*9287c87dSGlenn Strauss } fam_dir_entry;
111*9287c87dSGlenn Strauss 
112*9287c87dSGlenn Strauss typedef struct stat_cache_fam {
113e1164797SGlenn Strauss 	splay_tree *dirs; /* the nodes of the tree are fam_dir_entry */
114e1164797SGlenn Strauss 
115e1164797SGlenn Strauss 	FAMConnection fam;
116e1164797SGlenn Strauss 	int    fam_fcce_ndx;
117e1164797SGlenn Strauss 
118*9287c87dSGlenn Strauss 	int dir_ndx;
119*9287c87dSGlenn Strauss 	fam_dir_entry *fam_dir;
120*9287c87dSGlenn Strauss 	buffer *dir_name; /* for building the dirname from the filename */
121*9287c87dSGlenn Strauss 	buffer *hash_key;  /* temp-store for the hash-key */
122*9287c87dSGlenn Strauss } stat_cache_fam;
123*9287c87dSGlenn Strauss 
124*9287c87dSGlenn Strauss static fam_dir_entry * fam_dir_entry_init(void) {
125*9287c87dSGlenn Strauss 	fam_dir_entry *fam_dir = NULL;
126*9287c87dSGlenn Strauss 
127*9287c87dSGlenn Strauss 	fam_dir = calloc(1, sizeof(*fam_dir));
128*9287c87dSGlenn Strauss 	force_assert(NULL != fam_dir);
129*9287c87dSGlenn Strauss 
130*9287c87dSGlenn Strauss 	fam_dir->name = buffer_init();
131*9287c87dSGlenn Strauss 
132*9287c87dSGlenn Strauss 	return fam_dir;
133*9287c87dSGlenn Strauss }
134*9287c87dSGlenn Strauss 
135*9287c87dSGlenn Strauss static void fam_dir_entry_free(FAMConnection *fc, void *data) {
136*9287c87dSGlenn Strauss 	fam_dir_entry *fam_dir = data;
137*9287c87dSGlenn Strauss 
138*9287c87dSGlenn Strauss 	if (!fam_dir) return;
139*9287c87dSGlenn Strauss 
140*9287c87dSGlenn Strauss 	FAMCancelMonitor(fc, fam_dir->req);
141*9287c87dSGlenn Strauss 
142*9287c87dSGlenn Strauss 	buffer_free(fam_dir->name);
143*9287c87dSGlenn Strauss 	free(fam_dir->req);
144*9287c87dSGlenn Strauss 
145*9287c87dSGlenn Strauss 	free(fam_dir);
146*9287c87dSGlenn Strauss }
147*9287c87dSGlenn Strauss 
148*9287c87dSGlenn Strauss static handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) {
149*9287c87dSGlenn Strauss 	size_t i;
150*9287c87dSGlenn Strauss 	stat_cache_fam *scf = srv->stat_cache->scf;
151*9287c87dSGlenn Strauss 	size_t events;
152*9287c87dSGlenn Strauss 
153*9287c87dSGlenn Strauss 	UNUSED(_fce);
154*9287c87dSGlenn Strauss 	/* */
155*9287c87dSGlenn Strauss 
156*9287c87dSGlenn Strauss 	if (revent & FDEVENT_IN) {
157*9287c87dSGlenn Strauss 		events = FAMPending(&scf->fam);
158*9287c87dSGlenn Strauss 
159*9287c87dSGlenn Strauss 		for (i = 0; i < events; i++) {
160*9287c87dSGlenn Strauss 			FAMEvent fe;
161*9287c87dSGlenn Strauss 			fam_dir_entry *fam_dir;
162*9287c87dSGlenn Strauss 			splay_tree *node;
163*9287c87dSGlenn Strauss 			int ndx, j;
164*9287c87dSGlenn Strauss 
165*9287c87dSGlenn Strauss 			FAMNextEvent(&scf->fam, &fe);
166*9287c87dSGlenn Strauss 
167*9287c87dSGlenn Strauss 			/* handle event */
168*9287c87dSGlenn Strauss 
169*9287c87dSGlenn Strauss 			switch(fe.code) {
170*9287c87dSGlenn Strauss 			case FAMChanged:
171*9287c87dSGlenn Strauss 			case FAMDeleted:
172*9287c87dSGlenn Strauss 			case FAMMoved:
173*9287c87dSGlenn Strauss 				/* if the filename is a directory remove the entry */
174*9287c87dSGlenn Strauss 
175*9287c87dSGlenn Strauss 				fam_dir = fe.userdata;
176*9287c87dSGlenn Strauss 				fam_dir->version++;
177*9287c87dSGlenn Strauss 
178*9287c87dSGlenn Strauss 				/* file/dir is still here */
179*9287c87dSGlenn Strauss 				if (fe.code == FAMChanged) break;
180*9287c87dSGlenn Strauss 
181*9287c87dSGlenn Strauss 				/* we have 2 versions, follow and no-follow-symlink */
182*9287c87dSGlenn Strauss 
183*9287c87dSGlenn Strauss 				for (j = 0; j < 2; j++) {
184*9287c87dSGlenn Strauss 					buffer_copy_string(scf->hash_key, fe.filename);
185*9287c87dSGlenn Strauss 					buffer_append_int(scf->hash_key, j);
186*9287c87dSGlenn Strauss 
187*9287c87dSGlenn Strauss 					ndx = hashme(scf->hash_key);
188*9287c87dSGlenn Strauss 
189*9287c87dSGlenn Strauss 					scf->dirs = splaytree_splay(scf->dirs, ndx);
190*9287c87dSGlenn Strauss 					node = scf->dirs;
191*9287c87dSGlenn Strauss 
192*9287c87dSGlenn Strauss 					if (node && (node->key == ndx)) {
193*9287c87dSGlenn Strauss 						int osize = splaytree_size(scf->dirs);
194*9287c87dSGlenn Strauss 
195*9287c87dSGlenn Strauss 						fam_dir_entry_free(&scf->fam, node->data);
196*9287c87dSGlenn Strauss 						scf->dirs = splaytree_delete(scf->dirs, ndx);
197*9287c87dSGlenn Strauss 
198*9287c87dSGlenn Strauss 						force_assert(osize - 1 == splaytree_size(scf->dirs));
199*9287c87dSGlenn Strauss 					}
200*9287c87dSGlenn Strauss 				}
201*9287c87dSGlenn Strauss 				break;
202*9287c87dSGlenn Strauss 			default:
203*9287c87dSGlenn Strauss 				break;
204*9287c87dSGlenn Strauss 			}
205*9287c87dSGlenn Strauss 		}
206*9287c87dSGlenn Strauss 	}
207*9287c87dSGlenn Strauss 
208*9287c87dSGlenn Strauss 	if (revent & FDEVENT_HUP) {
209*9287c87dSGlenn Strauss 		/* fam closed the connection */
210*9287c87dSGlenn Strauss 		fdevent_event_del(srv->ev, &(scf->fam_fcce_ndx), FAMCONNECTION_GETFD(&scf->fam));
211*9287c87dSGlenn Strauss 		fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&scf->fam));
212*9287c87dSGlenn Strauss 
213*9287c87dSGlenn Strauss 		FAMClose(&scf->fam);
214*9287c87dSGlenn Strauss 	}
215*9287c87dSGlenn Strauss 
216*9287c87dSGlenn Strauss 	return HANDLER_GO_ON;
217*9287c87dSGlenn Strauss }
218*9287c87dSGlenn Strauss 
219*9287c87dSGlenn Strauss static stat_cache_fam * stat_cache_init_fam(server *srv) {
220*9287c87dSGlenn Strauss 	stat_cache_fam *scf = calloc(1, sizeof(*scf));
221*9287c87dSGlenn Strauss 	scf->fam_fcce_ndx = -1;
222*9287c87dSGlenn Strauss 	scf->dir_name = buffer_init();
223*9287c87dSGlenn Strauss 	scf->hash_key = buffer_init();
224*9287c87dSGlenn Strauss 
225*9287c87dSGlenn Strauss 	/* setup FAM */
226*9287c87dSGlenn Strauss 	if (0 != FAMOpen2(&scf->fam, "lighttpd")) {
227*9287c87dSGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "s",
228*9287c87dSGlenn Strauss 				"could not open a fam connection, dieing.");
229*9287c87dSGlenn Strauss 		return NULL;
230*9287c87dSGlenn Strauss 	}
231*9287c87dSGlenn Strauss       #ifdef HAVE_FAMNOEXISTS
232*9287c87dSGlenn Strauss 	FAMNoExists(&scf->fam);
233e8498bbfSGaël PORTAY       #endif
234e1164797SGlenn Strauss 
235*9287c87dSGlenn Strauss 	fdevent_setfd_cloexec(FAMCONNECTION_GETFD(&scf->fam));
236*9287c87dSGlenn Strauss 	fdevent_register(srv->ev, FAMCONNECTION_GETFD(&scf->fam), stat_cache_handle_fdevent, NULL);
237*9287c87dSGlenn Strauss 	fdevent_event_set(srv->ev, &(scf->fam_fcce_ndx), FAMCONNECTION_GETFD(&scf->fam), FDEVENT_IN);
238*9287c87dSGlenn Strauss 
239*9287c87dSGlenn Strauss 	return scf;
240*9287c87dSGlenn Strauss }
241*9287c87dSGlenn Strauss 
242*9287c87dSGlenn Strauss static void stat_cache_free_fam(stat_cache_fam *scf) {
243*9287c87dSGlenn Strauss 	if (NULL == scf) return;
244*9287c87dSGlenn Strauss 	buffer_free(scf->dir_name);
245*9287c87dSGlenn Strauss 	buffer_free(scf->hash_key);
246*9287c87dSGlenn Strauss 
247*9287c87dSGlenn Strauss 	while (scf->dirs) {
248*9287c87dSGlenn Strauss 		int osize;
249*9287c87dSGlenn Strauss 		splay_tree *node = scf->dirs;
250*9287c87dSGlenn Strauss 
251*9287c87dSGlenn Strauss 		osize = scf->dirs->size;
252*9287c87dSGlenn Strauss 
253*9287c87dSGlenn Strauss 		fam_dir_entry_free(&scf->fam, node->data);
254*9287c87dSGlenn Strauss 		scf->dirs = splaytree_delete(scf->dirs, node->key);
255*9287c87dSGlenn Strauss 
256*9287c87dSGlenn Strauss 		if (osize == 1) {
257*9287c87dSGlenn Strauss 			force_assert(NULL == scf->dirs);
258*9287c87dSGlenn Strauss 		} else {
259*9287c87dSGlenn Strauss 			force_assert(osize == (scf->dirs->size + 1));
260*9287c87dSGlenn Strauss 		}
261*9287c87dSGlenn Strauss 	}
262*9287c87dSGlenn Strauss 
263*9287c87dSGlenn Strauss 	if (-1 != scf->fam_fcce_ndx) {
264*9287c87dSGlenn Strauss 		/* fd events already gone */
265*9287c87dSGlenn Strauss 		scf->fam_fcce_ndx = -1;
266*9287c87dSGlenn Strauss 
267*9287c87dSGlenn Strauss 		FAMClose(&scf->fam);
268*9287c87dSGlenn Strauss 	}
269*9287c87dSGlenn Strauss 
270*9287c87dSGlenn Strauss 	free(scf);
271*9287c87dSGlenn Strauss }
272*9287c87dSGlenn Strauss 
273*9287c87dSGlenn Strauss static int buffer_copy_dirname(buffer *dst, const buffer *file) {
274*9287c87dSGlenn Strauss 	size_t i;
275*9287c87dSGlenn Strauss 
276*9287c87dSGlenn Strauss 	if (buffer_string_is_empty(file)) return -1;
277*9287c87dSGlenn Strauss 
278*9287c87dSGlenn Strauss 	for (i = buffer_string_length(file); i > 0; i--) {
279*9287c87dSGlenn Strauss 		if (file->ptr[i] == '/') {
280*9287c87dSGlenn Strauss 			buffer_copy_string_len(dst, file->ptr, i);
281*9287c87dSGlenn Strauss 			return 0;
282*9287c87dSGlenn Strauss 		}
283*9287c87dSGlenn Strauss 	}
284*9287c87dSGlenn Strauss 
285*9287c87dSGlenn Strauss 	return -1;
286*9287c87dSGlenn Strauss }
287*9287c87dSGlenn Strauss 
288*9287c87dSGlenn Strauss static handler_t stat_cache_fam_dir_check(server *srv, stat_cache_fam *scf, stat_cache_entry *sce, const buffer *name, unsigned int follow_symlink) {
289*9287c87dSGlenn Strauss 	if (0 != buffer_copy_dirname(scf->dir_name, name)) {
290*9287c87dSGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb",
291*9287c87dSGlenn Strauss 				"no '/' found in filename:", name);
292*9287c87dSGlenn Strauss 		return HANDLER_ERROR;
293*9287c87dSGlenn Strauss 	}
294*9287c87dSGlenn Strauss 
295*9287c87dSGlenn Strauss 	buffer_copy_buffer(scf->hash_key, scf->dir_name);
296*9287c87dSGlenn Strauss 	buffer_append_int(scf->hash_key, (int)follow_symlink);
297*9287c87dSGlenn Strauss 
298*9287c87dSGlenn Strauss 	scf->dir_ndx = hashme(scf->hash_key);
299*9287c87dSGlenn Strauss 
300*9287c87dSGlenn Strauss 	scf->dirs = splaytree_splay(scf->dirs, scf->dir_ndx);
301*9287c87dSGlenn Strauss 
302*9287c87dSGlenn Strauss 	if ((NULL != scf->dirs) && (scf->dirs->key == scf->dir_ndx)) {
303*9287c87dSGlenn Strauss 		scf->fam_dir = scf->dirs->data;
304*9287c87dSGlenn Strauss 
305*9287c87dSGlenn Strauss 		/* check whether we got a collision */
306*9287c87dSGlenn Strauss 		if (buffer_is_equal(scf->dir_name, scf->fam_dir->name)) {
307*9287c87dSGlenn Strauss 			/* test whether a found file cache entry is still ok */
308*9287c87dSGlenn Strauss 			if ((NULL != sce) && (scf->fam_dir->version == sce->dir_version)) {
309*9287c87dSGlenn Strauss 				/* the stat()-cache entry is still ok */
310*9287c87dSGlenn Strauss 				return HANDLER_FINISHED;
311*9287c87dSGlenn Strauss 			}
312*9287c87dSGlenn Strauss 		} else {
313*9287c87dSGlenn Strauss 			/* hash collision, forget about the entry */
314*9287c87dSGlenn Strauss 			scf->fam_dir = NULL;
315*9287c87dSGlenn Strauss 		}
316*9287c87dSGlenn Strauss 	} else {
317*9287c87dSGlenn Strauss 		scf->fam_dir = NULL;
318*9287c87dSGlenn Strauss 	}
319*9287c87dSGlenn Strauss 
320*9287c87dSGlenn Strauss 	return HANDLER_GO_ON;
321*9287c87dSGlenn Strauss }
322*9287c87dSGlenn Strauss 
323*9287c87dSGlenn Strauss static void stat_cache_fam_dir_monitor(server *srv, stat_cache_fam *scf, stat_cache_entry *sce, const buffer *name) {
324*9287c87dSGlenn Strauss 	/* is this directory already registered ? */
325*9287c87dSGlenn Strauss 	fam_dir_entry *fam_dir = scf->fam_dir;
326*9287c87dSGlenn Strauss 	if (NULL == fam_dir) {
327*9287c87dSGlenn Strauss 		fam_dir = fam_dir_entry_init();
328*9287c87dSGlenn Strauss 
329*9287c87dSGlenn Strauss 		buffer_copy_buffer(fam_dir->name, scf->dir_name);
330*9287c87dSGlenn Strauss 
331*9287c87dSGlenn Strauss 		fam_dir->version = 1;
332*9287c87dSGlenn Strauss 
333*9287c87dSGlenn Strauss 		fam_dir->req = calloc(1, sizeof(FAMRequest));
334*9287c87dSGlenn Strauss 		force_assert(NULL != fam_dir);
335*9287c87dSGlenn Strauss 
336*9287c87dSGlenn Strauss 		if (0 != FAMMonitorDirectory(&scf->fam, fam_dir->name->ptr,
337*9287c87dSGlenn Strauss 					     fam_dir->req, fam_dir)) {
338*9287c87dSGlenn Strauss 
339*9287c87dSGlenn Strauss 			log_error_write(srv, __FILE__, __LINE__, "sbsbs",
340*9287c87dSGlenn Strauss 					"monitoring dir failed:",
341*9287c87dSGlenn Strauss 					fam_dir->name,
342*9287c87dSGlenn Strauss 					"file:", name,
343*9287c87dSGlenn Strauss 					FamErrlist[FAMErrno]);
344*9287c87dSGlenn Strauss 
345*9287c87dSGlenn Strauss 			fam_dir_entry_free(&scf->fam, fam_dir);
346*9287c87dSGlenn Strauss 		} else {
347*9287c87dSGlenn Strauss 			int osize = splaytree_size(scf->dirs);
348*9287c87dSGlenn Strauss 
349*9287c87dSGlenn Strauss 			/* already splayed scf->dir_ndx */
350*9287c87dSGlenn Strauss 			if ((NULL != scf->dirs) && (scf->dirs->key == scf->dir_ndx)) {
351*9287c87dSGlenn Strauss 				/* hash collision: replace old entry */
352*9287c87dSGlenn Strauss 				fam_dir_entry_free(&scf->fam, scf->dirs->data);
353*9287c87dSGlenn Strauss 				scf->dirs->data = fam_dir;
354*9287c87dSGlenn Strauss 			} else {
355*9287c87dSGlenn Strauss 				scf->dirs = splaytree_insert(scf->dirs, scf->dir_ndx, fam_dir);
356*9287c87dSGlenn Strauss 				force_assert(osize == (splaytree_size(scf->dirs) - 1));
357*9287c87dSGlenn Strauss 			}
358*9287c87dSGlenn Strauss 
359*9287c87dSGlenn Strauss 			force_assert(scf->dirs);
360*9287c87dSGlenn Strauss 			force_assert(scf->dirs->data == fam_dir);
361*9287c87dSGlenn Strauss 			scf->fam_dir = fam_dir;
362*9287c87dSGlenn Strauss 		}
363*9287c87dSGlenn Strauss 	}
364*9287c87dSGlenn Strauss 
365*9287c87dSGlenn Strauss 	/* bind the fam_fc to the stat() cache entry */
366*9287c87dSGlenn Strauss 
367*9287c87dSGlenn Strauss 	if (fam_dir) {
368*9287c87dSGlenn Strauss 		sce->dir_version = fam_dir->version;
369*9287c87dSGlenn Strauss 	}
370*9287c87dSGlenn Strauss }
371*9287c87dSGlenn Strauss 
372*9287c87dSGlenn Strauss #endif
373*9287c87dSGlenn Strauss 
374*9287c87dSGlenn Strauss 
375e1164797SGlenn Strauss stat_cache *stat_cache_init(server *srv) {
376d8b363c1SStefan Bühler 	stat_cache *sc = NULL;
377e8498bbfSGaël PORTAY 	UNUSED(srv);
3785e134da0SJan Kneschke 
379d8b363c1SStefan Bühler 	sc = calloc(1, sizeof(*sc));
380566cf8deSStefan Bühler 	force_assert(NULL != sc);
3815e134da0SJan Kneschke 
382d8b363c1SStefan Bühler 	sc->hash_key = buffer_init();
383d8b363c1SStefan Bühler 
3845e134da0SJan Kneschke #ifdef HAVE_FAM_H
385*9287c87dSGlenn Strauss 	if (STAT_CACHE_ENGINE_FAM == srv->srvconf.stat_cache_engine) {
386*9287c87dSGlenn Strauss 		sc->scf = stat_cache_init_fam(srv);
387*9287c87dSGlenn Strauss 		if (NULL == sc->scf) {
3888e97c84cSGlenn Strauss 			free(sc);
389e1164797SGlenn Strauss 			return NULL;
390e1164797SGlenn Strauss 		}
391e1164797SGlenn Strauss 	}
392e1164797SGlenn Strauss #endif
393e1164797SGlenn Strauss 
394d8b363c1SStefan Bühler 	return sc;
3955e134da0SJan Kneschke }
3965e134da0SJan Kneschke 
3975e134da0SJan Kneschke static stat_cache_entry * stat_cache_entry_init(void) {
3985e134da0SJan Kneschke 	stat_cache_entry *sce = NULL;
3995e134da0SJan Kneschke 
4005e134da0SJan Kneschke 	sce = calloc(1, sizeof(*sce));
401566cf8deSStefan Bühler 	force_assert(NULL != sce);
4025e134da0SJan Kneschke 
4035e134da0SJan Kneschke 	sce->name = buffer_init();
4045e134da0SJan Kneschke 	sce->etag = buffer_init();
4055e134da0SJan Kneschke 	sce->content_type = buffer_init();
4065e134da0SJan Kneschke 
4075e134da0SJan Kneschke 	return sce;
4085e134da0SJan Kneschke }
4095e134da0SJan Kneschke 
4105e134da0SJan Kneschke static void stat_cache_entry_free(void *data) {
4115e134da0SJan Kneschke 	stat_cache_entry *sce = data;
4125e134da0SJan Kneschke 	if (!sce) return;
4135e134da0SJan Kneschke 
4145e134da0SJan Kneschke 	buffer_free(sce->etag);
4155e134da0SJan Kneschke 	buffer_free(sce->name);
4165e134da0SJan Kneschke 	buffer_free(sce->content_type);
4175e134da0SJan Kneschke 
4185e134da0SJan Kneschke 	free(sce);
4195e134da0SJan Kneschke }
4205e134da0SJan Kneschke 
421d1e70da8SJan Kneschke void stat_cache_free(stat_cache *sc) {
422d1e70da8SJan Kneschke 	while (sc->files) {
423ecb30c4eSJan Kneschke 		int osize;
424d1e70da8SJan Kneschke 		splay_tree *node = sc->files;
4255e134da0SJan Kneschke 
426ecb30c4eSJan Kneschke 		osize = sc->files->size;
427ecb30c4eSJan Kneschke 
428d1e70da8SJan Kneschke 		stat_cache_entry_free(node->data);
429d1e70da8SJan Kneschke 		sc->files = splaytree_delete(sc->files, node->key);
430d1e70da8SJan Kneschke 
43107dd0bd0SStefan Bühler 		force_assert(osize - 1 == splaytree_size(sc->files));
432d1e70da8SJan Kneschke 	}
433d1e70da8SJan Kneschke 
4349de38074SJan Kneschke 	buffer_free(sc->hash_key);
4355e134da0SJan Kneschke 
4365e134da0SJan Kneschke #ifdef HAVE_FAM_H
437*9287c87dSGlenn Strauss 	stat_cache_free_fam(sc->scf);
4385e134da0SJan Kneschke #endif
439d1e70da8SJan Kneschke 	free(sc);
4405e134da0SJan Kneschke }
4415e134da0SJan Kneschke 
442*9287c87dSGlenn Strauss int stat_cache_choose_engine (server *srv, const buffer *stat_cache_string) {
443*9287c87dSGlenn Strauss 	if (buffer_string_is_empty(stat_cache_string)) {
444*9287c87dSGlenn Strauss 		srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE;
445*9287c87dSGlenn Strauss 	} else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("simple"))) {
446*9287c87dSGlenn Strauss 		srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE;
447*9287c87dSGlenn Strauss #ifdef HAVE_FAM_H
448*9287c87dSGlenn Strauss 	} else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("fam"))) {
449*9287c87dSGlenn Strauss 		srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_FAM;
450*9287c87dSGlenn Strauss #endif
451*9287c87dSGlenn Strauss 	} else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("disable"))) {
452*9287c87dSGlenn Strauss 		srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_NONE;
453*9287c87dSGlenn Strauss 	} else {
454*9287c87dSGlenn Strauss 		log_error_write(srv, __FILE__, __LINE__, "sb",
455*9287c87dSGlenn Strauss 				"server.stat-cache-engine can be one of \"disable\", \"simple\","
456*9287c87dSGlenn Strauss #ifdef HAVE_FAM_H
457*9287c87dSGlenn Strauss 				" \"fam\","
458*9287c87dSGlenn Strauss #endif
459*9287c87dSGlenn Strauss 				" but not:", stat_cache_string);
460*9287c87dSGlenn Strauss 		return -1;
461*9287c87dSGlenn Strauss 	}
462*9287c87dSGlenn Strauss 	return 0;
463*9287c87dSGlenn Strauss }
464*9287c87dSGlenn Strauss 
4654d55d4adSMoritz Wilhelmy #if defined(HAVE_XATTR)
466d17d48e0SGlenn Strauss static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) {
4675e134da0SJan Kneschke 	int attrlen;
4685e134da0SJan Kneschke 	int ret;
4695e134da0SJan Kneschke 
4704365bdbeSStefan Bühler 	buffer_string_prepare_copy(buf, 1023);
4716afad87dSStefan Bühler 	attrlen = buf->size - 1;
472d17d48e0SGlenn Strauss 	if(0 == (ret = attr_get(name, xattrname, buf->ptr, &attrlen, 0))) {
473ad3e93eaSStefan Bühler 		buffer_commit(buf, attrlen);
4745e134da0SJan Kneschke 	}
4755e134da0SJan Kneschke 	return ret;
4765e134da0SJan Kneschke }
4774d55d4adSMoritz Wilhelmy #elif defined(HAVE_EXTATTR)
478d17d48e0SGlenn Strauss static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) {
4794365bdbeSStefan Bühler 	ssize_t attrlen;
4804d55d4adSMoritz Wilhelmy 
4819257d7dfSStefan Bühler 	buffer_string_prepare_copy(buf, 1023);
4824d55d4adSMoritz Wilhelmy 
483d17d48e0SGlenn Strauss 	if (-1 != (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, xattrname, buf->ptr, buf->size - 1))) {
4844d55d4adSMoritz Wilhelmy 		buf->used = attrlen + 1;
4854d55d4adSMoritz Wilhelmy 		buf->ptr[attrlen] = '\0';
4864d55d4adSMoritz Wilhelmy 		return 0;
4874d55d4adSMoritz Wilhelmy 	}
4884d55d4adSMoritz Wilhelmy 	return -1;
4894d55d4adSMoritz Wilhelmy }
4905e134da0SJan Kneschke #endif
4915e134da0SJan Kneschke 
4920cc7556aSGlenn Strauss const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen)
4930cc7556aSGlenn Strauss {
4940cc7556aSGlenn Strauss     const char *end = name + nlen; /*(end of string)*/
4950cc7556aSGlenn Strauss     const size_t used = con->conf.mimetypes->used;
4960cc7556aSGlenn Strauss     if (used < 16) {
4970cc7556aSGlenn Strauss         for (size_t i = 0; i < used; ++i) {
4980cc7556aSGlenn Strauss             /* suffix match */
4990cc7556aSGlenn Strauss             const data_string *ds = (data_string *)con->conf.mimetypes->data[i];
5000cc7556aSGlenn Strauss             const size_t klen = buffer_string_length(ds->key);
5010cc7556aSGlenn Strauss             if (klen <= nlen && 0 == strncasecmp(end-klen, ds->key->ptr, klen))
5020cc7556aSGlenn Strauss                 return ds->value;
5030cc7556aSGlenn Strauss         }
5040cc7556aSGlenn Strauss     }
5050cc7556aSGlenn Strauss     else {
5060cc7556aSGlenn Strauss         const char *s;
5070cc7556aSGlenn Strauss         const data_string *ds;
5080cc7556aSGlenn Strauss         if (nlen) {
5090cc7556aSGlenn Strauss             for (s = end-1; s != name && *s != '/'; --s) ; /*(like memrchr())*/
5100cc7556aSGlenn Strauss             if (*s == '/') ++s;
5110cc7556aSGlenn Strauss         }
5120cc7556aSGlenn Strauss         else {
5130cc7556aSGlenn Strauss             s = name;
5140cc7556aSGlenn Strauss         }
5150cc7556aSGlenn Strauss         /* search for basename, then longest .ext2.ext1, then .ext1, then "" */
51686bb8be2SGlenn Strauss         ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s);
5170cc7556aSGlenn Strauss         if (NULL != ds) return ds->value;
5180cc7556aSGlenn Strauss         while (++s < end) {
5190cc7556aSGlenn Strauss             while (*s != '.' && ++s != end) ;
5209e46b8eaSGlenn Strauss             if (s == end) break;
5219e46b8eaSGlenn Strauss             /* search ".ext" then "ext" */
52286bb8be2SGlenn Strauss             ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s);
5239e46b8eaSGlenn Strauss             if (NULL != ds) return ds->value;
5249e46b8eaSGlenn Strauss             /* repeat search without leading '.' to handle situation where
5259e46b8eaSGlenn Strauss              * admin configured mimetype.assign keys without leading '.' */
5269e46b8eaSGlenn Strauss             if (++s < end) {
5279e46b8eaSGlenn Strauss                 if (*s == '.') { --s; continue; }
52886bb8be2SGlenn Strauss                 ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s);
5290cc7556aSGlenn Strauss                 if (NULL != ds) return ds->value;
5300cc7556aSGlenn Strauss             }
5310cc7556aSGlenn Strauss         }
5329e46b8eaSGlenn Strauss         /* search for ""; catchall */
5339e46b8eaSGlenn Strauss         ds = (data_string *)array_get_element(con->conf.mimetypes, "");
5349e46b8eaSGlenn Strauss         if (NULL != ds) return ds->value;
5359e46b8eaSGlenn Strauss     }
5360cc7556aSGlenn Strauss 
5370cc7556aSGlenn Strauss     return NULL;
5380cc7556aSGlenn Strauss }
5390cc7556aSGlenn Strauss 
540657a024dSMarcus Rückert #ifdef HAVE_LSTAT
54108994e47SJan Kneschke static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) {
54208994e47SJan Kneschke 	if (lstat(dname->ptr, lst) == 0) {
543657a024dSMarcus Rückert 		return S_ISLNK(lst->st_mode) ? 0 : 1;
544657a024dSMarcus Rückert 	}
545657a024dSMarcus Rückert 	else {
54608994e47SJan Kneschke 		log_error_write(srv, __FILE__, __LINE__, "sbs",
547657a024dSMarcus Rückert 				"lstat failed for:",
548657a024dSMarcus Rückert 				dname, strerror(errno));
549657a024dSMarcus Rückert 	};
550657a024dSMarcus Rückert 	return -1;
551657a024dSMarcus Rückert }
552657a024dSMarcus Rückert #endif
553657a024dSMarcus Rückert 
5545e134da0SJan Kneschke /***
5555e134da0SJan Kneschke  *
5565e134da0SJan Kneschke  *
5575e134da0SJan Kneschke  *
5585e134da0SJan Kneschke  * returns:
5595e134da0SJan Kneschke  *  - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
5605e134da0SJan Kneschke  *  - HANDLER_ERROR on stat() failed -> see errno for problem
5615e134da0SJan Kneschke  */
5625e134da0SJan Kneschke 
5635e134da0SJan Kneschke handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
5645e134da0SJan Kneschke 	stat_cache_entry *sce = NULL;
5655e134da0SJan Kneschke 	stat_cache *sc;
5665e134da0SJan Kneschke 	struct stat st;
5677d875859SJan Kneschke 	int fd;
56808994e47SJan Kneschke 	struct stat lst;
5695e134da0SJan Kneschke 	int file_ndx;
5705e134da0SJan Kneschke 
5715e134da0SJan Kneschke 	*ret_sce = NULL;
5725e134da0SJan Kneschke 
5735e134da0SJan Kneschke 	/*
5745e134da0SJan Kneschke 	 * check if the directory for this file has changed
5755e134da0SJan Kneschke 	 */
5765e134da0SJan Kneschke 
5775e134da0SJan Kneschke 	sc = srv->stat_cache;
5785e134da0SJan Kneschke 
5796afad87dSStefan Bühler 	buffer_copy_buffer(sc->hash_key, name);
5806afad87dSStefan Bühler 	buffer_append_int(sc->hash_key, con->conf.follow_symlink);
5819de38074SJan Kneschke 
5829de38074SJan Kneschke 	file_ndx = hashme(sc->hash_key);
5835e134da0SJan Kneschke 	sc->files = splaytree_splay(sc->files, file_ndx);
5845e134da0SJan Kneschke 
5855e134da0SJan Kneschke 	if (sc->files && (sc->files->key == file_ndx)) {
5865e134da0SJan Kneschke 		/* we have seen this file already and
5875e134da0SJan Kneschke 		 * don't stat() it again in the same second */
5885e134da0SJan Kneschke 
58969f890e2SStefan Bühler 		sce = sc->files->data;
5905e134da0SJan Kneschke 
591ecb30c4eSJan Kneschke 		/* check if the name is the same, we might have a collision */
592ecb30c4eSJan Kneschke 
593ecb30c4eSJan Kneschke 		if (buffer_is_equal(name, sce->name)) {
5945e134da0SJan Kneschke 			if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
595acd5e450SGlenn Strauss 				if (sce->stat_ts == srv->cur_ts && con->conf.follow_symlink) {
5965e134da0SJan Kneschke 					*ret_sce = sce;
5975e134da0SJan Kneschke 					return HANDLER_GO_ON;
5985e134da0SJan Kneschke 				}
5995e134da0SJan Kneschke 			}
600ecb30c4eSJan Kneschke 		} else {
60169f890e2SStefan Bühler 			/* collision, forget about the entry */
60269f890e2SStefan Bühler 			sce = NULL;
603ecb30c4eSJan Kneschke 		}
6045e134da0SJan Kneschke 	}
6055e134da0SJan Kneschke 
6065e134da0SJan Kneschke #ifdef HAVE_FAM_H
6075e134da0SJan Kneschke 	/* dir-check */
6085e134da0SJan Kneschke 	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
609*9287c87dSGlenn Strauss 		switch (stat_cache_fam_dir_check(srv, sc->scf, sce, name, con->conf.follow_symlink)) {
610*9287c87dSGlenn Strauss 		case HANDLER_GO_ON:
611*9287c87dSGlenn Strauss 			break;
612*9287c87dSGlenn Strauss 		case HANDLER_FINISHED:
6135e134da0SJan Kneschke 			*ret_sce = sce;
6145e134da0SJan Kneschke 			return HANDLER_GO_ON;
615*9287c87dSGlenn Strauss 		case HANDLER_ERROR:
616*9287c87dSGlenn Strauss 		default:
617*9287c87dSGlenn Strauss 			return HANDLER_ERROR;
6185e134da0SJan Kneschke 		}
6195e134da0SJan Kneschke 	}
6205e134da0SJan Kneschke #endif
621478cb34bSJan Kneschke 
622478cb34bSJan Kneschke 	/*
623478cb34bSJan Kneschke 	 * *lol*
624478cb34bSJan Kneschke 	 * - open() + fstat() on a named-pipe results in a (intended) hang.
625657a024dSMarcus Rückert 	 * - stat() if regular file + open() to see if we can read from it is better
626478cb34bSJan Kneschke 	 *
627478cb34bSJan Kneschke 	 * */
628478cb34bSJan Kneschke 	if (-1 == stat(name->ptr, &st)) {
629478cb34bSJan Kneschke 		return HANDLER_ERROR;
630478cb34bSJan Kneschke 	}
631478cb34bSJan Kneschke 
632478cb34bSJan Kneschke 
633478cb34bSJan Kneschke 	if (S_ISREG(st.st_mode)) {
63457066345SStefan Bühler 		/* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
635ad3e93eaSStefan Bühler 		if (name->ptr[buffer_string_length(name) - 1] == '/') {
63657066345SStefan Bühler 			errno = ENOTDIR;
63757066345SStefan Bühler 			return HANDLER_ERROR;
63857066345SStefan Bühler 		}
63957066345SStefan Bühler 
640478cb34bSJan Kneschke 		/* try to open the file to check if we can read it */
641603d6aceSJan Kneschke 		if (-1 == (fd = open(name->ptr, O_RDONLY))) {
642603d6aceSJan Kneschke 			return HANDLER_ERROR;
643603d6aceSJan Kneschke 		}
644603d6aceSJan Kneschke 		close(fd);
645603d6aceSJan Kneschke 	}
646603d6aceSJan Kneschke 
6475e134da0SJan Kneschke 	if (NULL == sce) {
648ecb30c4eSJan Kneschke 
6495e134da0SJan Kneschke 		sce = stat_cache_entry_init();
6506afad87dSStefan Bühler 		buffer_copy_buffer(sce->name, name);
6515e134da0SJan Kneschke 
65269f890e2SStefan Bühler 		/* already splayed file_ndx */
65369f890e2SStefan Bühler 		if ((NULL != sc->files) && (sc->files->key == file_ndx)) {
65469f890e2SStefan Bühler 			/* hash collision: replace old entry */
65569f890e2SStefan Bühler 			stat_cache_entry_free(sc->files->data);
65669f890e2SStefan Bühler 			sc->files->data = sce;
65769f890e2SStefan Bühler 		} else {
65869f890e2SStefan Bühler 			int osize = splaytree_size(sc->files);
65969f890e2SStefan Bühler 
6605e134da0SJan Kneschke 			sc->files = splaytree_insert(sc->files, file_ndx, sce);
66169f890e2SStefan Bühler 			force_assert(osize + 1 == splaytree_size(sc->files));
66269f890e2SStefan Bühler 		}
66307dd0bd0SStefan Bühler 		force_assert(sc->files);
66407dd0bd0SStefan Bühler 		force_assert(sc->files->data == sce);
6655e134da0SJan Kneschke 	}
6665e134da0SJan Kneschke 
6675e134da0SJan Kneschke 	sce->st = st;
6685e134da0SJan Kneschke 	sce->stat_ts = srv->cur_ts;
6695e134da0SJan Kneschke 
6707d875859SJan Kneschke 	/* catch the obvious symlinks
6717d875859SJan Kneschke 	 *
6727d875859SJan Kneschke 	 * this is not a secure check as we still have a race-condition between
6737d875859SJan Kneschke 	 * the stat() and the open. We can only solve this by
6747d875859SJan Kneschke 	 * 1. open() the file
6757d875859SJan Kneschke 	 * 2. fstat() the fd
6767d875859SJan Kneschke 	 *
6777d875859SJan Kneschke 	 * and keeping the file open for the rest of the time. But this can
6787d875859SJan Kneschke 	 * only be done at network level.
6797d875859SJan Kneschke 	 *
68008994e47SJan Kneschke 	 * per default it is not a symlink
6817d875859SJan Kneschke 	 * */
682657a024dSMarcus Rückert #ifdef HAVE_LSTAT
683657a024dSMarcus Rückert 	sce->is_symlink = 0;
6849de38074SJan Kneschke 
6859de38074SJan Kneschke 	/* we want to only check for symlinks if we should block symlinks.
686a57bb820SMarcus Rückert 	 */
6879de38074SJan Kneschke 	if (!con->conf.follow_symlink) {
68808994e47SJan Kneschke 		if (stat_cache_lstat(srv, name, &lst)  == 0) {
689657a024dSMarcus Rückert 				sce->is_symlink = 1;
6907d875859SJan Kneschke 		}
6917d875859SJan Kneschke 
692657a024dSMarcus Rückert 		/*
693657a024dSMarcus Rückert 		 * we assume "/" can not be symlink, so
694657a024dSMarcus Rückert 		 * skip the symlink stuff if our path is /
695657a024dSMarcus Rückert 		 **/
696ad3e93eaSStefan Bühler 		else if (buffer_string_length(name) > 1) {
69708994e47SJan Kneschke 			buffer *dname;
69808994e47SJan Kneschke 			char *s_cur;
699657a024dSMarcus Rückert 
70008994e47SJan Kneschke 			dname = buffer_init();
7016afad87dSStefan Bühler 			buffer_copy_buffer(dname, name);
70208994e47SJan Kneschke 
70308994e47SJan Kneschke 			while ((s_cur = strrchr(dname->ptr, '/'))) {
704ad3e93eaSStefan Bühler 				buffer_string_set_length(dname, s_cur - dname->ptr);
70508994e47SJan Kneschke 				if (dname->ptr == s_cur) {
706657a024dSMarcus Rückert 					break;
707657a024dSMarcus Rückert 				}
708657a024dSMarcus Rückert 				if (stat_cache_lstat(srv, dname, &lst)  == 0) {
709657a024dSMarcus Rückert 					sce->is_symlink = 1;
710657a024dSMarcus Rückert 					break;
711657a024dSMarcus Rückert 				};
712657a024dSMarcus Rückert 			};
71308994e47SJan Kneschke 			buffer_free(dname);
714657a024dSMarcus Rückert 		};
71505919041SMarcus Rückert 	};
716657a024dSMarcus Rückert #endif
717657a024dSMarcus Rückert 
7185e134da0SJan Kneschke 	if (S_ISREG(st.st_mode)) {
7195e134da0SJan Kneschke 		/* determine mimetype */
7205e134da0SJan Kneschke 		buffer_reset(sce->content_type);
7214d55d4adSMoritz Wilhelmy #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR)
72207d9517dSStefan Bühler 		if (con->conf.use_xattr) {
723d17d48e0SGlenn Strauss 			stat_cache_attr_get(sce->content_type, name->ptr, srv->srvconf.xattr_name->ptr);
724b3027fefSStefan Bühler 		}
725b3027fefSStefan Bühler #endif
726b3027fefSStefan Bühler 		/* xattr did not set a content-type. ask the config */
7276afad87dSStefan Bühler 		if (buffer_string_is_empty(sce->content_type)) {
7280cc7556aSGlenn Strauss 			const buffer *type = stat_cache_mimetype_by_ext(con, CONST_BUF_LEN(name));
7290cc7556aSGlenn Strauss 			if (NULL != type) {
7300cc7556aSGlenn Strauss 				buffer_copy_buffer(sce->content_type, type);
7315e134da0SJan Kneschke 			}
7325e134da0SJan Kneschke 		}
733b3027fefSStefan Bühler 		etag_create(sce->etag, &(sce->st), con->etag_flags);
7347d4d8195SJan Kneschke 	} else if (S_ISDIR(st.st_mode)) {
7355a583661SMarcus Rückert 		etag_create(sce->etag, &(sce->st), con->etag_flags);
7365e134da0SJan Kneschke 	}
7375e134da0SJan Kneschke 
7385e134da0SJan Kneschke #ifdef HAVE_FAM_H
739d8b363c1SStefan Bühler 	if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
740*9287c87dSGlenn Strauss 		stat_cache_fam_dir_monitor(srv, sc->scf, sce, name);
7415e134da0SJan Kneschke 	}
7425e134da0SJan Kneschke #endif
74351978868SJan Kneschke 
7445e134da0SJan Kneschke 	*ret_sce = sce;
7455e134da0SJan Kneschke 
7465e134da0SJan Kneschke 	return HANDLER_GO_ON;
7475e134da0SJan Kneschke }
7485e134da0SJan Kneschke 
749a65c57a5SGlenn Strauss int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st) {
750a65c57a5SGlenn Strauss 	/*(Note: O_NOFOLLOW affects only the final path segment, the target file,
751a65c57a5SGlenn Strauss 	 * not any intermediate symlinks along the path)*/
752a65c57a5SGlenn Strauss 	#ifndef O_BINARY
753a65c57a5SGlenn Strauss 	#define O_BINARY 0
754a65c57a5SGlenn Strauss 	#endif
755a65c57a5SGlenn Strauss 	#ifndef O_LARGEFILE
756a65c57a5SGlenn Strauss 	#define O_LARGEFILE 0
757a65c57a5SGlenn Strauss 	#endif
758a65c57a5SGlenn Strauss 	#ifndef O_NOCTTY
759a65c57a5SGlenn Strauss 	#define O_NOCTTY 0
760a65c57a5SGlenn Strauss 	#endif
761a65c57a5SGlenn Strauss 	#ifndef O_NONBLOCK
762a65c57a5SGlenn Strauss 	#define O_NONBLOCK 0
763a65c57a5SGlenn Strauss 	#endif
764a65c57a5SGlenn Strauss 	#ifndef O_NOFOLLOW
765a65c57a5SGlenn Strauss 	#define O_NOFOLLOW 0
766a65c57a5SGlenn Strauss 	#endif
767a65c57a5SGlenn Strauss 	const int oflags = O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK
768a65c57a5SGlenn Strauss 			 | (con->conf.follow_symlink ? 0 : O_NOFOLLOW);
769a65c57a5SGlenn Strauss 	const int fd = open(name->ptr, O_RDONLY | oflags);
770a65c57a5SGlenn Strauss 	if (fd >= 0) {
771a65c57a5SGlenn Strauss 		if (0 == fstat(fd, st)) {
772a65c57a5SGlenn Strauss 			return fd;
773a65c57a5SGlenn Strauss 		} else {
774a65c57a5SGlenn Strauss 			close(fd);
775a65c57a5SGlenn Strauss 		}
776a65c57a5SGlenn Strauss 	}
777a65c57a5SGlenn Strauss 	UNUSED(srv); /*(might log_error_write(srv, ...) in the future)*/
778a65c57a5SGlenn Strauss 	return -1;
779a65c57a5SGlenn Strauss }
780a65c57a5SGlenn Strauss 
7815e134da0SJan Kneschke /**
7825e134da0SJan Kneschke  * remove stat() from cache which havn't been stat()ed for
7835e134da0SJan Kneschke  * more than 10 seconds
7845e134da0SJan Kneschke  *
7855e134da0SJan Kneschke  *
7865e134da0SJan Kneschke  * walk though the stat-cache, collect the ids which are too old
7875e134da0SJan Kneschke  * and remove them in a second loop
7885e134da0SJan Kneschke  */
7895e134da0SJan Kneschke 
7905e134da0SJan Kneschke static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) {
7915e134da0SJan Kneschke 	stat_cache_entry *sce;
7925e134da0SJan Kneschke 
7935e134da0SJan Kneschke 	if (!t) return 0;
7945e134da0SJan Kneschke 
7955e134da0SJan Kneschke 	stat_cache_tag_old_entries(srv, t->left, keys, ndx);
7965e134da0SJan Kneschke 	stat_cache_tag_old_entries(srv, t->right, keys, ndx);
7975e134da0SJan Kneschke 
7985e134da0SJan Kneschke 	sce = t->data;
7995e134da0SJan Kneschke 
800b795fd36SJan Kneschke 	if (srv->cur_ts - sce->stat_ts > 2) {
8015e134da0SJan Kneschke 		keys[(*ndx)++] = t->key;
8025e134da0SJan Kneschke 	}
8035e134da0SJan Kneschke 
8045e134da0SJan Kneschke 	return 0;
8055e134da0SJan Kneschke }
8065e134da0SJan Kneschke 
8075e134da0SJan Kneschke int stat_cache_trigger_cleanup(server *srv) {
8085e134da0SJan Kneschke 	stat_cache *sc;
80956f637b8SJan Kneschke 	size_t max_ndx = 0, i;
8105e134da0SJan Kneschke 	int *keys;
8115e134da0SJan Kneschke 
8125e134da0SJan Kneschke 	sc = srv->stat_cache;
8135e134da0SJan Kneschke 
8145e134da0SJan Kneschke 	if (!sc->files) return 0;
8155e134da0SJan Kneschke 
8166f208cfdSStefan Bühler 	keys = calloc(1, sizeof(int) * sc->files->size);
817566cf8deSStefan Bühler 	force_assert(NULL != keys);
8185e134da0SJan Kneschke 
8195e134da0SJan Kneschke 	stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
8205e134da0SJan Kneschke 
8215e134da0SJan Kneschke 	for (i = 0; i < max_ndx; i++) {
8225e134da0SJan Kneschke 		int ndx = keys[i];
8235e134da0SJan Kneschke 		splay_tree *node;
8245e134da0SJan Kneschke 
8255e134da0SJan Kneschke 		sc->files = splaytree_splay(sc->files, ndx);
8265e134da0SJan Kneschke 
8275e134da0SJan Kneschke 		node = sc->files;
8285e134da0SJan Kneschke 
8295e134da0SJan Kneschke 		if (node && (node->key == ndx)) {
8305e134da0SJan Kneschke 			stat_cache_entry_free(node->data);
8315e134da0SJan Kneschke 			sc->files = splaytree_delete(sc->files, ndx);
832b795fd36SJan Kneschke 		}
8335e134da0SJan Kneschke 	}
8345e134da0SJan Kneschke 
8355e134da0SJan Kneschke 	free(keys);
8365e134da0SJan Kneschke 
8375e134da0SJan Kneschke 	return 0;
8385e134da0SJan Kneschke }
839