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

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

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