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

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

Get plugin helper table pointer at init time. ticket#274.

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