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

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

Use a forward reference to smbwrp_fileinfo instead of including the
header. 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#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 = (smbwrp_fileinfo *)malloc(cFiles * sizeof (smbwrp_fileinfo));
82 if (!pdce->aInfos)
83 {
84 free (pdce);
85 return ERROR_NOT_ENOUGH_MEMORY;
86 }
87 pdce->cInfos = 0;
88 pdce->cInfosAllocated = cFiles;
89 pdce->ulHash = computehash (path);
90 pdce->ulLastUpdateTime = 0;
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 (DirectoryCache *pdc, DirectoryCacheEntry *pdce)
270{
271 if (pdce->pNext)
272 {
273 pdce->pNext->pPrev = pdce->pPrev;
274 }
275 else
276 {
277 pdc->pEntriesTail = pdce->pPrev;
278 }
279
280 if (pdce->pPrev)
281 {
282 pdce->pPrev->pNext = pdce->pNext;
283 }
284 else
285 {
286 pdc->pEntriesHead = pdce->pNext;
287 }
288
289 pdce->pNext = NULL;
290 pdce->pPrev = NULL;
291
292 pdc->cEntries--;
293}
294
295static void dcInsertEntry (DirectoryCache *pdc, DirectoryCacheEntry *pdce)
296{
297 pdce->pNext = pdc->pEntriesHead;
298
299 if (pdc->pEntriesHead)
300 {
301 pdc->pEntriesHead->pPrev = pdce;
302 }
303 else
304 {
305 pdc->pEntriesTail = pdce;
306 }
307
308 pdc->pEntriesHead = pdce;
309 pdc->cEntries++;
310}
311
312
313static void dcEnable (DirectoryCache *pdc, int fEnable, unsigned long ulExpirationTime, int cMaxEntries)
314{
315 pdc->fEnabled = fEnable;
316 pdc->ulExpirationTime = ulExpirationTime;
317 pdc->cMaxEntries = cMaxEntries;
318};
319
320static int dcExistsInCache (DirectoryCache *pdc, const char *path)
321{
322 int rc = CacheFault;
323
324 if (pdc->fEnabled)
325 {
326 if (dcFindDirCache (pdc, path, 0) != NULL)
327 {
328 rc = CacheOk;
329 }
330
331 debuglocal(pdc->resource, 9, "ExistsInCache: [%s], %d\n", path, rc);
332 }
333
334 return rc;
335}
336
337static int dcRead (DirectoryCache *pdc, const char *path,
338 PFNADDDIRENTRY fn,
339 void* plist,
340 int *ptotal_received, int fForceCache)
341{
342 int i;
343 int rc = CacheFault;
344
345 if (pdc->fEnabled)
346 {
347 DirectoryCacheEntry *pdce;
348
349 pdce = dcFindDirCache (pdc, path, 0);
350
351 if (pdce)
352 {
353 debuglocal(pdc->resource, 9, "dcRead: entry %p found for [%s]\n", pdce, path);
354
355 if (fForceCache)
356 {
357 for (i = 0; i < pdce->cInfos; i++)
358 {
359 fn ("", &pdce->aInfos[i], path, plist);
360 }
361
362 rc = CacheOk;
363 }
364 else
365 {
366 if (dceExpired (pdce, pdc->ulExpirationTime))
367 {
368 debuglocal(pdc->resource, 9, "dcRead: expired\n");
369 dcRemoveEntry (pdc, pdce);
370 dceDelete (pdce);
371 }
372 else
373 {
374 for (i = 0; i < pdce->cInfos; i++)
375 {
376 fn ("", &pdce->aInfos[i], path, plist);
377 }
378
379 rc = CacheOk;
380 }
381 }
382
383 if (rc == CacheOk)
384 {
385 *ptotal_received = (int)pdce->cInfos;
386 }
387 }
388
389 debuglocal(pdc->resource, 9, "dcRead: [%s], %d\n", path, rc);
390 }
391
392 return rc;
393}
394
395static DirectoryCacheEntry *dcWriteBegin (DirectoryCache *pdc, const char *path, int cFiles)
396{
397 DirectoryCacheEntry *pdce = NULL;
398
399 if (pdc->fEnabled)
400 {
401 pdce = dcFindDirCache (pdc, path, 0);
402
403 if (!pdce)
404 {
405 /* Does not exist in the cache yet. */
406 dceCreate (&pdce, pdc, path, cFiles);
407 }
408 else
409 {
410 /* Discard the listing. */
411 dceClear (pdce);
412 /* Remove the entry from list. It will be added again in dircache_write_end. */
413 dcRemoveEntry (pdc, pdce);
414 }
415
416 if (pdce)
417 {
418 pdce->ulLastUpdateTime = timesec ();
419 }
420
421 debuglocal(pdc->resource, 9, "CacheWriteBegin: %s\n", path);
422 }
423
424 return pdce;
425}
426
427
428static void dcInvalidate (DirectoryCache *pdc, const char *path, int fPrefix)
429{
430 DirectoryCacheEntry *pdce;
431
432 debuglocal(pdc->resource, 9, "CacheInvalidate: [%s]\n", path);
433
434 pdce = dcFindDirCache (pdc, path, fPrefix);
435
436 while (pdce)
437 {
438 dcRemoveEntry (pdc, pdce);
439 dceDelete (pdce);
440
441 pdce = dcFindDirCache (pdc, path, fPrefix);
442 }
443}
444
445static int dcFindPath (DirectoryCache *pdc,
446 const char *path,
447 const char *parent,
448 const char *name,
449 smbwrp_fileinfo *finfo,
450 unsigned long *pulAge)
451{
452 DirectoryCacheEntry *pdce = pdc->pEntriesHead;
453
454 unsigned long hash = computehash (parent);
455
456 debuglocal(pdc->resource, 9, "dcFindPath: [%s][%s]\n", parent, name);
457
458 while (pdce != NULL)
459 {
460 debuglocal(pdc->resource, 9, "dcFindPath: entry [%s]\n", pdce->pszPath);
461
462 if ( pdce->ulHash == hash
463 && dcePathEqualsTo (pdc, pdce, parent))
464 {
465 /* This entry should contain the path. */
466 int i;
467 for (i = 0; i < pdce->cInfos; i++)
468 {
469 if (ph->fsphStrICmp (pdce->aInfos[i].fname, name) == 0)
470 {
471 *finfo = pdce->aInfos[i];
472 *pulAge = timesec () - pdce->ulLastUpdateTime;
473 debuglocal(pdc->resource, 9, "dircache: FindPath %s found, age %d\n", path, *pulAge);
474 return 1;
475 }
476 }
477 }
478
479 pdce = pdce->pNext;
480 }
481
482 debuglocal(pdc->resource, 9, "dircache: FindPath %s not found\n", path);
483 return 0;
484}
485
486static int dcCreateDirPath(char *path, int cbPath, char* dir_mask, char* fullpath)
487{
488 /* State contains the original path passed to the plugin (fullpath) and
489 * the actual filename mask (dir_mask), which should be used to filter
490 * appropriate filenames from the full listing.
491 * So the directory name can be constructed by removing the mask from the fullpath.
492 */
493 int cbDir;
494 int cbMask = strlen(dir_mask) + 1;
495 if (cbMask > cbPath)
496 {
497 /* This actually should never happen, because mask is a part of path.
498 * But still return a failure, better no dircache than a crash.
499 */
500 return 0;
501 }
502 cbDir = cbPath - cbMask;
503 if (cbDir > 0)
504 {
505 cbDir--; /* Exclude the slash. */
506 memcpy(path, fullpath, cbDir);
507 }
508 path[cbDir] = 0;
509 return 1;
510}
511
512
513/*
514 * Public API.
515 */
516
517int dircache_create( DirectoryCache **ppdc, void* pRes,
518 int cachetimeout, int cachedepth)
519{
520 unsigned long ulExpirationTime = cachetimeout;
521 int cMaxEntries = cachedepth;
522 int rc;
523
524 debuglocal( pRes, 9, "dircache_create: %u seconds, %d entries\n", ulExpirationTime, cMaxEntries);
525
526 rc = dcCreate(ppdc, pRes);
527 if (rc == NO_ERROR)
528 {
529 dcEnable (*ppdc, 1, ulExpirationTime, cMaxEntries);
530
531 }
532
533 debuglocal( (*ppdc)->resource, 9, "dircache_create: %p, rc = %d\n", *ppdc, rc);
534 return rc;
535}
536
537void dircache_delete(DirectoryCache *pdc)
538{
539 debuglocal(pdc->resource, 9, "dircache_delete: %p\n", pdc);
540
541 if (pdc)
542 {
543 dcDelete(pdc);
544 }
545}
546
547int dircache_list_files(DirectoryCache *pdc, PFNADDDIRENTRY fn,
548 void* plist,
549 char* dir_mask, char* fullpath,
550 int *ptotal_received)
551{
552 int rc;
553
554 int cbPath = strlen(fullpath) + 1;
555 char *path = (char*) alloca(cbPath);
556 if (!dcCreateDirPath(path, cbPath, dir_mask, fullpath))
557 {
558 return 0;
559 }
560
561 debuglocal(pdc->resource, 9, "dircache_list_files [%s]\n", path);
562 if (!pdc)
563 {
564 return 0;
565 }
566
567 lockRequest (pdc->mutex);
568
569 rc = dcRead (pdc, path, fn, plist, ptotal_received, 0);
570
571 lockRelease (pdc->mutex);
572
573 return (rc == CacheOk);
574}
575
576void *dircache_write_begin(DirectoryCache *pdc,
577 char* dir_mask, char* fullpath,
578 int cFiles)
579{
580 DirectoryCacheEntry *pdce = NULL;
581
582 int cbPath = strlen(fullpath) + 1;
583 char *path = (char*) alloca(cbPath);
584 if (!dcCreateDirPath(path, cbPath, dir_mask, fullpath))
585 {
586 return NULL;
587 }
588
589 debuglocal(pdc->resource, 9, "dircache_write_begin pdc %p path [%s]\n", pdc, path);
590
591 if (!pdc)
592 {
593 return NULL;
594 }
595
596 lockRequest (pdc->mutex);
597
598 if (pdc->cEntries >= pdc->cMaxEntries)
599 {
600 /* Remove oldest entry. */
601 pdce = pdc->pEntriesTail;
602 dcRemoveEntry (pdc, pdce);
603 dceDelete (pdce);
604 pdce = NULL;
605 }
606
607 pdce = dcWriteBegin (pdc, path, cFiles);
608
609 lockRelease (pdc->mutex);
610
611 debuglocal(pdc->resource, 9, "dircache_write_begin returning ctx %p\n", pdce);
612 return (void *)pdce;
613}
614
615void dircache_write_entry(DirectoryCache *pdc, void *dircachectx,
616 const smbwrp_fileinfo *finfo)
617{
618 DirectoryCacheEntry *pdce = (DirectoryCacheEntry *)dircachectx;
619
620 debuglocal(pdc->resource, 9, "dircache_write_entry %p\n", pdce);
621
622 if (!pdce)
623 {
624 return;
625 }
626
627 lockRequest (pdc->mutex);
628
629 dceWriteEntry (pdc, pdce, finfo);
630
631 lockRelease (pdc->mutex);
632}
633
634void dircache_write_end(DirectoryCache *pdc, void *dircachectx)
635{
636 DirectoryCacheEntry *pdce = (DirectoryCacheEntry *)dircachectx;
637
638 debuglocal(pdc->resource, 9, "dircache_write_end: pdce %p\n", pdce);
639
640 if (!pdce)
641 {
642 return;
643 }
644
645 lockRequest (pdc->mutex);
646
647 if (pdce->fInvalid)
648 {
649 /* Something happened during writing to the entry. Delete it. */
650 dceDelete (pdce);
651 }
652 else
653 {
654 dceAdjust (pdc, pdce);
655 dcInsertEntry (pdc, pdce);
656 }
657
658 lockRelease (pdc->mutex);
659}
660
661void dircache_invalidate(DirectoryCache *pdc,
662 const char *path,
663 int fParent)
664{
665 debuglocal(pdc->resource, 9, "dircache_invalidate [%s], parent %d\n", path, fParent);
666
667 if (!pdc)
668 {
669 return;
670 }
671
672 lockRequest (pdc->mutex);
673
674 if (fParent)
675 {
676 int cb = strlen (path) + 1;
677 char *p = (char *)alloca(cb);
678 memcpy(p, path, cb);
679 char *lastSlash = ph->fsphStrRChr(p, '\\');
680 if (!lastSlash)
681 lastSlash = ph->fsphStrRChr(p, '/');
682 if (lastSlash)
683 {
684 *lastSlash = 0;
685 dcInvalidate (pdc, p, 0);
686 }
687 else
688 {
689 dcInvalidate (pdc, "", 0);
690 }
691 }
692 else
693 {
694 dcInvalidate (pdc, path, 0);
695 }
696
697 lockRelease (pdc->mutex);
698
699 return;
700}
701
702int dircache_find_path(DirectoryCache *pdc,
703 const char *path,
704 smbwrp_fileinfo *finfo,
705 unsigned long *pulAge)
706{
707 int cb;
708 char *p;
709 char *lastSlash;
710 int fFound = 0;
711
712 debuglocal(pdc->resource, 9, "dircache_find_path [%s]\n", path);
713
714 if (!pdc)
715 {
716 return 0;
717 }
718
719 if (ph->fsphStrChr(path, '*') || ph->fsphStrChr(path, '?'))
720 {
721 /* Wildcards are not allowed in the input path. */
722 return 0;
723 }
724
725 lockRequest (pdc->mutex);
726
727 /* Prepare the parent path. */
728 cb = strlen (path) + 1;
729 p = (char *)alloca(cb);
730 memcpy(p, path, cb);
731 lastSlash = ph->fsphStrRChr(p, '\\');
732 if (!lastSlash)
733 lastSlash = ph->fsphStrRChr(p, '/');
734 if (lastSlash)
735 {
736 *lastSlash = 0;
737 fFound = dcFindPath (pdc, path, p, lastSlash + 1, finfo, pulAge);
738 }
739 else
740 {
741 /* Find in the root directory. p is the name. */
742 fFound = dcFindPath (pdc, path, "", p, finfo, pulAge);
743 }
744
745 lockRelease (pdc->mutex);
746
747 return fFound;
748}
Note: See TracBrowser for help on using the repository browser.