Tcl Source Code

View Ticket
Login
Ticket UUID: da7c079978cb3183731147d2e6a555794416a5b
Title: FS strips root '/' from FS root volume "zvfs:/"
Type: Bug Version: 8.6.6
Submitter: anonymous Created on: 2017-06-08 02:32:33
Subsystem: 37. File System Assigned To: nobody
Priority: 5 Medium Severity: Important
Status: Open Last Modified: 2017-06-19 20:22:33
Resolution: None Closed By: nobody
    Closed on:
Description:
I've been attempting to create a vfs which is totally isolated from the native fs. I've set the Tcl_Filesystem to "zvfs" and, following the example in tclTest.c, my FSListVolumesProc returns a single Tcl_Obj "zvfs:/".

Using a minimal Tcl_Filesystem with only ListVolumes, Chdir and PathInFilesystem (which always returns TCL_OK unless the path is a windows path), I find that Tcl is stripping the root '/' from what it considers the cwd.

% cd zvfs:/
% cd dir
% pwd
zvfs:/dir
% cd ..
% pwd
zvfs:

Tcl handles windows paths correctly -- the '/' is not stripped from, say, "C:/".

The routine TclFSNormalizeAbsolutePath in tclPathObj.c is the culprit. It is taking "zvfs:/dir/.." and:

		/*
		 * Have '..' so need to skip previous directory.
		 */
		if (retVal == NULL) {
		    const char *path = TclGetString(pathPtr);

		    retVal = Tcl_NewStringObj(path, dirSep - path);
		    Tcl_IncrRefCount(retVal);
		}

removes the '/' leaving "zvfs:". Toward the bottom of the function there is specific windows code to restore the '/':


    /*
     * Ensure a windows drive like C:/ has a trailing separator.
     */

    if (tclPlatform == TCL_PLATFORM_WINDOWS) {
	int len;
	const char *path = Tcl_GetStringFromObj(retVal, &len);

	if (len == 2 && path[0] != 0 && path[1] == ':') {
	    if (Tcl_IsShared(retVal)) {
		TclDecrRefCount(retVal);
		retVal = Tcl_DuplicateObj(retVal);
		Tcl_IncrRefCount(retVal);
	    }
	    Tcl_AppendToObj(retVal, "/", 1);
	}
    }

Either:
a) I don't understand what FSListVolumesProc is supposed to return
b) If "zvfs:/" is OK, then perhaps it's not meant to be equivalent to a windows "drive" even though TclpObjListVolumes() in win/tclWinFCmd.c returns "C:/", etc.
c) Tcl does not consider fs volumes other than windows

I don't know if this is a bug or what.

How is a fs supposed to tell Tcl about a volume prefix? 

Must a fs "mount" its directory structure somewhere within '/' for both unix and windows?

I wish there was a reference fs or the documentation were more detailed.
User Comments: dgp added on 2017-06-19 20:22:33:
Have you tried comparing what you're doing
with trofs?

http://math.nist.gov/~DPorter/tcltk/trofs/

Sorry to say I've not yet dug into all the detail
you are reporting. Don't know when I will have time.

anonymous added on 2017-06-19 20:08:46:
I've now hit an absolute roadblock so I'm dead in the water, but enough of the analogies.

I tried implementing FSRenameFileProc but Tcl fails to recognize "zvfs:/newfile" as belonging to the zvfs fs. It tries to stat the native fs with a utf name and fails since windows filenames cannot have an embedded ':'. Therefore Tcl never calls my FSRenameFileProc at all.

I don't see any way around this bug.

anonymous added on 2017-06-18 00:15:59:
I am continuing with my zvfs: fs and I found another bug. Again, it is an
instance of Tcl not handling VOLUME_RELATIVE fs correctly and doing a
"windows" thing just because I happen to be developing on windows.

After:
 % cd zvfs:
 % pwd
 zvfs:/
 % cd /

"cd /" attempts to cd to "zv/"

Tcl_FSGetNormalizedPath() in tclPathObj.c around line 1946:

  Tcl_PathType type = Tcl_FSGetPathType(absolutePath);

Variable "type" is now set to TCL_PATH_VOLUME_RELATIVE (which is actually
correct even though my fs is not windows)

A few lines later:

  #ifdef _WIN32
	    } else if (type == TCL_PATH_VOLUME_RELATIVE) {
		/*
		 * Only Windows has volume-relative paths.
		 */


If TCL_PATH_VOLUME_RELATIVE is only for windows, then Tcl_FSGetPathType() has a bug. Otherwise, the comment "Only Windows has volume..." is false.

Since TclWinVolumeRelativeNormalize() is called, it makes the incorrect
assumption that the cwd is of the form "D:..." (<drive>:) and proceeds to
take the "D:" and append the "/". Thus the bogus result "zv/". That path is passed to FSPathInFilesystemProc where it is rejected.

anonymous (claiming to be [email protected]) added on 2017-06-11 02:02:11:
There seems to be many places in generic/ which address windows peculiarities. The volume relative stuff is one of them. I'm guessing that Tcl has not considered other volume relative fs'

I have an extremely bare-boned test code for this prob. I found that I am able to use FSNormalizePathProc() to detect and, it look like, fix the bogus path. In the hope it could be useful, you can find the code here: https://chiselapp.com/user/iamdave/repository/Testing/index

I think my "fix" is a kludge -- if Tcl accepts "fs:/" from FSListVolumesProc() then Tcl should handle them properly. Perhaps TclFSNormalizeAbsolutePath in tclPathObj.c should check the volume prefix against all fs' registered and not have a special case for windows.
 Otherwise, please make the documentation more clear.

If the current Tcl behavior is correct, then Tcl should force a fs to "mount" itself within Tcl's unix type fs rooted at '/'.

Again, a reference implementation of a full Tcl vfs would be very, very helpful.