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

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

Made code completely independent from samba data structures. ticket#274.

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