source: branches/client-2.0/src/dircache.c@ 919

Last change on this file since 919 was 498, checked in by Silvan Scherrer, 15 years ago

samba client: caching

File size: 18.1 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <stdarg.h>
4#include <string.h>
5#include <time.h>
6
7#define NDPL_LARGEFILES
8#define INCL_LONGLONG
9#include <ndextpl2.h>
10
11#include "smbwrp.h"
12
13extern PLUGINHELPERTABLE2L *ph;
14
15/*
16 * Static helpers.
17 */
18static int lockCreate (NDMUTEX *pMutex)
19{
20 return ph->fsphCreateMutex (pMutex);
21}
22
23static void lockDelete (NDMUTEX mutex)
24{
25 ph->fsphCloseMutex (mutex);
26}
27
28static int lockRequest (NDMUTEX mutex)
29{
30 return ph->fsphRequestMutex (mutex, 10000);
31}
32
33void lockRelease (NDMUTEX mutex)
34{
35 ph->fsphReleaseMutex (mutex);
36}
37
38static ULONG Hash (const char *pData, ULONG ulLen)
39{
40 ULONG hash = 0ul, g;
41 static ULONG ulHashModule = 30031ul;
42
43 const char *p;
44 int i;
45 for( p = pData, i = 0; i < ulLen; i++, p++ )
46 {
47 hash = ( hash << 4 ) + (*p);
48 g = hash & 0xf0000000ul;
49 if ( g )
50 {
51 hash ^= ( g >> 24 );
52 hash ^= g;
53 }
54 }
55 return ( hash % ulHashModule );
56}
57
58static unsigned long timesec (void)
59{
60 ULONG ul = 0;
61 DosQuerySysInfo (QSV_TIME_LOW, QSV_TIME_LOW, &ul, sizeof (ul));
62 return ul;
63}
64
65static unsigned long computehash (const char *s)
66{
67 return s? Hash ((char *)s, strlen (s)): 0;
68}
69
70
71
72/*
73 * An entry in the directory cache contains one directory listing.
74 */
75typedef struct DirectoryCacheEntry
76{
77 struct DirectoryCacheEntry *pNext;
78 struct DirectoryCacheEntry *pPrev;
79
80 struct DirectoryCache *pdc;
81
82 smbwrp_fileinfo *aInfos;
83 int cInfos;
84 int cInfosAllocated;
85
86 char *pszPath;
87 ULONG ulHash;
88 ULONG ulLastUpdateTime;
89 int fInvalid;
90} DirectoryCacheEntry;
91
92typedef struct DirectoryCache
93{
94 NDMUTEX mutex;
95
96 DirectoryCacheEntry *pEntriesHead;
97 DirectoryCacheEntry *pEntriesTail;
98 int cEntries;
99
100 int fEnabled;
101 unsigned long ulExpirationTime;
102 int cMaxEntries;
103} DirectoryCache;
104
105enum {
106 CacheFault = 0,
107 CacheOk = 1
108};
109
110
111static int dceCreate (DirectoryCacheEntry **ppdce, DirectoryCache *pdc, const char *path, int cFiles)
112{
113 DirectoryCacheEntry *pdce = (DirectoryCacheEntry *)malloc(sizeof(DirectoryCacheEntry));
114
115 if (!pdce)
116 {
117 return ERROR_NOT_ENOUGH_MEMORY;
118 }
119
120 pdce->aInfos = (smbwrp_fileinfo *)malloc(cFiles * sizeof (smbwrp_fileinfo));
121 if (!pdce->aInfos)
122 {
123 free (pdce);
124 return ERROR_NOT_ENOUGH_MEMORY;
125 }
126 pdce->cInfos = 0;
127 pdce->cInfosAllocated = cFiles;
128 pdce->ulHash = computehash (path);
129 pdce->ulLastUpdateTime = 0;
130 pdce->pdc = pdc;
131 pdce->fInvalid = 0;
132
133 int l = strlen (path);
134 pdce->pszPath = (char *)malloc (l + 1);
135 memcpy (pdce->pszPath, path, l + 1);
136
137 pdce->pNext = NULL;
138 pdce->pPrev = NULL;
139
140 *ppdce = pdce;
141
142 return NO_ERROR;
143}
144
145static void dceDelete (DirectoryCacheEntry *pdce)
146{
147 free(pdce->aInfos);
148 free(pdce->pszPath);
149 free(pdce);
150}
151
152static void dceClear (DirectoryCacheEntry *pdce)
153{
154 pdce->cInfos = 0;
155}
156
157static void dceWriteEntry (DirectoryCacheEntry *pdce, const smbwrp_fileinfo *finfo)
158{
159 if (pdce->cInfos >= pdce->cInfosAllocated)
160 {
161 /* 50% increase of the buffer, but at least 1 more. */
162 int cNewAllocated = pdce->cInfosAllocated + pdce->cInfosAllocated / 2 + 1;
163
164 smbwrp_fileinfo *pNewInfos = (smbwrp_fileinfo *)realloc(pdce->aInfos,
165 cNewAllocated * sizeof (smbwrp_fileinfo));
166
167 debuglocal(9, "dceWriteEntry: [%s] realloc %d -> %d\n", pdce->pszPath, pdce->cInfosAllocated, cNewAllocated);
168 if (pNewInfos)
169 {
170 pdce->cInfosAllocated = cNewAllocated;
171 pdce->aInfos = pNewInfos;
172 }
173 else
174 {
175 /* Mark the entry as invalid. The entry will be deleted in dircache_write_end */
176 pdce->fInvalid = 1;
177 return;
178 }
179 }
180
181 pdce->aInfos[pdce->cInfos] = *finfo;
182 pdce->cInfos++;
183}
184
185static void dceAdjust (DirectoryCacheEntry *pdce)
186{
187 /* If the entry has too many preallocated info structures, adjust the memory allocation */
188 int cFree = pdce->cInfosAllocated - pdce->cInfos;
189
190 if (cFree > pdce->cInfos / 2)
191 {
192 /* More than 50% is free. Make the free space 2 times smaller. */
193 int cNewAllocated = pdce->cInfos + cFree / 2;
194
195 smbwrp_fileinfo *pNewInfos = (smbwrp_fileinfo *)realloc(pdce->aInfos,
196 cNewAllocated * sizeof (smbwrp_fileinfo));
197
198 debuglocal(9, "dceAdjust: [%s] realloc %d -> %d\n", pdce->pszPath, pdce->cInfosAllocated, cNewAllocated);
199 if (pNewInfos)
200 {
201 pdce->cInfosAllocated = cNewAllocated;
202 pdce->aInfos = pNewInfos;
203 }
204 else
205 {
206 /* Ignore. The old buffer remains. */
207 }
208 }
209}
210
211static int dcePathEqualsTo (DirectoryCacheEntry *pdce, const char *path)
212{
213 debuglocal(9, "dcePathEqualTo: [%s] [%s]\n", path, pdce->pszPath);
214 return ph->fsphStrICmp (path, pdce->pszPath) == 0;
215}
216
217static int dcePathPrefixedWith (DirectoryCacheEntry *pdce, const char *path)
218{
219 return ph->fsphStrNICmp (path, pdce->pszPath, strlen (path)) == 0;
220}
221
222static int dceExpired (DirectoryCacheEntry *pdce, unsigned long ulExpirationTime)
223{
224 return (timesec () - pdce->ulLastUpdateTime >= ulExpirationTime);
225}
226
227static struct DirectoryCacheEntry *dcFindDirCache (struct DirectoryCache *pdc, const char *path, int fPrefix)
228{
229 unsigned long hash = computehash (path);
230
231 DirectoryCacheEntry *iter = pdc->pEntriesHead;
232
233 debuglocal(9, "findDirCache: [%s]\n", path);
234
235 while (iter != NULL)
236 {
237 debuglocal(9, "findDirCache: entry [%s]\n", iter->pszPath);
238 if (fPrefix)
239 {
240 if (dcePathPrefixedWith (iter, path))
241 {
242 break;
243 }
244 }
245 else
246 {
247 if ( iter->ulHash == hash
248 && dcePathEqualsTo (iter, path)
249 )
250 {
251 break;
252 }
253 }
254 iter = iter->pNext;
255 }
256
257 debuglocal(9, "findDirCache: %p\n", iter);
258
259 return iter;
260}
261
262static int dcCreate (struct DirectoryCache **ppdc)
263{
264 int rc;
265
266 DirectoryCache *pdc = (DirectoryCache *)malloc(sizeof (DirectoryCache));
267
268 if (!pdc)
269 {
270 return ERROR_NOT_ENOUGH_MEMORY;
271 }
272
273 rc = lockCreate (&pdc->mutex);
274
275 if (rc != NO_ERROR)
276 {
277 free(pdc);
278 return rc;
279 }
280
281 pdc->pEntriesHead = NULL;
282 pdc->pEntriesTail = NULL;
283 pdc->cEntries = 0;
284
285 pdc->fEnabled = 0;
286 pdc->ulExpirationTime = -1;
287
288 *ppdc = pdc;
289
290 return NO_ERROR;
291}
292
293static void dcDelete (struct DirectoryCache *pdc)
294{
295 DirectoryCacheEntry *iter = pdc->pEntriesHead;
296
297 while (iter != NULL)
298 {
299 DirectoryCacheEntry *next = iter->pNext;
300
301 dceDelete (iter);
302
303 iter = next;
304 }
305
306 lockDelete (pdc->mutex);
307 free (pdc);
308}
309
310static void dcRemoveEntry (DirectoryCacheEntry *pdce)
311{
312 DirectoryCache *pdc = pdce->pdc;
313
314 if (pdce->pNext)
315 {
316 pdce->pNext->pPrev = pdce->pPrev;
317 }
318 else
319 {
320 pdc->pEntriesTail = pdce->pPrev;
321 }
322
323 if (pdce->pPrev)
324 {
325 pdce->pPrev->pNext = pdce->pNext;
326 }
327 else
328 {
329 pdc->pEntriesHead = pdce->pNext;
330 }
331
332 pdc->cEntries--;
333}
334
335static void dcInsertEntry (DirectoryCacheEntry *pdce)
336{
337 DirectoryCache *pdc = pdce->pdc;
338
339 pdce->pNext = pdc->pEntriesHead;
340
341 if (pdc->pEntriesHead)
342 {
343 pdc->pEntriesHead->pPrev = pdce;
344 }
345 else
346 {
347 pdc->pEntriesTail = pdce;
348 }
349
350 pdc->pEntriesHead = pdce;
351 pdc->cEntries++;
352}
353
354
355static void dcEnable (DirectoryCache *pdc, int fEnable, unsigned long ulExpirationTime, int cMaxEntries)
356{
357 pdc->fEnabled = fEnable;
358 pdc->ulExpirationTime = ulExpirationTime;
359 pdc->cMaxEntries = cMaxEntries;
360};
361
362static int dcExistsInCache (DirectoryCache *pdc, const char *path)
363{
364 int rc = CacheFault;
365
366 if (pdc->fEnabled)
367 {
368 if (dcFindDirCache (pdc, path, 0) != NULL)
369 {
370 rc = CacheOk;
371 }
372
373 debuglocal(9, "ExistsInCache: [%s], %d\n", path, rc);
374 }
375
376 return rc;
377}
378
379static int dcRead (DirectoryCache *pdc, const char *path,
380 PFNADDDIRENTRY fn,
381 filelist_state *state,
382 int *ptotal_received, int fForceCache)
383{
384 int i;
385
386 int rc = CacheFault;
387
388 if (pdc->fEnabled)
389 {
390 DirectoryCacheEntry *pdce;
391
392 pdce = dcFindDirCache (pdc, path, 0);
393
394 if (pdce)
395 {
396 debuglocal(9, "CacheRead: entry %p found for [%s]\n", pdce, path);
397
398 if (fForceCache)
399 {
400 for (i = 0; i < pdce->cInfos; i++)
401 {
402 fn ("", &pdce->aInfos[i], path, state);
403 }
404
405 rc = CacheOk;
406 }
407 else
408 {
409 if (dceExpired (pdce, pdc->ulExpirationTime))
410 {
411 debuglocal(9, "CacheRead: expired\n");
412 dcRemoveEntry (pdce);
413 dceDelete (pdce);
414 }
415 else
416 {
417 for (i = 0; i < pdce->cInfos; i++)
418 {
419 fn ("", &pdce->aInfos[i], path, state);
420 }
421
422 rc = CacheOk;
423 }
424 }
425
426 if (rc == CacheOk)
427 {
428 *ptotal_received = (int)pdce->cInfos;
429 }
430 }
431
432 debuglocal(9, "CacheRead: [%s], %d\n", path, rc);
433 }
434
435 return rc;
436}
437
438static DirectoryCacheEntry *dcWriteBegin (DirectoryCache *pdc, const char *path, int cFiles)
439{
440 DirectoryCacheEntry *pdce = NULL;
441
442 if (pdc->fEnabled)
443 {
444 pdce = dcFindDirCache (pdc, path, 0);
445
446 if (!pdce)
447 {
448 /* Does not exist in the cache yet. */
449 dceCreate (&pdce, pdc, path, cFiles);
450 }
451 else
452 {
453 /* Discard the listing. */
454 dceClear (pdce);
455 /* Remove the entry from list. It will be added again in dircache_write_end. */
456 dcRemoveEntry (pdce);
457 }
458
459 if (pdce)
460 {
461 pdce->ulLastUpdateTime = timesec ();
462 }
463
464 debuglocal(9, "CacheWriteBegin: %s\n", path);
465 }
466
467 return pdce;
468}
469
470
471static void dcInvalidate (DirectoryCache *pdc, const char *path, int fPrefix)
472{
473 DirectoryCacheEntry *pdce;
474
475 debuglocal(9, "CacheInvalidate: [%s]\n", path);
476
477 pdce = dcFindDirCache (pdc, path, fPrefix);
478
479 while (pdce)
480 {
481 dcRemoveEntry (pdce);
482 dceDelete (pdce);
483
484 pdce = dcFindDirCache (pdc, path, fPrefix);
485 }
486}
487
488static int dcFindPath (DirectoryCache *pdc,
489 const char *path,
490 const char *parent,
491 const char *name,
492 smbwrp_fileinfo *finfo,
493 unsigned long *pulAge)
494{
495 DirectoryCacheEntry *pdce = pdc->pEntriesHead;
496
497 unsigned long hash = computehash (parent);
498
499 debuglocal(9, "dcFindPath: [%s][%s]\n", parent, name);
500
501 while (pdce != NULL)
502 {
503 debuglocal(9, "dcFindPath: entry [%s]\n", pdce->pszPath);
504
505 if ( pdce->ulHash == hash
506 && dcePathEqualsTo (pdce, parent))
507 {
508 /* This entry should contain the path. */
509 int i;
510 for (i = 0; i < pdce->cInfos; i++)
511 {
512 if (ph->fsphStrICmp (pdce->aInfos[i].fname, name) == 0)
513 {
514 *finfo = pdce->aInfos[i];
515 *pulAge = timesec () - pdce->ulLastUpdateTime;
516 debuglocal(9, "dircache: FindPath %s found, age %d\n", path, *pulAge);
517 return 1;
518 }
519 }
520 }
521
522 pdce = pdce->pNext;
523 }
524
525 debuglocal(9, "dircache: FindPath %s not found\n", path);
526 return 0;
527}
528
529static int dcCreateDirPath(char *path, int cbPath, filelist_state *state)
530{
531 /* State contains the original path passed to the plugin (fullpath) and
532 * the actual filename mask (dir_mask), which should be used to filter
533 * appropriate filenames from the full listing.
534 * So the directory name can be constructed by removing the mask from the fullpath.
535 */
536 int cbDir;
537 int cbMask = strlen(state->dir_mask) + 1;
538 if (cbMask > cbPath)
539 {
540 /* This actually should never happen, because mask is a part of path.
541 * But still return a failure, better no dircache than a crash.
542 */
543 return 0;
544 }
545 cbDir = cbPath - cbMask;
546 if (cbDir > 0)
547 {
548 cbDir--; /* Exclude the slash. */
549 memcpy(path, state->fullpath, cbDir);
550 }
551 path[cbDir] = 0;
552 return 1;
553}
554
555
556/*
557 * Public API.
558 */
559
560int dircache_create(DirectoryCache **ppdc, unsigned long ulExpirationTime, int cMaxEntries)
561{
562 int rc;
563
564 debuglocal(9, "dircache_create: %u seconds, %d entries\n", ulExpirationTime, cMaxEntries);
565
566 rc = dcCreate(ppdc);
567
568 if (rc == NO_ERROR)
569 {
570 dcEnable (*ppdc, 1, ulExpirationTime, cMaxEntries);
571 }
572
573 debuglocal(9, "dircache_create: %p, rc = %d\n", *ppdc, rc);
574 return rc;
575}
576
577void dircache_delete(DirectoryCache *pdc)
578{
579 debuglocal(9, "dircache_delete: %p\n", pdc);
580
581 if (pdc)
582 {
583 dcDelete(pdc);
584 }
585}
586
587
588int dircache_list_files(PFNADDDIRENTRY fn,
589 filelist_state *state,
590 int *ptotal_received)
591{
592 int rc;
593 DirectoryCache *pdc = state->pConn->pRes->pdc;
594
595 int cbPath = strlen(state->fullpath) + 1;
596 char *path = alloca(cbPath);
597 if (!dcCreateDirPath(path, cbPath, state))
598 {
599 return 0;
600 }
601
602 debuglocal(9, "dircache_list_files [%s]\n", path);
603
604 if (!pdc)
605 {
606 return 0;
607 }
608
609 lockRequest (pdc->mutex);
610
611 rc = dcRead (pdc, path, fn, state, ptotal_received, 0);
612
613 lockRelease (pdc->mutex);
614
615 return (rc == CacheOk);
616}
617
618void *dircache_write_begin(filelist_state *state,
619 int cFiles)
620{
621 DirectoryCache *pdc = state->pConn->pRes->pdc;
622 DirectoryCacheEntry *pdce = NULL;
623
624 int cbPath = strlen(state->fullpath) + 1;
625 char *path = alloca(cbPath);
626 if (!dcCreateDirPath(path, cbPath, state))
627 {
628 return NULL;
629 }
630
631 debuglocal(9, "dircache_write_begin pdc %p path [%s]\n", pdc, path);
632
633 if (!pdc)
634 {
635 return NULL;
636 }
637
638 lockRequest (pdc->mutex);
639
640 if (pdc->cEntries >= pdc->cMaxEntries)
641 {
642 /* Remove oldest entry. */
643 pdce = pdc->pEntriesTail;
644 dcRemoveEntry (pdce);
645 dceDelete (pdce);
646 pdce = NULL;
647 }
648
649 pdce = dcWriteBegin (pdc, path, cFiles);
650
651 lockRelease (pdc->mutex);
652
653 debuglocal(9, "dircache_write_begin returning ctx %p\n", pdce);
654 return (void *)pdce;
655}
656
657void dircache_write_entry(void *dircachectx, const smbwrp_fileinfo *finfo)
658{
659 DirectoryCacheEntry *pdce = (DirectoryCacheEntry *)dircachectx;
660
661 debuglocal(9, "dircache_write_entry %p\n", pdce);
662
663 if (!pdce)
664 {
665 return;
666 }
667
668 lockRequest (pdce->pdc->mutex);
669
670 dceWriteEntry (pdce, finfo);
671
672 lockRelease (pdce->pdc->mutex);
673}
674
675void dircache_write_end(void *dircachectx)
676{
677 DirectoryCacheEntry *pdce = (DirectoryCacheEntry *)dircachectx;
678 DirectoryCache *pdc;
679
680 debuglocal(9, "dircache_write_end: pdce %p\n", pdce);
681
682 if (!pdce)
683 {
684 return;
685 }
686
687 pdc = pdce->pdc;
688
689 lockRequest (pdc->mutex);
690
691 if (pdce->fInvalid)
692 {
693 /* Something happened during writing to the entry. Delete it. */
694 dceDelete (pdce);
695 }
696 else
697 {
698 dceAdjust (pdce);
699 dcInsertEntry (pdce);
700 }
701
702 lockRelease (pdc->mutex);
703}
704
705void dircache_invalidate(const char *path,
706 struct DirectoryCache *pdc,
707 int fParent)
708{
709 debuglocal(9, "dircache_invalidate [%s], parent %d\n", path, fParent);
710
711 if (!pdc)
712 {
713 return;
714 }
715
716 lockRequest (pdc->mutex);
717
718 if (fParent)
719 {
720 int cb = strlen (path) + 1;
721 char *p = (char *)alloca(cb);
722 memcpy(p, path, cb);
723 char *lastSlash = ph->fsphStrRChr(p, '\\');
724 if (lastSlash)
725 {
726 *lastSlash = 0;
727 dcInvalidate (pdc, p, 0);
728 }
729 else
730 {
731 dcInvalidate (pdc, "", 0);
732 }
733 }
734 else
735 {
736 dcInvalidate (pdc, path, 0);
737 }
738
739 lockRelease (pdc->mutex);
740
741 return;
742}
743
744int dircache_find_path(struct DirectoryCache *pdc,
745 const char *path,
746 smbwrp_fileinfo *finfo,
747 unsigned long *pulAge)
748{
749 int cb;
750 char *p;
751 char *lastSlash;
752 int fFound = 0;
753
754 debuglocal(9, "dircache_find_path [%s]\n", path);
755
756 if (!pdc)
757 {
758 return 0;
759 }
760
761 if (ph->fsphStrChr(path, '*') || ph->fsphStrChr(path, '?'))
762 {
763 /* Wildcards are not allowed in the input path. */
764 return 0;
765 }
766
767 lockRequest (pdc->mutex);
768
769 /* Prepare the parent path. */
770 cb = strlen (path) + 1;
771 p = (char *)alloca(cb);
772 memcpy(p, path, cb);
773 lastSlash = ph->fsphStrRChr(p, '\\');
774 if (lastSlash)
775 {
776 *lastSlash = 0;
777 fFound = dcFindPath (pdc, path, p, lastSlash + 1, finfo, pulAge);
778 }
779 else
780 {
781 /* Find in the root directory. p is the name. */
782 fFound = dcFindPath (pdc, path, "", p, finfo, pulAge);
783 }
784
785 lockRelease (pdc->mutex);
786
787 return fFound;
788}
Note: See TracBrowser for help on using the repository browser.