source: branches/client-3.0/src/dircache.c

Last change on this file was 1033, checked in by Paul Smedley, 8 years ago

client-3.0: Report number of files cached - fix typo

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