1*76404edcSAsim Jamshed #include "log.h"
2*76404edcSAsim Jamshed #include "stat_cache.h"
3*76404edcSAsim Jamshed #include "fdevent.h"
4*76404edcSAsim Jamshed #include "etag.h"
5*76404edcSAsim Jamshed
6*76404edcSAsim Jamshed #include <sys/types.h>
7*76404edcSAsim Jamshed #include <sys/stat.h>
8*76404edcSAsim Jamshed
9*76404edcSAsim Jamshed #include <stdlib.h>
10*76404edcSAsim Jamshed #include <string.h>
11*76404edcSAsim Jamshed #include <errno.h>
12*76404edcSAsim Jamshed #include <unistd.h>
13*76404edcSAsim Jamshed #include <stdio.h>
14*76404edcSAsim Jamshed #include <fcntl.h>
15*76404edcSAsim Jamshed #include <assert.h>
16*76404edcSAsim Jamshed
17*76404edcSAsim Jamshed #ifdef HAVE_ATTR_ATTRIBUTES_H
18*76404edcSAsim Jamshed # include <attr/attributes.h>
19*76404edcSAsim Jamshed #endif
20*76404edcSAsim Jamshed
21*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
22*76404edcSAsim Jamshed # include <fam.h>
23*76404edcSAsim Jamshed #endif
24*76404edcSAsim Jamshed
25*76404edcSAsim Jamshed #include "sys-mmap.h"
26*76404edcSAsim Jamshed
27*76404edcSAsim Jamshed /* NetBSD 1.3.x needs it */
28*76404edcSAsim Jamshed #ifndef MAP_FAILED
29*76404edcSAsim Jamshed # define MAP_FAILED -1
30*76404edcSAsim Jamshed #endif
31*76404edcSAsim Jamshed
32*76404edcSAsim Jamshed #ifndef O_LARGEFILE
33*76404edcSAsim Jamshed # define O_LARGEFILE 0
34*76404edcSAsim Jamshed #endif
35*76404edcSAsim Jamshed
36*76404edcSAsim Jamshed #ifndef HAVE_LSTAT
37*76404edcSAsim Jamshed # define lstat stat
38*76404edcSAsim Jamshed #endif
39*76404edcSAsim Jamshed
40*76404edcSAsim Jamshed #if 0
41*76404edcSAsim Jamshed /* enables debug code for testing if all nodes in the stat-cache as accessable */
42*76404edcSAsim Jamshed #define DEBUG_STAT_CACHE
43*76404edcSAsim Jamshed #endif
44*76404edcSAsim Jamshed
45*76404edcSAsim Jamshed /*
46*76404edcSAsim Jamshed * stat-cache
47*76404edcSAsim Jamshed *
48*76404edcSAsim Jamshed * we cache the stat() calls in our own storage
49*76404edcSAsim Jamshed * the directories are cached in FAM
50*76404edcSAsim Jamshed *
51*76404edcSAsim Jamshed * if we get a change-event from FAM, we increment the version in the FAM->dir mapping
52*76404edcSAsim Jamshed *
53*76404edcSAsim Jamshed * if the stat()-cache is queried we check if the version id for the directory is the
54*76404edcSAsim Jamshed * same and return immediatly.
55*76404edcSAsim Jamshed *
56*76404edcSAsim Jamshed *
57*76404edcSAsim Jamshed * What we need:
58*76404edcSAsim Jamshed *
59*76404edcSAsim Jamshed * - for each stat-cache entry we need a fast indirect lookup on the directory name
60*76404edcSAsim Jamshed * - for each FAMRequest we have to find the version in the directory cache (index as userdata)
61*76404edcSAsim Jamshed *
62*76404edcSAsim Jamshed * stat <<-> directory <-> FAMRequest
63*76404edcSAsim Jamshed *
64*76404edcSAsim Jamshed * if file is deleted, directory is dirty, file is rechecked ...
65*76404edcSAsim Jamshed * if directory is deleted, directory mapping is removed
66*76404edcSAsim Jamshed *
67*76404edcSAsim Jamshed * */
68*76404edcSAsim Jamshed
69*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
70*76404edcSAsim Jamshed typedef struct {
71*76404edcSAsim Jamshed FAMRequest *req;
72*76404edcSAsim Jamshed FAMConnection *fc;
73*76404edcSAsim Jamshed
74*76404edcSAsim Jamshed buffer *name;
75*76404edcSAsim Jamshed
76*76404edcSAsim Jamshed int version;
77*76404edcSAsim Jamshed } fam_dir_entry;
78*76404edcSAsim Jamshed #endif
79*76404edcSAsim Jamshed
80*76404edcSAsim Jamshed /* the directory name is too long to always compare on it
81*76404edcSAsim Jamshed * - we need a hash
82*76404edcSAsim Jamshed * - the hash-key is used as sorting criteria for a tree
83*76404edcSAsim Jamshed * - a splay-tree is used as we can use the caching effect of it
84*76404edcSAsim Jamshed */
85*76404edcSAsim Jamshed
86*76404edcSAsim Jamshed /* we want to cleanup the stat-cache every few seconds, let's say 10
87*76404edcSAsim Jamshed *
88*76404edcSAsim Jamshed * - remove entries which are outdated since 30s
89*76404edcSAsim Jamshed * - remove entries which are fresh but havn't been used since 60s
90*76404edcSAsim Jamshed * - if we don't have a stat-cache entry for a directory, release it from the monitor
91*76404edcSAsim Jamshed */
92*76404edcSAsim Jamshed
93*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
94*76404edcSAsim Jamshed typedef struct {
95*76404edcSAsim Jamshed int *ptr;
96*76404edcSAsim Jamshed
97*76404edcSAsim Jamshed size_t used;
98*76404edcSAsim Jamshed size_t size;
99*76404edcSAsim Jamshed } fake_keys;
100*76404edcSAsim Jamshed
101*76404edcSAsim Jamshed static fake_keys ctrl;
102*76404edcSAsim Jamshed #endif
103*76404edcSAsim Jamshed
stat_cache_init(void)104*76404edcSAsim Jamshed stat_cache *stat_cache_init(void) {
105*76404edcSAsim Jamshed stat_cache *fc = NULL;
106*76404edcSAsim Jamshed
107*76404edcSAsim Jamshed fc = calloc(1, sizeof(*fc));
108*76404edcSAsim Jamshed
109*76404edcSAsim Jamshed fc->dir_name = buffer_init();
110*76404edcSAsim Jamshed fc->hash_key = buffer_init();
111*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
112*76404edcSAsim Jamshed fc->fam = calloc(1, sizeof(*fc->fam));
113*76404edcSAsim Jamshed #endif
114*76404edcSAsim Jamshed
115*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
116*76404edcSAsim Jamshed ctrl.size = 0;
117*76404edcSAsim Jamshed #endif
118*76404edcSAsim Jamshed
119*76404edcSAsim Jamshed return fc;
120*76404edcSAsim Jamshed }
121*76404edcSAsim Jamshed
stat_cache_entry_init(void)122*76404edcSAsim Jamshed static stat_cache_entry * stat_cache_entry_init(void) {
123*76404edcSAsim Jamshed stat_cache_entry *sce = NULL;
124*76404edcSAsim Jamshed
125*76404edcSAsim Jamshed sce = calloc(1, sizeof(*sce));
126*76404edcSAsim Jamshed
127*76404edcSAsim Jamshed sce->name = buffer_init();
128*76404edcSAsim Jamshed sce->etag = buffer_init();
129*76404edcSAsim Jamshed sce->content_type = buffer_init();
130*76404edcSAsim Jamshed
131*76404edcSAsim Jamshed return sce;
132*76404edcSAsim Jamshed }
133*76404edcSAsim Jamshed
stat_cache_entry_free(void * data)134*76404edcSAsim Jamshed static void stat_cache_entry_free(void *data) {
135*76404edcSAsim Jamshed stat_cache_entry *sce = data;
136*76404edcSAsim Jamshed if (!sce) return;
137*76404edcSAsim Jamshed
138*76404edcSAsim Jamshed buffer_free(sce->etag);
139*76404edcSAsim Jamshed buffer_free(sce->name);
140*76404edcSAsim Jamshed buffer_free(sce->content_type);
141*76404edcSAsim Jamshed
142*76404edcSAsim Jamshed free(sce);
143*76404edcSAsim Jamshed }
144*76404edcSAsim Jamshed
145*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
fam_dir_entry_init(void)146*76404edcSAsim Jamshed static fam_dir_entry * fam_dir_entry_init(void) {
147*76404edcSAsim Jamshed fam_dir_entry *fam_dir = NULL;
148*76404edcSAsim Jamshed
149*76404edcSAsim Jamshed fam_dir = calloc(1, sizeof(*fam_dir));
150*76404edcSAsim Jamshed
151*76404edcSAsim Jamshed fam_dir->name = buffer_init();
152*76404edcSAsim Jamshed
153*76404edcSAsim Jamshed return fam_dir;
154*76404edcSAsim Jamshed }
155*76404edcSAsim Jamshed
fam_dir_entry_free(void * data)156*76404edcSAsim Jamshed static void fam_dir_entry_free(void *data) {
157*76404edcSAsim Jamshed fam_dir_entry *fam_dir = data;
158*76404edcSAsim Jamshed
159*76404edcSAsim Jamshed if (!fam_dir) return;
160*76404edcSAsim Jamshed
161*76404edcSAsim Jamshed FAMCancelMonitor(fam_dir->fc, fam_dir->req);
162*76404edcSAsim Jamshed
163*76404edcSAsim Jamshed buffer_free(fam_dir->name);
164*76404edcSAsim Jamshed free(fam_dir->req);
165*76404edcSAsim Jamshed
166*76404edcSAsim Jamshed free(fam_dir);
167*76404edcSAsim Jamshed }
168*76404edcSAsim Jamshed #endif
169*76404edcSAsim Jamshed
stat_cache_free(stat_cache * sc)170*76404edcSAsim Jamshed void stat_cache_free(stat_cache *sc) {
171*76404edcSAsim Jamshed while (sc->files) {
172*76404edcSAsim Jamshed int osize;
173*76404edcSAsim Jamshed splay_tree *node = sc->files;
174*76404edcSAsim Jamshed
175*76404edcSAsim Jamshed osize = sc->files->size;
176*76404edcSAsim Jamshed
177*76404edcSAsim Jamshed stat_cache_entry_free(node->data);
178*76404edcSAsim Jamshed sc->files = splaytree_delete(sc->files, node->key);
179*76404edcSAsim Jamshed
180*76404edcSAsim Jamshed assert(osize - 1 == splaytree_size(sc->files));
181*76404edcSAsim Jamshed }
182*76404edcSAsim Jamshed
183*76404edcSAsim Jamshed buffer_free(sc->dir_name);
184*76404edcSAsim Jamshed buffer_free(sc->hash_key);
185*76404edcSAsim Jamshed
186*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
187*76404edcSAsim Jamshed while (sc->dirs) {
188*76404edcSAsim Jamshed int osize;
189*76404edcSAsim Jamshed splay_tree *node = sc->dirs;
190*76404edcSAsim Jamshed
191*76404edcSAsim Jamshed osize = sc->dirs->size;
192*76404edcSAsim Jamshed
193*76404edcSAsim Jamshed fam_dir_entry_free(node->data);
194*76404edcSAsim Jamshed sc->dirs = splaytree_delete(sc->dirs, node->key);
195*76404edcSAsim Jamshed
196*76404edcSAsim Jamshed if (osize == 1) {
197*76404edcSAsim Jamshed assert(NULL == sc->dirs);
198*76404edcSAsim Jamshed } else {
199*76404edcSAsim Jamshed assert(osize == (sc->dirs->size + 1));
200*76404edcSAsim Jamshed }
201*76404edcSAsim Jamshed }
202*76404edcSAsim Jamshed
203*76404edcSAsim Jamshed if (sc->fam) {
204*76404edcSAsim Jamshed FAMClose(sc->fam);
205*76404edcSAsim Jamshed free(sc->fam);
206*76404edcSAsim Jamshed }
207*76404edcSAsim Jamshed #endif
208*76404edcSAsim Jamshed free(sc);
209*76404edcSAsim Jamshed }
210*76404edcSAsim Jamshed
211*76404edcSAsim Jamshed #ifdef HAVE_XATTR
stat_cache_attr_get(buffer * buf,char * name)212*76404edcSAsim Jamshed static int stat_cache_attr_get(buffer *buf, char *name) {
213*76404edcSAsim Jamshed int attrlen;
214*76404edcSAsim Jamshed int ret;
215*76404edcSAsim Jamshed
216*76404edcSAsim Jamshed attrlen = 1024;
217*76404edcSAsim Jamshed buffer_prepare_copy(buf, attrlen);
218*76404edcSAsim Jamshed attrlen--;
219*76404edcSAsim Jamshed if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) {
220*76404edcSAsim Jamshed buf->used = attrlen + 1;
221*76404edcSAsim Jamshed buf->ptr[attrlen] = '\0';
222*76404edcSAsim Jamshed }
223*76404edcSAsim Jamshed return ret;
224*76404edcSAsim Jamshed }
225*76404edcSAsim Jamshed #endif
226*76404edcSAsim Jamshed
227*76404edcSAsim Jamshed /* the famous DJB hash function for strings */
hashme(buffer * str)228*76404edcSAsim Jamshed static uint32_t hashme(buffer *str) {
229*76404edcSAsim Jamshed uint32_t hash = 5381;
230*76404edcSAsim Jamshed const char *s;
231*76404edcSAsim Jamshed for (s = str->ptr; *s; s++) {
232*76404edcSAsim Jamshed hash = ((hash << 5) + hash) + *s;
233*76404edcSAsim Jamshed }
234*76404edcSAsim Jamshed
235*76404edcSAsim Jamshed hash &= ~(1 << 31); /* strip the highest bit */
236*76404edcSAsim Jamshed
237*76404edcSAsim Jamshed return hash;
238*76404edcSAsim Jamshed }
239*76404edcSAsim Jamshed
240*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
stat_cache_handle_fdevent(server * srv,void * _fce,int revent)241*76404edcSAsim Jamshed handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) {
242*76404edcSAsim Jamshed size_t i;
243*76404edcSAsim Jamshed stat_cache *sc = srv->stat_cache;
244*76404edcSAsim Jamshed size_t events;
245*76404edcSAsim Jamshed
246*76404edcSAsim Jamshed UNUSED(_fce);
247*76404edcSAsim Jamshed /* */
248*76404edcSAsim Jamshed
249*76404edcSAsim Jamshed if ((revent & FDEVENT_IN) &&
250*76404edcSAsim Jamshed sc->fam) {
251*76404edcSAsim Jamshed
252*76404edcSAsim Jamshed events = FAMPending(sc->fam);
253*76404edcSAsim Jamshed
254*76404edcSAsim Jamshed for (i = 0; i < events; i++) {
255*76404edcSAsim Jamshed FAMEvent fe;
256*76404edcSAsim Jamshed fam_dir_entry *fam_dir;
257*76404edcSAsim Jamshed splay_tree *node;
258*76404edcSAsim Jamshed int ndx, j;
259*76404edcSAsim Jamshed
260*76404edcSAsim Jamshed FAMNextEvent(sc->fam, &fe);
261*76404edcSAsim Jamshed
262*76404edcSAsim Jamshed /* handle event */
263*76404edcSAsim Jamshed
264*76404edcSAsim Jamshed switch(fe.code) {
265*76404edcSAsim Jamshed case FAMChanged:
266*76404edcSAsim Jamshed case FAMDeleted:
267*76404edcSAsim Jamshed case FAMMoved:
268*76404edcSAsim Jamshed /* if the filename is a directory remove the entry */
269*76404edcSAsim Jamshed
270*76404edcSAsim Jamshed fam_dir = fe.userdata;
271*76404edcSAsim Jamshed fam_dir->version++;
272*76404edcSAsim Jamshed
273*76404edcSAsim Jamshed /* file/dir is still here */
274*76404edcSAsim Jamshed if (fe.code == FAMChanged) break;
275*76404edcSAsim Jamshed
276*76404edcSAsim Jamshed /* we have 2 versions, follow and no-follow-symlink */
277*76404edcSAsim Jamshed
278*76404edcSAsim Jamshed for (j = 0; j < 2; j++) {
279*76404edcSAsim Jamshed buffer_copy_string(sc->hash_key, fe.filename);
280*76404edcSAsim Jamshed buffer_append_long(sc->hash_key, j);
281*76404edcSAsim Jamshed
282*76404edcSAsim Jamshed ndx = hashme(sc->hash_key);
283*76404edcSAsim Jamshed
284*76404edcSAsim Jamshed sc->dirs = splaytree_splay(sc->dirs, ndx);
285*76404edcSAsim Jamshed node = sc->dirs;
286*76404edcSAsim Jamshed
287*76404edcSAsim Jamshed if (node && (node->key == ndx)) {
288*76404edcSAsim Jamshed int osize = splaytree_size(sc->dirs);
289*76404edcSAsim Jamshed
290*76404edcSAsim Jamshed fam_dir_entry_free(node->data);
291*76404edcSAsim Jamshed sc->dirs = splaytree_delete(sc->dirs, ndx);
292*76404edcSAsim Jamshed
293*76404edcSAsim Jamshed assert(osize - 1 == splaytree_size(sc->dirs));
294*76404edcSAsim Jamshed }
295*76404edcSAsim Jamshed }
296*76404edcSAsim Jamshed break;
297*76404edcSAsim Jamshed default:
298*76404edcSAsim Jamshed break;
299*76404edcSAsim Jamshed }
300*76404edcSAsim Jamshed }
301*76404edcSAsim Jamshed }
302*76404edcSAsim Jamshed
303*76404edcSAsim Jamshed if (revent & FDEVENT_HUP) {
304*76404edcSAsim Jamshed /* fam closed the connection */
305*76404edcSAsim Jamshed srv->stat_cache->fam_fcce_ndx = -1;
306*76404edcSAsim Jamshed
307*76404edcSAsim Jamshed fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(sc->fam));
308*76404edcSAsim Jamshed fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(sc->fam));
309*76404edcSAsim Jamshed
310*76404edcSAsim Jamshed FAMClose(sc->fam);
311*76404edcSAsim Jamshed free(sc->fam);
312*76404edcSAsim Jamshed
313*76404edcSAsim Jamshed sc->fam = NULL;
314*76404edcSAsim Jamshed }
315*76404edcSAsim Jamshed
316*76404edcSAsim Jamshed return HANDLER_GO_ON;
317*76404edcSAsim Jamshed }
318*76404edcSAsim Jamshed
buffer_copy_dirname(buffer * dst,buffer * file)319*76404edcSAsim Jamshed static int buffer_copy_dirname(buffer *dst, buffer *file) {
320*76404edcSAsim Jamshed size_t i;
321*76404edcSAsim Jamshed
322*76404edcSAsim Jamshed if (buffer_is_empty(file)) return -1;
323*76404edcSAsim Jamshed
324*76404edcSAsim Jamshed for (i = file->used - 1; i+1 > 0; i--) {
325*76404edcSAsim Jamshed if (file->ptr[i] == '/') {
326*76404edcSAsim Jamshed buffer_copy_string_len(dst, file->ptr, i);
327*76404edcSAsim Jamshed return 0;
328*76404edcSAsim Jamshed }
329*76404edcSAsim Jamshed }
330*76404edcSAsim Jamshed
331*76404edcSAsim Jamshed return -1;
332*76404edcSAsim Jamshed }
333*76404edcSAsim Jamshed #endif
334*76404edcSAsim Jamshed
335*76404edcSAsim Jamshed #ifdef HAVE_LSTAT
stat_cache_lstat(server * srv,buffer * dname,struct stat * lst)336*76404edcSAsim Jamshed static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) {
337*76404edcSAsim Jamshed if (lstat(dname->ptr, lst) == 0) {
338*76404edcSAsim Jamshed return S_ISLNK(lst->st_mode) ? 0 : 1;
339*76404edcSAsim Jamshed }
340*76404edcSAsim Jamshed else {
341*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbs",
342*76404edcSAsim Jamshed "lstat failed for:",
343*76404edcSAsim Jamshed dname, strerror(errno));
344*76404edcSAsim Jamshed };
345*76404edcSAsim Jamshed return -1;
346*76404edcSAsim Jamshed }
347*76404edcSAsim Jamshed #endif
348*76404edcSAsim Jamshed
349*76404edcSAsim Jamshed /***
350*76404edcSAsim Jamshed *
351*76404edcSAsim Jamshed *
352*76404edcSAsim Jamshed *
353*76404edcSAsim Jamshed * returns:
354*76404edcSAsim Jamshed * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file)
355*76404edcSAsim Jamshed * - HANDLER_ERROR on stat() failed -> see errno for problem
356*76404edcSAsim Jamshed */
357*76404edcSAsim Jamshed
stat_cache_get_entry(server * srv,connection * con,buffer * name,stat_cache_entry ** ret_sce)358*76404edcSAsim Jamshed handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) {
359*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
360*76404edcSAsim Jamshed fam_dir_entry *fam_dir = NULL;
361*76404edcSAsim Jamshed int dir_ndx = -1;
362*76404edcSAsim Jamshed splay_tree *dir_node = NULL;
363*76404edcSAsim Jamshed #endif
364*76404edcSAsim Jamshed stat_cache_entry *sce = NULL;
365*76404edcSAsim Jamshed stat_cache *sc;
366*76404edcSAsim Jamshed struct stat st;
367*76404edcSAsim Jamshed size_t k;
368*76404edcSAsim Jamshed int fd;
369*76404edcSAsim Jamshed struct stat lst;
370*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
371*76404edcSAsim Jamshed size_t i;
372*76404edcSAsim Jamshed #endif
373*76404edcSAsim Jamshed
374*76404edcSAsim Jamshed int file_ndx;
375*76404edcSAsim Jamshed splay_tree *file_node = NULL;
376*76404edcSAsim Jamshed
377*76404edcSAsim Jamshed *ret_sce = NULL;
378*76404edcSAsim Jamshed
379*76404edcSAsim Jamshed /*
380*76404edcSAsim Jamshed * check if the directory for this file has changed
381*76404edcSAsim Jamshed */
382*76404edcSAsim Jamshed
383*76404edcSAsim Jamshed sc = srv->stat_cache;
384*76404edcSAsim Jamshed
385*76404edcSAsim Jamshed buffer_copy_string_buffer(sc->hash_key, name);
386*76404edcSAsim Jamshed buffer_append_long(sc->hash_key, con->conf.follow_symlink);
387*76404edcSAsim Jamshed
388*76404edcSAsim Jamshed file_ndx = hashme(sc->hash_key);
389*76404edcSAsim Jamshed sc->files = splaytree_splay(sc->files, file_ndx);
390*76404edcSAsim Jamshed
391*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
392*76404edcSAsim Jamshed for (i = 0; i < ctrl.used; i++) {
393*76404edcSAsim Jamshed if (ctrl.ptr[i] == file_ndx) break;
394*76404edcSAsim Jamshed }
395*76404edcSAsim Jamshed #endif
396*76404edcSAsim Jamshed
397*76404edcSAsim Jamshed if (sc->files && (sc->files->key == file_ndx)) {
398*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
399*76404edcSAsim Jamshed /* it was in the cache */
400*76404edcSAsim Jamshed assert(i < ctrl.used);
401*76404edcSAsim Jamshed #endif
402*76404edcSAsim Jamshed
403*76404edcSAsim Jamshed /* we have seen this file already and
404*76404edcSAsim Jamshed * don't stat() it again in the same second */
405*76404edcSAsim Jamshed
406*76404edcSAsim Jamshed file_node = sc->files;
407*76404edcSAsim Jamshed
408*76404edcSAsim Jamshed sce = file_node->data;
409*76404edcSAsim Jamshed
410*76404edcSAsim Jamshed /* check if the name is the same, we might have a collision */
411*76404edcSAsim Jamshed
412*76404edcSAsim Jamshed if (buffer_is_equal(name, sce->name)) {
413*76404edcSAsim Jamshed if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) {
414*76404edcSAsim Jamshed if (sce->stat_ts == srv->cur_ts) {
415*76404edcSAsim Jamshed *ret_sce = sce;
416*76404edcSAsim Jamshed return HANDLER_GO_ON;
417*76404edcSAsim Jamshed }
418*76404edcSAsim Jamshed }
419*76404edcSAsim Jamshed } else {
420*76404edcSAsim Jamshed /* oops, a collision,
421*76404edcSAsim Jamshed *
422*76404edcSAsim Jamshed * file_node is used by the FAM check below to see if we know this file
423*76404edcSAsim Jamshed * and if we can save a stat().
424*76404edcSAsim Jamshed *
425*76404edcSAsim Jamshed * BUT, the sce is not reset here as the entry into the cache is ok, we
426*76404edcSAsim Jamshed * it is just not pointing to our requested file.
427*76404edcSAsim Jamshed *
428*76404edcSAsim Jamshed * */
429*76404edcSAsim Jamshed
430*76404edcSAsim Jamshed file_node = NULL;
431*76404edcSAsim Jamshed }
432*76404edcSAsim Jamshed } else {
433*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
434*76404edcSAsim Jamshed if (i != ctrl.used) {
435*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "xSB",
436*76404edcSAsim Jamshed file_ndx, "was already inserted but not found in cache, ", name);
437*76404edcSAsim Jamshed }
438*76404edcSAsim Jamshed assert(i == ctrl.used);
439*76404edcSAsim Jamshed #endif
440*76404edcSAsim Jamshed }
441*76404edcSAsim Jamshed
442*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
443*76404edcSAsim Jamshed /* dir-check */
444*76404edcSAsim Jamshed if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) {
445*76404edcSAsim Jamshed if (0 != buffer_copy_dirname(sc->dir_name, name)) {
446*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sb",
447*76404edcSAsim Jamshed "no '/' found in filename:", name);
448*76404edcSAsim Jamshed return HANDLER_ERROR;
449*76404edcSAsim Jamshed }
450*76404edcSAsim Jamshed
451*76404edcSAsim Jamshed buffer_copy_string_buffer(sc->hash_key, sc->dir_name);
452*76404edcSAsim Jamshed buffer_append_long(sc->hash_key, con->conf.follow_symlink);
453*76404edcSAsim Jamshed
454*76404edcSAsim Jamshed dir_ndx = hashme(sc->hash_key);
455*76404edcSAsim Jamshed
456*76404edcSAsim Jamshed sc->dirs = splaytree_splay(sc->dirs, dir_ndx);
457*76404edcSAsim Jamshed
458*76404edcSAsim Jamshed if (sc->dirs && (sc->dirs->key == dir_ndx)) {
459*76404edcSAsim Jamshed dir_node = sc->dirs;
460*76404edcSAsim Jamshed }
461*76404edcSAsim Jamshed
462*76404edcSAsim Jamshed if (dir_node && file_node) {
463*76404edcSAsim Jamshed /* we found a file */
464*76404edcSAsim Jamshed
465*76404edcSAsim Jamshed sce = file_node->data;
466*76404edcSAsim Jamshed fam_dir = dir_node->data;
467*76404edcSAsim Jamshed
468*76404edcSAsim Jamshed if (fam_dir->version == sce->dir_version) {
469*76404edcSAsim Jamshed /* the stat()-cache entry is still ok */
470*76404edcSAsim Jamshed
471*76404edcSAsim Jamshed *ret_sce = sce;
472*76404edcSAsim Jamshed return HANDLER_GO_ON;
473*76404edcSAsim Jamshed }
474*76404edcSAsim Jamshed }
475*76404edcSAsim Jamshed }
476*76404edcSAsim Jamshed #endif
477*76404edcSAsim Jamshed
478*76404edcSAsim Jamshed /*
479*76404edcSAsim Jamshed * *lol*
480*76404edcSAsim Jamshed * - open() + fstat() on a named-pipe results in a (intended) hang.
481*76404edcSAsim Jamshed * - stat() if regular file + open() to see if we can read from it is better
482*76404edcSAsim Jamshed *
483*76404edcSAsim Jamshed * */
484*76404edcSAsim Jamshed if (-1 == stat(name->ptr, &st)) {
485*76404edcSAsim Jamshed return HANDLER_ERROR;
486*76404edcSAsim Jamshed }
487*76404edcSAsim Jamshed
488*76404edcSAsim Jamshed
489*76404edcSAsim Jamshed if (S_ISREG(st.st_mode)) {
490*76404edcSAsim Jamshed /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */
491*76404edcSAsim Jamshed if (name->ptr[name->used-2] == '/') {
492*76404edcSAsim Jamshed errno = ENOTDIR;
493*76404edcSAsim Jamshed return HANDLER_ERROR;
494*76404edcSAsim Jamshed }
495*76404edcSAsim Jamshed
496*76404edcSAsim Jamshed /* try to open the file to check if we can read it */
497*76404edcSAsim Jamshed if (-1 == (fd = open(name->ptr, O_RDONLY))) {
498*76404edcSAsim Jamshed return HANDLER_ERROR;
499*76404edcSAsim Jamshed }
500*76404edcSAsim Jamshed close(fd);
501*76404edcSAsim Jamshed }
502*76404edcSAsim Jamshed
503*76404edcSAsim Jamshed if (NULL == sce) {
504*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
505*76404edcSAsim Jamshed int osize = splaytree_size(sc->files);
506*76404edcSAsim Jamshed #endif
507*76404edcSAsim Jamshed
508*76404edcSAsim Jamshed sce = stat_cache_entry_init();
509*76404edcSAsim Jamshed buffer_copy_string_buffer(sce->name, name);
510*76404edcSAsim Jamshed
511*76404edcSAsim Jamshed sc->files = splaytree_insert(sc->files, file_ndx, sce);
512*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
513*76404edcSAsim Jamshed if (ctrl.size == 0) {
514*76404edcSAsim Jamshed ctrl.size = 16;
515*76404edcSAsim Jamshed ctrl.used = 0;
516*76404edcSAsim Jamshed ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr));
517*76404edcSAsim Jamshed } else if (ctrl.size == ctrl.used) {
518*76404edcSAsim Jamshed ctrl.size += 16;
519*76404edcSAsim Jamshed ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr));
520*76404edcSAsim Jamshed }
521*76404edcSAsim Jamshed
522*76404edcSAsim Jamshed ctrl.ptr[ctrl.used++] = file_ndx;
523*76404edcSAsim Jamshed
524*76404edcSAsim Jamshed assert(sc->files);
525*76404edcSAsim Jamshed assert(sc->files->data == sce);
526*76404edcSAsim Jamshed assert(osize + 1 == splaytree_size(sc->files));
527*76404edcSAsim Jamshed #endif
528*76404edcSAsim Jamshed }
529*76404edcSAsim Jamshed
530*76404edcSAsim Jamshed sce->st = st;
531*76404edcSAsim Jamshed sce->stat_ts = srv->cur_ts;
532*76404edcSAsim Jamshed
533*76404edcSAsim Jamshed /* catch the obvious symlinks
534*76404edcSAsim Jamshed *
535*76404edcSAsim Jamshed * this is not a secure check as we still have a race-condition between
536*76404edcSAsim Jamshed * the stat() and the open. We can only solve this by
537*76404edcSAsim Jamshed * 1. open() the file
538*76404edcSAsim Jamshed * 2. fstat() the fd
539*76404edcSAsim Jamshed *
540*76404edcSAsim Jamshed * and keeping the file open for the rest of the time. But this can
541*76404edcSAsim Jamshed * only be done at network level.
542*76404edcSAsim Jamshed *
543*76404edcSAsim Jamshed * per default it is not a symlink
544*76404edcSAsim Jamshed * */
545*76404edcSAsim Jamshed #ifdef HAVE_LSTAT
546*76404edcSAsim Jamshed sce->is_symlink = 0;
547*76404edcSAsim Jamshed
548*76404edcSAsim Jamshed /* we want to only check for symlinks if we should block symlinks.
549*76404edcSAsim Jamshed */
550*76404edcSAsim Jamshed if (!con->conf.follow_symlink) {
551*76404edcSAsim Jamshed if (stat_cache_lstat(srv, name, &lst) == 0) {
552*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
553*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sb",
554*76404edcSAsim Jamshed "found symlink", name);
555*76404edcSAsim Jamshed #endif
556*76404edcSAsim Jamshed sce->is_symlink = 1;
557*76404edcSAsim Jamshed }
558*76404edcSAsim Jamshed
559*76404edcSAsim Jamshed /*
560*76404edcSAsim Jamshed * we assume "/" can not be symlink, so
561*76404edcSAsim Jamshed * skip the symlink stuff if our path is /
562*76404edcSAsim Jamshed **/
563*76404edcSAsim Jamshed else if ((name->used > 2)) {
564*76404edcSAsim Jamshed buffer *dname;
565*76404edcSAsim Jamshed char *s_cur;
566*76404edcSAsim Jamshed
567*76404edcSAsim Jamshed dname = buffer_init();
568*76404edcSAsim Jamshed buffer_copy_string_buffer(dname, name);
569*76404edcSAsim Jamshed
570*76404edcSAsim Jamshed while ((s_cur = strrchr(dname->ptr,'/'))) {
571*76404edcSAsim Jamshed *s_cur = '\0';
572*76404edcSAsim Jamshed dname->used = s_cur - dname->ptr + 1;
573*76404edcSAsim Jamshed if (dname->ptr == s_cur) {
574*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
575*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "s", "reached /");
576*76404edcSAsim Jamshed #endif
577*76404edcSAsim Jamshed break;
578*76404edcSAsim Jamshed }
579*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
580*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbs",
581*76404edcSAsim Jamshed "checking if", dname, "is a symlink");
582*76404edcSAsim Jamshed #endif
583*76404edcSAsim Jamshed if (stat_cache_lstat(srv, dname, &lst) == 0) {
584*76404edcSAsim Jamshed sce->is_symlink = 1;
585*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
586*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sb",
587*76404edcSAsim Jamshed "found symlink", dname);
588*76404edcSAsim Jamshed #endif
589*76404edcSAsim Jamshed break;
590*76404edcSAsim Jamshed };
591*76404edcSAsim Jamshed };
592*76404edcSAsim Jamshed buffer_free(dname);
593*76404edcSAsim Jamshed };
594*76404edcSAsim Jamshed };
595*76404edcSAsim Jamshed #endif
596*76404edcSAsim Jamshed
597*76404edcSAsim Jamshed if (S_ISREG(st.st_mode)) {
598*76404edcSAsim Jamshed /* determine mimetype */
599*76404edcSAsim Jamshed buffer_reset(sce->content_type);
600*76404edcSAsim Jamshed #ifdef HAVE_XATTR
601*76404edcSAsim Jamshed if (con->conf.use_xattr) {
602*76404edcSAsim Jamshed stat_cache_attr_get(sce->content_type, name->ptr);
603*76404edcSAsim Jamshed }
604*76404edcSAsim Jamshed #endif
605*76404edcSAsim Jamshed /* xattr did not set a content-type. ask the config */
606*76404edcSAsim Jamshed if (buffer_is_empty(sce->content_type)) {
607*76404edcSAsim Jamshed for (k = 0; k < con->conf.mimetypes->used; k++) {
608*76404edcSAsim Jamshed data_string *ds = (data_string *)con->conf.mimetypes->data[k];
609*76404edcSAsim Jamshed buffer *type = ds->key;
610*76404edcSAsim Jamshed
611*76404edcSAsim Jamshed if (type->used == 0) continue;
612*76404edcSAsim Jamshed
613*76404edcSAsim Jamshed /* check if the right side is the same */
614*76404edcSAsim Jamshed if (type->used > name->used) continue;
615*76404edcSAsim Jamshed
616*76404edcSAsim Jamshed if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) {
617*76404edcSAsim Jamshed buffer_copy_string_buffer(sce->content_type, ds->value);
618*76404edcSAsim Jamshed break;
619*76404edcSAsim Jamshed }
620*76404edcSAsim Jamshed }
621*76404edcSAsim Jamshed }
622*76404edcSAsim Jamshed etag_create(sce->etag, &(sce->st), con->etag_flags);
623*76404edcSAsim Jamshed } else if (S_ISDIR(st.st_mode)) {
624*76404edcSAsim Jamshed etag_create(sce->etag, &(sce->st), con->etag_flags);
625*76404edcSAsim Jamshed }
626*76404edcSAsim Jamshed
627*76404edcSAsim Jamshed #ifdef HAVE_FAM_H
628*76404edcSAsim Jamshed if (sc->fam &&
629*76404edcSAsim Jamshed (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM)) {
630*76404edcSAsim Jamshed /* is this directory already registered ? */
631*76404edcSAsim Jamshed if (!dir_node) {
632*76404edcSAsim Jamshed fam_dir = fam_dir_entry_init();
633*76404edcSAsim Jamshed fam_dir->fc = sc->fam;
634*76404edcSAsim Jamshed
635*76404edcSAsim Jamshed buffer_copy_string_buffer(fam_dir->name, sc->dir_name);
636*76404edcSAsim Jamshed
637*76404edcSAsim Jamshed fam_dir->version = 1;
638*76404edcSAsim Jamshed
639*76404edcSAsim Jamshed fam_dir->req = calloc(1, sizeof(FAMRequest));
640*76404edcSAsim Jamshed
641*76404edcSAsim Jamshed if (0 != FAMMonitorDirectory(sc->fam, fam_dir->name->ptr,
642*76404edcSAsim Jamshed fam_dir->req, fam_dir)) {
643*76404edcSAsim Jamshed
644*76404edcSAsim Jamshed log_error_write(srv, __FILE__, __LINE__, "sbsbs",
645*76404edcSAsim Jamshed "monitoring dir failed:",
646*76404edcSAsim Jamshed fam_dir->name,
647*76404edcSAsim Jamshed "file:", name,
648*76404edcSAsim Jamshed FamErrlist[FAMErrno]);
649*76404edcSAsim Jamshed
650*76404edcSAsim Jamshed fam_dir_entry_free(fam_dir);
651*76404edcSAsim Jamshed } else {
652*76404edcSAsim Jamshed int osize = 0;
653*76404edcSAsim Jamshed
654*76404edcSAsim Jamshed if (sc->dirs) {
655*76404edcSAsim Jamshed osize = sc->dirs->size;
656*76404edcSAsim Jamshed }
657*76404edcSAsim Jamshed
658*76404edcSAsim Jamshed sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir);
659*76404edcSAsim Jamshed assert(sc->dirs);
660*76404edcSAsim Jamshed assert(sc->dirs->data == fam_dir);
661*76404edcSAsim Jamshed assert(osize == (sc->dirs->size - 1));
662*76404edcSAsim Jamshed }
663*76404edcSAsim Jamshed } else {
664*76404edcSAsim Jamshed fam_dir = dir_node->data;
665*76404edcSAsim Jamshed }
666*76404edcSAsim Jamshed
667*76404edcSAsim Jamshed /* bind the fam_fc to the stat() cache entry */
668*76404edcSAsim Jamshed
669*76404edcSAsim Jamshed if (fam_dir) {
670*76404edcSAsim Jamshed sce->dir_version = fam_dir->version;
671*76404edcSAsim Jamshed sce->dir_ndx = dir_ndx;
672*76404edcSAsim Jamshed }
673*76404edcSAsim Jamshed }
674*76404edcSAsim Jamshed #endif
675*76404edcSAsim Jamshed
676*76404edcSAsim Jamshed *ret_sce = sce;
677*76404edcSAsim Jamshed
678*76404edcSAsim Jamshed return HANDLER_GO_ON;
679*76404edcSAsim Jamshed }
680*76404edcSAsim Jamshed
681*76404edcSAsim Jamshed /**
682*76404edcSAsim Jamshed * remove stat() from cache which havn't been stat()ed for
683*76404edcSAsim Jamshed * more than 10 seconds
684*76404edcSAsim Jamshed *
685*76404edcSAsim Jamshed *
686*76404edcSAsim Jamshed * walk though the stat-cache, collect the ids which are too old
687*76404edcSAsim Jamshed * and remove them in a second loop
688*76404edcSAsim Jamshed */
689*76404edcSAsim Jamshed
stat_cache_tag_old_entries(server * srv,splay_tree * t,int * keys,size_t * ndx)690*76404edcSAsim Jamshed static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) {
691*76404edcSAsim Jamshed stat_cache_entry *sce;
692*76404edcSAsim Jamshed
693*76404edcSAsim Jamshed if (!t) return 0;
694*76404edcSAsim Jamshed
695*76404edcSAsim Jamshed stat_cache_tag_old_entries(srv, t->left, keys, ndx);
696*76404edcSAsim Jamshed stat_cache_tag_old_entries(srv, t->right, keys, ndx);
697*76404edcSAsim Jamshed
698*76404edcSAsim Jamshed sce = t->data;
699*76404edcSAsim Jamshed
700*76404edcSAsim Jamshed if (srv->cur_ts - sce->stat_ts > 2) {
701*76404edcSAsim Jamshed keys[(*ndx)++] = t->key;
702*76404edcSAsim Jamshed }
703*76404edcSAsim Jamshed
704*76404edcSAsim Jamshed return 0;
705*76404edcSAsim Jamshed }
706*76404edcSAsim Jamshed
stat_cache_trigger_cleanup(server * srv)707*76404edcSAsim Jamshed int stat_cache_trigger_cleanup(server *srv) {
708*76404edcSAsim Jamshed stat_cache *sc;
709*76404edcSAsim Jamshed size_t max_ndx = 0, i;
710*76404edcSAsim Jamshed int *keys;
711*76404edcSAsim Jamshed
712*76404edcSAsim Jamshed sc = srv->stat_cache;
713*76404edcSAsim Jamshed
714*76404edcSAsim Jamshed if (!sc->files) return 0;
715*76404edcSAsim Jamshed
716*76404edcSAsim Jamshed keys = calloc(1, sizeof(size_t) * sc->files->size);
717*76404edcSAsim Jamshed
718*76404edcSAsim Jamshed stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx);
719*76404edcSAsim Jamshed
720*76404edcSAsim Jamshed for (i = 0; i < max_ndx; i++) {
721*76404edcSAsim Jamshed int ndx = keys[i];
722*76404edcSAsim Jamshed splay_tree *node;
723*76404edcSAsim Jamshed
724*76404edcSAsim Jamshed sc->files = splaytree_splay(sc->files, ndx);
725*76404edcSAsim Jamshed
726*76404edcSAsim Jamshed node = sc->files;
727*76404edcSAsim Jamshed
728*76404edcSAsim Jamshed if (node && (node->key == ndx)) {
729*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
730*76404edcSAsim Jamshed size_t j;
731*76404edcSAsim Jamshed int osize = splaytree_size(sc->files);
732*76404edcSAsim Jamshed stat_cache_entry *sce = node->data;
733*76404edcSAsim Jamshed #endif
734*76404edcSAsim Jamshed stat_cache_entry_free(node->data);
735*76404edcSAsim Jamshed sc->files = splaytree_delete(sc->files, ndx);
736*76404edcSAsim Jamshed
737*76404edcSAsim Jamshed #ifdef DEBUG_STAT_CACHE
738*76404edcSAsim Jamshed for (j = 0; j < ctrl.used; j++) {
739*76404edcSAsim Jamshed if (ctrl.ptr[j] == ndx) {
740*76404edcSAsim Jamshed ctrl.ptr[j] = ctrl.ptr[--ctrl.used];
741*76404edcSAsim Jamshed break;
742*76404edcSAsim Jamshed }
743*76404edcSAsim Jamshed }
744*76404edcSAsim Jamshed
745*76404edcSAsim Jamshed assert(osize - 1 == splaytree_size(sc->files));
746*76404edcSAsim Jamshed #endif
747*76404edcSAsim Jamshed }
748*76404edcSAsim Jamshed }
749*76404edcSAsim Jamshed
750*76404edcSAsim Jamshed free(keys);
751*76404edcSAsim Jamshed
752*76404edcSAsim Jamshed return 0;
753*76404edcSAsim Jamshed }
754