2 $Id: dircache.c,v 1.1.2.7 2010-02-11 13:06:54 franklahm Exp $
3 Copyright (c) 2010 Frank Lahm <franklahm@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
18 #endif /* HAVE_CONFIG_H */
26 #include <atalk/util.h>
27 #include <atalk/cnid.h>
28 #include <atalk/logger.h>
29 #include <atalk/volume.h>
30 #include <atalk/directory.h>
31 #include <atalk/queue.h>
32 #include <atalk/bstrlib.h>
33 #include <atalk/bstradd.h>
36 #include "directory.h"
41 * Dircache and indexes
42 * ====================
43 * The maximum dircache size is:
44 * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
45 * It is a hashtable which we use to store "struct dir"s in. If the cache get full, oldest
46 * entries are evicted in chunks of DIRCACHE_FREE.
47 * We have/need two indexes:
48 * - a DID/name index on the main dircache, another hashtable
49 * - a queue index on the dircache, for evicting the oldest entries
50 * The cache supports locking of struct dir elements through the DIRF_CACHELOCK flag. A dir
51 * locked this way wont ever be removed from the cache, so be careful.
53 * Sending SIGHUP to a afpd child causes it to dump the dircache to a file
54 * "/tmp/dircache.PID".
57 /********************************************************
58 * Local funcs and variables
59 ********************************************************/
61 /*****************************
64 static hash_t *dircache; /* The actual cache */
65 static unsigned int dircache_maxsize; /* cache maximum size */
68 static hash_val_t hash_vid_did(const void *key)
70 const struct dir *k = (const struct dir *)key;
71 hash_val_t hash = 2166136261;
73 hash ^= k->d_vid >> 8;
78 hash ^= k->d_did >> 24;
80 hash ^= (k->d_did >> 16) & 0xff;
82 hash ^= (k->d_did >> 8) & 0xff;
84 hash ^= (k->d_did >> 0) & 0xff;
90 static int hash_comp_vid_did(const void *key1, const void *key2)
92 const struct dir *k1 = key1;
93 const struct dir *k2 = key2;
95 return !(k1->d_did == k2->d_did && k1->d_vid == k2->d_vid);
98 /**************************************************
99 * DID/name index on dircache (another hashtable) */
101 static hash_t *index_didname;
104 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
105 || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
106 #define get16bits(d) (*((const uint16_t *) (d)))
109 #if !defined (get16bits)
110 #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
111 +(uint32_t)(((const uint8_t *)(d))[0]) )
114 static hash_val_t hash_didname(const void *p)
116 const struct dir *key = (const struct dir *)p;
117 const unsigned char *data = key->d_u_name->data;
118 int len = key->d_u_name->slen;
119 hash_val_t hash = key->d_pdid + key->d_vid;
126 for (;len > 0; len--) {
127 hash += get16bits (data);
128 tmp = (get16bits (data+2) << 11) ^ hash;
129 hash = (hash << 16) ^ tmp;
130 data += 2*sizeof (uint16_t);
134 /* Handle end cases */
136 case 3: hash += get16bits (data);
138 hash ^= data[sizeof (uint16_t)] << 18;
141 case 2: hash += get16bits (data);
145 case 1: hash += *data;
150 /* Force "avalanching" of final 127 bits */
161 static int hash_comp_didname(const void *k1, const void *k2)
163 const struct dir *key1 = (const struct dir *)k1;
164 const struct dir *key2 = (const struct dir *)k2;
166 return ! (key1->d_vid == key2->d_vid
167 && key1->d_pdid == key2->d_pdid
168 && (bstrcmp(key1->d_u_name, key2->d_u_name) == 0) );
171 /***************************
172 * queue index on dircache */
174 static q_t *index_queue; /* the index itself */
175 static unsigned int queue_count;
176 static const int dircache_free_quantum = 256; /* number of entries to free */
179 * @brief Remove a fixed number of (oldest) entries from the cache and indexes
181 * The default is to remove the 256 oldest entries from the cache.
182 * 1. Get the oldest entry
183 * 2. If it's in use ie open forks reference it or it's curdir requeue it,
184 * or it's locked (from catsearch) dont remove it
185 * 3. Remove the dir from the main cache and the didname index
186 * 4. Free the struct dir structure and all its members
188 static void dircache_evict(void)
190 int i = dircache_free_quantum;
193 LOG(log_debug, logtype_afpd, "dircache: {starting cache eviction}");
196 if ((dir = (struct dir *)dequeue(index_queue)) == NULL) { /* 1 */
204 || (dir->d_flags & DIRF_CACHELOCK)) { /* 2 */
205 if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
213 dircache_remove(NULL, dir, DIRCACHE | DIDNAME_INDEX); /* 3 */
214 dir_free(dir); /* 4 */
217 AFP_ASSERT(queue_count == dircache->hash_nodecount);
219 LOG(log_debug, logtype_afpd, "dircache: {finished cache eviction}");
223 /********************************************************
225 ********************************************************/
228 * @brief Search the dircache via a DID
230 * @param vol (r) pointer to struct vol
231 * @param did (r) CNID of the directory
233 * @returns Pointer to struct dir if found, else NULL
235 struct dir *dircache_search_by_did(const struct vol *vol, cnid_t did)
237 struct dir *cdir = NULL;
242 AFP_ASSERT(ntohl(did) >= CNID_START);
244 key.d_vid = vol->v_vid;
246 if ((hn = hash_lookup(dircache, &key)))
247 cdir = hnode_get(hn);
250 LOG(log_debug, logtype_afpd, "dircache(did:%u): {cached: path:'%s'}", ntohl(did), cfrombstring(cdir->d_fullpath));
252 LOG(log_debug, logtype_afpd, "dircache(did:%u): {not in cache}", ntohl(did));
258 * @brief Search the cache via did/name hashtable
260 * @param vol (r) volume
261 * @param dir (r) directory
262 * @param name (r) name (server side encoding)
263 * @parma len (r) strlen of name
265 * @returns pointer to struct dir if found in cache, else NULL
267 struct dir *dircache_search_by_name(const struct vol *vol, const struct dir *dir, char *name, int len)
269 struct dir *cdir = NULL;
272 static_bstring uname = {-1, len, (unsigned char *)name};
277 AFP_ASSERT(len == strlen(name));
278 AFP_ASSERT(len < 256);
280 if (dir->d_did != DIRDID_ROOT_PARENT) {
281 key.d_vid = vol->v_vid;
282 key.d_pdid = dir->d_did;
283 key.d_u_name = &uname;
285 if ((hn = hash_lookup(index_didname, &key)))
286 cdir = hnode_get(hn);
290 LOG(log_debug, logtype_afpd, "dircache(pdid:%u, did:%u, '%s'): {found in cache}",
291 ntohl(dir->d_did), ntohl(cdir->d_did), cfrombstring(cdir->d_fullpath));
293 LOG(log_debug, logtype_afpd, "dircache(pdid:%u,'%s/%s'): {not in cache}",
294 ntohl(dir->d_did), cfrombstring(dir->d_fullpath), name);
300 * @brief create struct dir from struct path
302 * Add a struct dir to the cache and its indexes.
304 * @param dir (r) pointer to parrent directory
306 * @returns 0 on success, -1 on error which should result in an abort
308 int dircache_add(struct dir *dir)
311 AFP_ASSERT(ntohl(dir->d_pdid) >= 2);
312 AFP_ASSERT(ntohl(dir->d_did) >= CNID_START);
313 AFP_ASSERT(dir->d_fullpath);
314 AFP_ASSERT(dir->d_u_name);
315 AFP_ASSERT(dir->d_vid);
316 AFP_ASSERT(dircache->hash_nodecount <= dircache_maxsize);
318 /* Check if cache is full */
319 if (dircache->hash_nodecount == dircache_maxsize)
322 /* Add it to the main dircache */
323 if (hash_alloc_insert(dircache, dir, dir) == 0) {
328 /* Add it to the did/name index */
329 if (hash_alloc_insert(index_didname, dir, dir) == 0) {
334 /* Add it to the fifo queue index */
335 if ((dir->qidx_node = enqueue(index_queue, dir)) == NULL) {
342 LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {added}", ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
344 AFP_ASSERT(queue_count == index_didname->hash_nodecount
345 && queue_count == dircache->hash_nodecount);
351 * @brief Remove an entry from the dircache
353 * Callers outside of dircache.c should call this with
354 * flags = QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE.
356 void dircache_remove(const struct vol *vol _U_, struct dir *dir, int flags)
361 AFP_ASSERT((flags & ~(QUEUE_INDEX | DIDNAME_INDEX | DIRCACHE)) == 0);
363 if (dir->d_flags & DIRF_CACHELOCK)
366 if (flags & QUEUE_INDEX) {
367 /* remove it from the queue index */
368 dequeue(dir->qidx_node->prev); /* this effectively deletes the dequeued node */
372 if (flags & DIDNAME_INDEX) {
373 if ((hn = hash_lookup(index_didname, dir)) == NULL) {
374 LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in didname index",
375 ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
379 hash_delete(index_didname, hn);
382 if (flags & DIRCACHE) {
383 if ((hn = hash_lookup(dircache, dir)) == NULL) {
384 LOG(log_error, logtype_default, "dircache_remove(%u,%s): not in dircache",
385 ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
389 hash_delete(dircache, hn);
392 LOG(log_debug, logtype_afpd, "dircache(did:%u,'%s'): {removed}", ntohl(dir->d_did), cfrombstring(dir->d_fullpath));
394 AFP_ASSERT(queue_count == index_didname->hash_nodecount
395 && queue_count == dircache->hash_nodecount);
399 * @brief Initialize the dircache and indexes
401 * This is called in child afpd initialisation. The maximum cache size will be
402 * max(DEFAULT_MAX_DIRCACHE_SIZE, min(size, MAX_POSSIBLE_DIRCACHE_SIZE)).
403 * It initializes a hashtable which we use to store a directory cache in.
404 * It also initializes two indexes:
405 * - a DID/name index on the main dircache
406 * - a queue index on the dircache
408 * @param size (r) requested maximum size from afpd.conf
410 * @return 0 on success, -1 on error
412 int dircache_init(int reqsize)
414 dircache_maxsize = DEFAULT_MAX_DIRCACHE_SIZE;
416 /* Initialize the main dircache */
417 if (reqsize > DEFAULT_MAX_DIRCACHE_SIZE && reqsize < MAX_POSSIBLE_DIRCACHE_SIZE) {
418 while ((dircache_maxsize < MAX_POSSIBLE_DIRCACHE_SIZE) && (dircache_maxsize < reqsize))
419 dircache_maxsize *= 2;
421 if ((dircache = hash_create(dircache_maxsize, hash_comp_vid_did, hash_vid_did)) == NULL)
424 LOG(log_debug, logtype_afpd, "dircache_init: done. max dircache size: %u", dircache_maxsize);
426 /* Initialize did/name index hashtable */
427 if ((index_didname = hash_create(dircache_maxsize, hash_comp_didname, hash_didname)) == NULL)
430 /* Initialize index queue */
431 if ((index_queue = queue_init()) == NULL)
436 /* As long as directory.c hasn't got its own initializer call, we do it for it */
437 rootParent.d_did = DIRDID_ROOT_PARENT;
438 rootParent.d_fullpath = bfromcstr("ROOT_PARENT");
439 rootParent.d_m_name = bfromcstr("ROOT_PARENT");
440 rootParent.d_u_name = rootParent.d_m_name;
446 * @brief Dump dircache to /tmp/dircache.PID
448 void dircache_dump(void)
452 qnode_t *n = index_queue->next;
453 const struct dir *dir;
455 LOG(log_warning, logtype_afpd, "Dumping directory cache...");
457 sprintf(tmpnam, "/tmp/dircache.%u", getpid());
458 if ((dump = fopen(tmpnam, "w+")) == NULL) {
459 LOG(log_error, logtype_afpd, "dircache_dump: %s", strerror(errno));
464 fprintf(dump, "Number of cache entries: %u\n", queue_count);
465 fprintf(dump, "Configured maximum cache size: %u\n", dircache_maxsize);
466 fprintf(dump, "==================================================\n\n");
468 for (int i = 1; i <= queue_count; i++) {
469 if (n == index_queue)
471 dir = (struct dir *)n->data;
472 fprintf(dump, "%05u: vid:%u, pdid:%6u, did:%6u, path:%s, locked:%3s, oforks:%s\n",
473 i, ntohs(dir->d_vid), ntohl(dir->d_pdid), ntohl(dir->d_did), cfrombstring(dir->d_fullpath),
474 (dir->d_flags & DIRF_CACHELOCK) ? "yes" : "no",
475 dir->d_ofork ? "yes" : "no");