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

Last change on this file since 961 was 960, checked in by Silvan Scherrer, 9 years ago

samba client: add the possibility to have logfiles per share

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