source: trunk/client/src/dircache.c@ 965

Last change on this file since 965 was 965, checked in by Yuri Dario, 9 years ago

Updates to dircache code to be more independent from samba. ticket#274.

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