Tcl Source Code

Check-in [a340fc1f8f]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:3024359 Make sure that the per-thread cache of the list of file systems currently registered is only updated at times when no active loops are traversing it. Also reduce the amount of epoch storing and checking to where it can make a difference.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a340fc1f8fa2aa420b084cd6a8c13bc7e0566d22
User & Date: dgp 2012-06-25 16:42:45
Context
2012-06-26
08:05
Use EAI_SYSTEM only if it exists. check-in: 3a19fabce0 user: max tags: trunk
2012-06-25
16:42
3024359 Make sure that the per-thread cache of the list of file systems currently registered is only... check-in: a340fc1f8f user: dgp tags: trunk
16:19
Repair Claim/Disclaim imbalance check-in: 42530a7e20 user: dgp tags: core-8-5-branch
13:05
minor: changelog formatting check-in: 6a08a1397b user: dkf tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ChangeLog.









1
2
3
4
5
6
7








2012-06-25  Donal K. Fellows  <[email protected]>

	* generic/tclCmdAH.c (EncodingDirsObjCmd): [Bug 3537605]: Do the right
	thing when reporting errors with the number of arguments.

2012-06-25  Jan Nijtmans  <[email protected]>

>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2012-06-25  Don Porter  <[email protected]>

	* generic/tclFileSystem.h:	[Bug 3024359] Make sure that the
	* generic/tclIOUtil.c:	per-thread cache of the list of file systems
	* generic/tclPathObj.c:	currently registered is only updated at times
	when no active loops are traversing it.  Also reduce the amount of
	epoch storing and checking to where it can make a difference.

2012-06-25  Donal K. Fellows  <[email protected]>

	* generic/tclCmdAH.c (EncodingDirsObjCmd): [Bug 3537605]: Do the right
	thing when reporting errors with the number of arguments.

2012-06-25  Jan Nijtmans  <[email protected]>

Changes to generic/tclFileSystem.h.

47
48
49
50
51
52
53

54
55
56
57
58
59
60
typedef struct ThreadSpecificData {
    int initialized;
    int cwdPathEpoch;
    int filesystemEpoch;
    Tcl_Obj *cwdPathPtr;
    ClientData cwdClientData;
    FilesystemRecord *filesystemList;

} ThreadSpecificData;

/*
 * The internal TclFS API provides routines for handling and manipulating
 * paths efficiently, taking direct advantage of the "path" Tcl_Obj type.
 *
 * These functions are not exported at all at present.







>







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
typedef struct ThreadSpecificData {
    int initialized;
    int cwdPathEpoch;
    int filesystemEpoch;
    Tcl_Obj *cwdPathPtr;
    ClientData cwdClientData;
    FilesystemRecord *filesystemList;
    int claims;
} ThreadSpecificData;

/*
 * The internal TclFS API provides routines for handling and manipulating
 * paths efficiently, taking direct advantage of the "path" Tcl_Obj type.
 *
 * These functions are not exported at all at present.

Changes to generic/tclIOUtil.c.

36
37
38
39
40
41
42
43
44


45
46
47
48
49
50
51
52
static FilesystemRecord*FsGetFirstFilesystem(void);
static void		FsThrExitProc(ClientData cd);
static Tcl_Obj *	FsListMounts(Tcl_Obj *pathPtr, const char *pattern);
static void		FsAddMountsToGlobResult(Tcl_Obj *resultPtr,
			    Tcl_Obj *pathPtr, const char *pattern,
			    Tcl_GlobTypeData *types);
static void		FsUpdateCwd(Tcl_Obj *cwdObj, ClientData clientData);
#ifdef TCL_THREADS
static void		FsRecacheFilesystemList(void);


#endif
static void *		DivertFindSymbol(Tcl_Interp *interp,
			    Tcl_LoadHandle loadHandle, const char *symbol);
static void		DivertUnloadFile(Tcl_LoadHandle loadHandle);

/*
 * These form part of the native filesystem support. They are needed here
 * because we have a few native filesystem functions (which are the same for







<

>
>
|







36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
static FilesystemRecord*FsGetFirstFilesystem(void);
static void		FsThrExitProc(ClientData cd);
static Tcl_Obj *	FsListMounts(Tcl_Obj *pathPtr, const char *pattern);
static void		FsAddMountsToGlobResult(Tcl_Obj *resultPtr,
			    Tcl_Obj *pathPtr, const char *pattern,
			    Tcl_GlobTypeData *types);
static void		FsUpdateCwd(Tcl_Obj *cwdObj, ClientData clientData);

static void		FsRecacheFilesystemList(void);
static void		Claim(void);
static void		Disclaim(void);

static void *		DivertFindSymbol(Tcl_Interp *interp,
			    Tcl_LoadHandle loadHandle, const char *symbol);
static void		DivertUnloadFile(Tcl_LoadHandle loadHandle);

/*
 * These form part of the native filesystem support. They are needed here
 * because we have a few native filesystem functions (which are the same for
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
 * This is incremented each time we modify the linked list of filesystems. Any
 * time it changes, all cached filesystem representations are suspect and must
 * be freed. For multithreading builds, change of the filesystem epoch will
 * trigger cache cleanup in all threads.
 */

static int theFilesystemEpoch = 0;

/*
 * Stores the linked list of filesystems. A 1:1 copy of this list is also
 * maintained in the TSD for each thread. This is to avoid synchronization
 * issues.
 */








|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * This is incremented each time we modify the linked list of filesystems. Any
 * time it changes, all cached filesystem representations are suspect and must
 * be freed. For multithreading builds, change of the filesystem epoch will
 * trigger cache cleanup in all threads.
 */

static int theFilesystemEpoch = 1;

/*
 * Stores the linked list of filesystems. A 1:1 copy of this list is also
 * maintained in the TSD for each thread. This is to avoid synchronization
 * issues.
 */

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551

552
553
554
555
556
557
558
559
560
561

562
563
564
565
566
567
568
569
570
571
572
573



574
575
576

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622






623

624
625




626
627
628
629
630
631
632
633
	    return 1;
	} else {
	    return 0;
	}
    }
}

#ifdef TCL_THREADS
static void
FsRecacheFilesystemList(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL, *toFree = NULL;

    /*
     * Trash the current cache.
     */

    fsRecPtr = tsdPtr->filesystemList;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = fsRecPtr->nextPtr;
	fsRecPtr->fsPtr = NULL;
	fsRecPtr->nextPtr = toFree;
	toFree = fsRecPtr;
	fsRecPtr = tmpFsRecPtr;
    }
    tsdPtr->filesystemList = NULL;

    /*
     * Code below operates on shared data. We are already called under mutex
     * lock so we can safely proceed.
     *
     * Locate tail of the global filesystem list.
     */


    fsRecPtr = filesystemList;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = fsRecPtr;
	fsRecPtr = fsRecPtr->nextPtr;
    }

    /*
     * Refill the cache honouring the order.
     */


    fsRecPtr = tmpFsRecPtr;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = ckalloc(sizeof(FilesystemRecord));
	*tmpFsRecPtr = *fsRecPtr;
	tmpFsRecPtr->nextPtr = tsdPtr->filesystemList;
	tmpFsRecPtr->prevPtr = NULL;
	if (tsdPtr->filesystemList) {
	    tsdPtr->filesystemList->prevPtr = tmpFsRecPtr;
	}
	tsdPtr->filesystemList = tmpFsRecPtr;
	fsRecPtr = fsRecPtr->prevPtr;
    }




    while (toFree) {
	FilesystemRecord *next = toFree->nextPtr;

	ckfree(toFree);
	toFree = next;
    }

    /*
     * Make sure the above gets released on thread exit.
     */

    if (tsdPtr->initialized == 0) {
	Tcl_CreateThreadExitHandler(FsThrExitProc, tsdPtr);
	tsdPtr->initialized = 1;
    }
}
#endif /* TCL_THREADS */

static FilesystemRecord *
FsGetFirstFilesystem(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    FilesystemRecord *fsRecPtr;

#ifndef TCL_THREADS
    tsdPtr->filesystemEpoch = theFilesystemEpoch;
    fsRecPtr = filesystemList;
#else
    Tcl_MutexLock(&filesystemMutex);
    if (tsdPtr->filesystemList == NULL
	    || (tsdPtr->filesystemEpoch != theFilesystemEpoch)) {
	FsRecacheFilesystemList();
	tsdPtr->filesystemEpoch = theFilesystemEpoch;
    }
    Tcl_MutexUnlock(&filesystemMutex);
    fsRecPtr = tsdPtr->filesystemList;
#endif
    return fsRecPtr;
}

/*
 * The epoch can be changed both by filesystems being added or removed and by
 * env(HOME) changing.
 */

int
TclFSEpochOk(
    int filesystemEpoch)
{






    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);


    (void) FsGetFirstFilesystem();




    return (filesystemEpoch == tsdPtr->filesystemEpoch);
}

/*
 * If non-NULL, clientData is owned by us and must be freed later.
 */

static void







<




|








<




<


<
<
<



>










>




|

<
<
<
|


>
>
>



>













<





<
<
<
<
<
<
<
|
|

<

<
|
<
<











>
>
>
>
>
>

>
|
|
>
>
>
>
|







518
519
520
521
522
523
524

525
526
527
528
529
530
531
532
533
534
535
536
537

538
539
540
541

542
543



544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564



565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587

588
589
590
591
592







593
594
595

596

597


598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
	    return 1;
	} else {
	    return 0;
	}
    }
}


static void
FsRecacheFilesystemList(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    FilesystemRecord *fsRecPtr, *tmpFsRecPtr = NULL, *toFree = NULL, *list;

    /*
     * Trash the current cache.
     */

    fsRecPtr = tsdPtr->filesystemList;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = fsRecPtr->nextPtr;

	fsRecPtr->nextPtr = toFree;
	toFree = fsRecPtr;
	fsRecPtr = tmpFsRecPtr;
    }


    /*



     * Locate tail of the global filesystem list.
     */

    Tcl_MutexLock(&filesystemMutex);
    fsRecPtr = filesystemList;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = fsRecPtr;
	fsRecPtr = fsRecPtr->nextPtr;
    }

    /*
     * Refill the cache honouring the order.
     */

    list = NULL;
    fsRecPtr = tmpFsRecPtr;
    while (fsRecPtr != NULL) {
	tmpFsRecPtr = ckalloc(sizeof(FilesystemRecord));
	*tmpFsRecPtr = *fsRecPtr;
	tmpFsRecPtr->nextPtr = list;
	tmpFsRecPtr->prevPtr = NULL;



	list = tmpFsRecPtr;
	fsRecPtr = fsRecPtr->prevPtr;
    }
    tsdPtr->filesystemList = list;
    tsdPtr->filesystemEpoch = theFilesystemEpoch;
    Tcl_MutexUnlock(&filesystemMutex);

    while (toFree) {
	FilesystemRecord *next = toFree->nextPtr;
	toFree->fsPtr = NULL;
	ckfree(toFree);
	toFree = next;
    }

    /*
     * Make sure the above gets released on thread exit.
     */

    if (tsdPtr->initialized == 0) {
	Tcl_CreateThreadExitHandler(FsThrExitProc, tsdPtr);
	tsdPtr->initialized = 1;
    }
}


static FilesystemRecord *
FsGetFirstFilesystem(void)
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);







    if (tsdPtr->filesystemList == NULL || ((tsdPtr->claims == 0)
	    && (tsdPtr->filesystemEpoch != theFilesystemEpoch))) {
	FsRecacheFilesystemList();

    }

    return tsdPtr->filesystemList;


}

/*
 * The epoch can be changed both by filesystems being added or removed and by
 * env(HOME) changing.
 */

int
TclFSEpochOk(
    int filesystemEpoch)
{
    return (filesystemEpoch == 0 || filesystemEpoch == theFilesystemEpoch);
}

static void
Claim()
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    tsdPtr->claims++;
}

static void
Disclaim()
{
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tclFsDataKey);
    tsdPtr->claims--;
}

/*
 * If non-NULL, clientData is owned by us and must be freed later.
 */

static void
737
738
739
740
741
742
743

744
745
746
747
748
749
750
	/* The native filesystem is static, so we don't free it. */

	if (fsRecPtr != &nativeFilesystemRecord) {
	    ckfree(fsRecPtr);
	}
	fsRecPtr = tmpFsRecPtr;
    }

    filesystemList = NULL;

    /*
     * Now filesystemList is NULL. This means that any attempt to use the
     * filesystem is likely to fail.
     */








>







734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
	/* The native filesystem is static, so we don't free it. */

	if (fsRecPtr != &nativeFilesystemRecord) {
	    ckfree(fsRecPtr);
	}
	fsRecPtr = tmpFsRecPtr;
    }
    theFilesystemEpoch++;
    filesystemList = NULL;

    /*
     * Now filesystemList is NULL. This means that any attempt to use the
     * filesystem is likely to fail.
     */

769
770
771
772
773
774
775

776
777
778
779
780
781
782
 *----------------------------------------------------------------------
 */

void
TclResetFilesystem(void)
{
    filesystemList = &nativeFilesystemRecord;


#ifdef __WIN32__
    /*
     * Cleans up the win32 API filesystem proc lookup table. This must happen
     * very late in finalization so that deleting of copied dlls can occur.
     */








>







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
 *----------------------------------------------------------------------
 */

void
TclResetFilesystem(void)
{
    filesystemList = &nativeFilesystemRecord;
    theFilesystemEpoch++;

#ifdef __WIN32__
    /*
     * Cleans up the win32 API filesystem proc lookup table. This must happen
     * very late in finalization so that deleting of copied dlls can occur.
     */

1341
1342
1343
1344
1345
1346
1347

1348
1349
1350
1351
1352
1353
1354
     * special case, in which if we have a native filesystem handler, we call
     * it first. This is because the root of Tcl's filesystem is always a
     * native filesystem (i.e. '/' on unix is native).
     */

    firstFsRecPtr = FsGetFirstFilesystem();


    for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) {
	if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
	    continue;
	}

	/*
	 * TODO: Assume that we always find the native file system; it should







>







1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
     * special case, in which if we have a native filesystem handler, we call
     * it first. This is because the root of Tcl's filesystem is always a
     * native filesystem (i.e. '/' on unix is native).
     */

    firstFsRecPtr = FsGetFirstFilesystem();

    Claim();
    for (fsRecPtr=firstFsRecPtr; fsRecPtr!=NULL; fsRecPtr=fsRecPtr->nextPtr) {
	if (fsRecPtr->fsPtr != &tclNativeFilesystem) {
	    continue;
	}

	/*
	 * TODO: Assume that we always find the native file system; it should
1378
1379
1380
1381
1382
1383
1384

1385
1386
1387
1388
1389
1390
1391

	/*
	 * We could add an efficiency check like this:
	 *		if (retVal == length-of(pathPtr)) {break;}
	 * but there's not much benefit.
	 */
    }


    return startAt;
}

/*
 *---------------------------------------------------------------------------
 *







>







1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392

	/*
	 * We could add an efficiency check like this:
	 *		if (retVal == length-of(pathPtr)) {break;}
	 * but there's not much benefit.
	 */
    }
    Disclaim();

    return startAt;
}

/*
 *---------------------------------------------------------------------------
 *
2584
2585
2586
2587
2588
2589
2590
2591

2592
2593
2594
2595
2596
2597
2598
2599

	/*
	 * We've never been called before, try to find a cwd. Call each of the
	 * "Tcl_GetCwd" function in succession. A non-NULL return value
	 * indicates the particular function has succeeded.
	 */

	for (fsRecPtr = FsGetFirstFilesystem();

		(retVal == NULL) && (fsRecPtr != NULL);
		fsRecPtr = fsRecPtr->nextPtr) {
	    ClientData retCd;
	    TclFSGetCwdProc2 *proc2;
	    if (fsRecPtr->fsPtr->getCwdProc == NULL) {
		continue;
	    }








|
>
|







2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601

	/*
	 * We've never been called before, try to find a cwd. Call each of the
	 * "Tcl_GetCwd" function in succession. A non-NULL return value
	 * indicates the particular function has succeeded.
	 */

	fsRecPtr = FsGetFirstFilesystem();
	Claim();
	for (; (retVal == NULL) && (fsRecPtr != NULL);
		fsRecPtr = fsRecPtr->nextPtr) {
	    ClientData retCd;
	    TclFSGetCwdProc2 *proc2;
	    if (fsRecPtr->fsPtr->getCwdProc == NULL) {
		continue;
	    }

2630
2631
2632
2633
2634
2635
2636

2637
2638
2639
2640
2641
2642
2643

2644
2645
2646
2647
2648
2649
2650
		    FsUpdateCwd(norm, retCd);
		    Tcl_DecrRefCount(norm);
		} else {
		    fsRecPtr->fsPtr->freeInternalRepProc(retCd);
		}
		Tcl_DecrRefCount(retVal);
		retVal = NULL;

		goto cdDidNotChange;
	    } else if (interp != NULL) {
		Tcl_AppendResult(interp,
			"error getting working directory name: ",
			Tcl_PosixError(interp), NULL);
	    }
	}


	/*
	 * Now the 'cwd' may NOT be normalized, at least on some platforms.
	 * For the sake of efficiency, we want a completely normalized cwd at
	 * all times.
	 *
	 * Finally, if retVal is NULL, we do not have a cwd, which could be







>







>







2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
		    FsUpdateCwd(norm, retCd);
		    Tcl_DecrRefCount(norm);
		} else {
		    fsRecPtr->fsPtr->freeInternalRepProc(retCd);
		}
		Tcl_DecrRefCount(retVal);
		retVal = NULL;
		Disclaim();
		goto cdDidNotChange;
	    } else if (interp != NULL) {
		Tcl_AppendResult(interp,
			"error getting working directory name: ",
			Tcl_PosixError(interp), NULL);
	    }
	}
	Disclaim();

	/*
	 * Now the 'cwd' may NOT be normalized, at least on some platforms.
	 * For the sake of efficiency, we want a completely normalized cwd at
	 * all times.
	 *
	 * Finally, if retVal is NULL, we do not have a cwd, which could be
3757
3758
3759
3760
3761
3762
3763

3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774

3775
3776
3777
3778
3779
3780
3781
     * Call each of the "listVolumes" function in succession. A non-NULL
     * return value indicates the particular function has succeeded. We call
     * all the functions registered, since we want a list of all drives from
     * all filesystems.
     */

    fsRecPtr = FsGetFirstFilesystem();

    while (fsRecPtr != NULL) {
	if (fsRecPtr->fsPtr->listVolumesProc != NULL) {
	    Tcl_Obj *thisFsVolumes = fsRecPtr->fsPtr->listVolumesProc();

	    if (thisFsVolumes != NULL) {
		Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
		Tcl_DecrRefCount(thisFsVolumes);
	    }
	}
	fsRecPtr = fsRecPtr->nextPtr;
    }


    return resultPtr;
}

/*
 *---------------------------------------------------------------------------
 *







>











>







3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
     * Call each of the "listVolumes" function in succession. A non-NULL
     * return value indicates the particular function has succeeded. We call
     * all the functions registered, since we want a list of all drives from
     * all filesystems.
     */

    fsRecPtr = FsGetFirstFilesystem();
    Claim();
    while (fsRecPtr != NULL) {
	if (fsRecPtr->fsPtr->listVolumesProc != NULL) {
	    Tcl_Obj *thisFsVolumes = fsRecPtr->fsPtr->listVolumesProc();

	    if (thisFsVolumes != NULL) {
		Tcl_ListObjAppendList(NULL, resultPtr, thisFsVolumes);
		Tcl_DecrRefCount(thisFsVolumes);
	    }
	}
	fsRecPtr = fsRecPtr->nextPtr;
    }
    Disclaim();

    return resultPtr;
}

/*
 *---------------------------------------------------------------------------
 *
3807
3808
3809
3810
3811
3812
3813

3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824

3825
3826
3827
3828
3829
3830
3831
     * Call each of the "matchInDirectory" functions in succession, with the
     * specific type information 'mountsOnly'. A non-NULL return value
     * indicates the particular function has succeeded. We call all the
     * functions registered, since we want a list from each filesystems.
     */

    fsRecPtr = FsGetFirstFilesystem();

    while (fsRecPtr != NULL) {
	if (fsRecPtr->fsPtr != &tclNativeFilesystem &&
		fsRecPtr->fsPtr->matchInDirectoryProc != NULL) {
	    if (resultPtr == NULL) {
		resultPtr = Tcl_NewObj();
	    }
	    fsRecPtr->fsPtr->matchInDirectoryProc(NULL, resultPtr, pathPtr,
		    pattern, &mountsOnly);
	}
	fsRecPtr = fsRecPtr->nextPtr;
    }


    return resultPtr;
}

/*
 *---------------------------------------------------------------------------
 *







>











>







3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
     * Call each of the "matchInDirectory" functions in succession, with the
     * specific type information 'mountsOnly'. A non-NULL return value
     * indicates the particular function has succeeded. We call all the
     * functions registered, since we want a list from each filesystems.
     */

    fsRecPtr = FsGetFirstFilesystem();
    Claim();
    while (fsRecPtr != NULL) {
	if (fsRecPtr->fsPtr != &tclNativeFilesystem &&
		fsRecPtr->fsPtr->matchInDirectoryProc != NULL) {
	    if (resultPtr == NULL) {
		resultPtr = Tcl_NewObj();
	    }
	    fsRecPtr->fsPtr->matchInDirectoryProc(NULL, resultPtr, pathPtr,
		    pattern, &mountsOnly);
	}
	fsRecPtr = fsRecPtr->nextPtr;
    }
    Disclaim();

    return resultPtr;
}

/*
 *---------------------------------------------------------------------------
 *
4030
4031
4032
4033
4034
4035
4036

4037
4038
4039
4040
4041
4042
4043
    /*
     * Call each of the "listVolumes" function in succession, checking whether
     * the given path is an absolute path on any of the volumes returned (this
     * is done by checking whether the path's prefix matches).
     */

    fsRecPtr = FsGetFirstFilesystem();

    while (fsRecPtr != NULL) {
	/*
	 * We want to skip the native filesystem in this loop because
	 * otherwise we won't necessarily pass all the Tcl testsuite - this is
	 * because some of the tests artificially change the current platform
	 * (between win, unix) but the list of volumes we get by calling
	 * fsRecPtr->fsPtr->listVolumesProc will reflect the current (real)







>







4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
    /*
     * Call each of the "listVolumes" function in succession, checking whether
     * the given path is an absolute path on any of the volumes returned (this
     * is done by checking whether the path's prefix matches).
     */

    fsRecPtr = FsGetFirstFilesystem();
    Claim();
    while (fsRecPtr != NULL) {
	/*
	 * We want to skip the native filesystem in this loop because
	 * otherwise we won't necessarily pass all the Tcl testsuite - this is
	 * because some of the tests artificially change the current platform
	 * (between win, unix) but the list of volumes we get by calling
	 * fsRecPtr->fsPtr->listVolumesProc will reflect the current (real)
4107
4108
4109
4110
4111
4112
4113

4114
4115
4116
4117
4118
4119
4120

		    break;
		}
	    }
	}
	fsRecPtr = fsRecPtr->nextPtr;
    }

    return type;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_FSRenameFile --







>







4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130

		    break;
		}
	    }
	}
	fsRecPtr = fsRecPtr->nextPtr;
    }
    Disclaim();
    return type;
}

/*
 *---------------------------------------------------------------------------
 *
 * Tcl_FSRenameFile --
4494
4495
4496
4497
4498
4499
4500


4501

4502
4503
4504

4505
4506
4507
4508
4509
4510
4511
     * Check if the filesystem has changed in some way since this object's
     * internal representation was calculated. Before doing that, assure we
     * have the most up-to-date copy of the master filesystem. This is
     * accomplished by the FsGetFirstFilesystem() call.
     */

    fsRecPtr = FsGetFirstFilesystem();


    if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) {

	return NULL;
    } else if (retVal != NULL) {
	/* TODO: Can this happen? */

	return retVal;
    }

    /*
     * Call each of the "pathInFilesystem" functions in succession. A
     * non-return value of -1 indicates the particular function has succeeded.
     */







>
>

>



>







4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
     * Check if the filesystem has changed in some way since this object's
     * internal representation was calculated. Before doing that, assure we
     * have the most up-to-date copy of the master filesystem. This is
     * accomplished by the FsGetFirstFilesystem() call.
     */

    fsRecPtr = FsGetFirstFilesystem();
    Claim();

    if (TclFSEnsureEpochOk(pathPtr, &retVal) != TCL_OK) {
	Disclaim();
	return NULL;
    } else if (retVal != NULL) {
	/* TODO: Can this happen? */
	Disclaim();
	return retVal;
    }

    /*
     * Call each of the "pathInFilesystem" functions in succession. A
     * non-return value of -1 indicates the particular function has succeeded.
     */
4520
4521
4522
4523
4524
4525
4526

4527
4528
4529

4530
4531
4532
4533
4534
4535
4536
	if (fsRecPtr->fsPtr->pathInFilesystemProc(pathPtr, &clientData)!=-1) {
	    /*
	     * We assume the type of pathPtr hasn't been changed by the above
	     * call to the pathInFilesystemProc.
	     */

	    TclFSSetPathDetails(pathPtr, fsRecPtr->fsPtr, clientData);

	    return fsRecPtr->fsPtr;
	}
    }


    return NULL;
}

/*
 *---------------------------------------------------------------------------
 *







>



>







4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
	if (fsRecPtr->fsPtr->pathInFilesystemProc(pathPtr, &clientData)!=-1) {
	    /*
	     * We assume the type of pathPtr hasn't been changed by the above
	     * call to the pathInFilesystemProc.
	     */

	    TclFSSetPathDetails(pathPtr, fsRecPtr->fsPtr, clientData);
	    Disclaim();
	    return fsRecPtr->fsPtr;
	}
    }
    Disclaim();

    return NULL;
}

/*
 *---------------------------------------------------------------------------
 *

Changes to generic/tclPathObj.c.

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
    Tcl_Interp *interp,		/* Used for error reporting */
    Tcl_Obj *pathPtr,		/* Path to take dirname of */
    Tcl_PathPart portion)	/* Requested portion of name */
{
    if (pathPtr->typePtr == &tclFsPathType) {
	FsPath *fsPathPtr = PATHOBJ(pathPtr);

	if (TclFSEpochOk(fsPathPtr->filesystemEpoch)
		&& (PATHFLAGS(pathPtr) != 0)) {
	    switch (portion) {
	    case TCL_PATH_DIRNAME: {
		/*
		 * Check if the joined-on bit has any directory delimiters in
		 * it. If so, the 'dirname' would be a joining of the main
		 * part with the dirname of the joined-on bit. We could handle
		 * that special case here, but we don't, and instead just use







<
|







560
561
562
563
564
565
566

567
568
569
570
571
572
573
574
    Tcl_Interp *interp,		/* Used for error reporting */
    Tcl_Obj *pathPtr,		/* Path to take dirname of */
    Tcl_PathPart portion)	/* Requested portion of name */
{
    if (pathPtr->typePtr == &tclFsPathType) {
	FsPath *fsPathPtr = PATHOBJ(pathPtr);


	if (PATHFLAGS(pathPtr) != 0) {
	    switch (portion) {
	    case TCL_PATH_DIRNAME: {
		/*
		 * Check if the joined-on bit has any directory delimiters in
		 * it. If so, the 'dirname' would be a joining of the main
		 * part with the dirname of the joined-on bit. We could handle
		 * that special case here, but we don't, and instead just use
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
    fsPathPtr->translatedPathPtr = NULL;
    fsPathPtr->normPathPtr = Tcl_NewStringObj(addStrRep, len);
    Tcl_IncrRefCount(fsPathPtr->normPathPtr);
    fsPathPtr->cwdPtr = dirPtr;
    Tcl_IncrRefCount(dirPtr);
    fsPathPtr->nativePathPtr = NULL;
    fsPathPtr->fsPtr = NULL;
    fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;

    SETPATHOBJ(pathPtr, fsPathPtr);
    PATHFLAGS(pathPtr) = TCLPATH_APPENDED;
    pathPtr->typePtr = &tclFsPathType;
    pathPtr->bytes = NULL;
    pathPtr->length = 0;








|







1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
    fsPathPtr->translatedPathPtr = NULL;
    fsPathPtr->normPathPtr = Tcl_NewStringObj(addStrRep, len);
    Tcl_IncrRefCount(fsPathPtr->normPathPtr);
    fsPathPtr->cwdPtr = dirPtr;
    Tcl_IncrRefCount(dirPtr);
    fsPathPtr->nativePathPtr = NULL;
    fsPathPtr->fsPtr = NULL;
    fsPathPtr->filesystemEpoch = 0;

    SETPATHOBJ(pathPtr, fsPathPtr);
    PATHFLAGS(pathPtr) = TCLPATH_APPENDED;
    pathPtr->typePtr = &tclFsPathType;
    pathPtr->bytes = NULL;
    pathPtr->length = 0;

1520
1521
1522
1523
1524
1525
1526

1527
1528
1529
1530
1531
1532
1533
     * Circular reference by design.
     */

    fsPathPtr->normPathPtr = pathPtr;
    fsPathPtr->cwdPtr = NULL;
    fsPathPtr->nativePathPtr = NULL;
    fsPathPtr->fsPtr = NULL;

    fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;

    SETPATHOBJ(pathPtr, fsPathPtr);
    PATHFLAGS(pathPtr) = 0;
    pathPtr->typePtr = &tclFsPathType;

    return TCL_OK;







>







1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
     * Circular reference by design.
     */

    fsPathPtr->normPathPtr = pathPtr;
    fsPathPtr->cwdPtr = NULL;
    fsPathPtr->nativePathPtr = NULL;
    fsPathPtr->fsPtr = NULL;
    /* Remember the epoch under which we decided pathPtr was normalized */
    fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;

    SETPATHOBJ(pathPtr, fsPathPtr);
    PATHFLAGS(pathPtr) = 0;
    pathPtr->typePtr = &tclFsPathType;

    return TCL_OK;
1656
1657
1658
1659
1660
1661
1662






1663
1664
1665
1666
1667
1668
1669
	    if (translatedCwdPtr == NULL) {
		return NULL;
	    }

	    retObj = Tcl_FSJoinToPath(translatedCwdPtr, 1,
		    &srcFsPathPtr->normPathPtr);
	    srcFsPathPtr->translatedPathPtr = retObj;






	    Tcl_IncrRefCount(retObj);
	    Tcl_DecrRefCount(translatedCwdPtr);
	} else {
	    /*
	     * It is a pure absolute, normalized path object. This is
	     * something like being a 'pure list'. The object's string,
	     * translatedPath and normalizedPath are all identical.







>
>
>
>
>
>







1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
	    if (translatedCwdPtr == NULL) {
		return NULL;
	    }

	    retObj = Tcl_FSJoinToPath(translatedCwdPtr, 1,
		    &srcFsPathPtr->normPathPtr);
	    srcFsPathPtr->translatedPathPtr = retObj;
	    if (translatedCwdPtr->typePtr == &tclFsPathType) {
		srcFsPathPtr->filesystemEpoch
			= PATHOBJ(translatedCwdPtr)->filesystemEpoch;
	    } else {
		srcFsPathPtr->filesystemEpoch = 0;
	    }
	    Tcl_IncrRefCount(retObj);
	    Tcl_DecrRefCount(translatedCwdPtr);
	} else {
	    /*
	     * It is a pure absolute, normalized path object. This is
	     * something like being a 'pure list'. The object's string,
	     * translatedPath and normalizedPath are all identical.
2458
2459
2460
2461
2462
2463
2464




2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
     */

    fsPathPtr = ckalloc(sizeof(FsPath));

    fsPathPtr->translatedPathPtr = transPtr;
    if (transPtr != pathPtr) {
	Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);




    }
    fsPathPtr->normPathPtr = NULL;
    fsPathPtr->cwdPtr = NULL;
    fsPathPtr->nativePathPtr = NULL;
    fsPathPtr->fsPtr = NULL;
    fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;

    /*
     * Free old representation before installing our new one.
     */

    TclFreeIntRep(pathPtr);
    SETPATHOBJ(pathPtr, fsPathPtr);







>
>
>
>





<







2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479

2480
2481
2482
2483
2484
2485
2486
     */

    fsPathPtr = ckalloc(sizeof(FsPath));

    fsPathPtr->translatedPathPtr = transPtr;
    if (transPtr != pathPtr) {
	Tcl_IncrRefCount(fsPathPtr->translatedPathPtr);
	/* Redo translation when $env(HOME) changes */
	fsPathPtr->filesystemEpoch = tsdPtr->filesystemEpoch;
    } else {
	fsPathPtr->filesystemEpoch = 0;
    }
    fsPathPtr->normPathPtr = NULL;
    fsPathPtr->cwdPtr = NULL;
    fsPathPtr->nativePathPtr = NULL;
    fsPathPtr->fsPtr = NULL;


    /*
     * Free old representation before installing our new one.
     */

    TclFreeIntRep(pathPtr);
    SETPATHOBJ(pathPtr, fsPathPtr);