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

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

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