WAD DIR /HL still gets names wrong

  • This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn more.
#1
I'm running build 18. Below, V: is a SUBST for H:\work. This happens in any subdirectory of any SUBST.
Code:
v:\empty> dir /hl

 Volume in drive V is DATA           Serial number is c007:d3e4
 Directory of  V:\empty\*

2016-05-14  22:00         <DIR>    .
2016-05-14  22:00         <DIR>    ..
2016-09-07  00:05         <DIR>    foo
                                    = V:\empty\work\empty\foo
2015-02-16  01:18         <DIR>    x64
                                    = V:\empty\work\empty\x64
2016-05-14  21:59               0  a
                                    = V:\empty\work\empty\a
                   0 bytes in 1 file and 4 dirs
       8,056,176,640 bytes free
 
#4
Some remarks upfront:

- Hardlinks are ONLY possible on the same volume.
- Hardlinks are on sytem level, not userlevel
- a SUBST'ed folder is not a hardlink, neither is a network drive
- every file on the local filesystem is a hardlink in itself; it's only "interesting" if there are more than one to the same file.
- Windows comes with a hardlink utility: FSUTIL.exe. It behaves different on Win10 and Win7

A hardlink will get reported without a diskname (like "\Windows\notepad.exe"). So if in the output there is a drivename, it's "interpretation".
That's what you see here going wrong. The "non-interpreted" part (\work\empty\foo) is alright, but the "V:"-part gets interpreted wrong somewhere
(I guess something like @path[%1] / @path [%cd%], but then on programmers-level

FSUTIL syntax example:
fsutil hardlink list c:\Windows\notepad.exe
Output:
Code:
\Windows\notepad.exe
\Windows\winsxs\amd64_microsoft-windows-notepadwin_31bf3856ad364e35_6.1.7601.23403_none_a1830d5f2ac33b80\notepad.exe
 
Last edited:
#5
Thanks, Maarten. It makes a little sense now. Maybe TCC should not implement DIR's /HL in a SUBST, or internally use @TRUENAME on everything in a SUBST.

Code:
v:\> echo %@truename[v:\empty\foo\]
H:\work\empty\foo\

v:\> fsutil hardlink list h:\work\empty\foo\
\work\empty\foo

v:\> fsutil hardlink list v:\empty\foo\
Error:  The directory is not a subdirectory of the root directory.

The FSUTIL utility requires a local NTFS volume.
 
#6
I used @truename[ ] too in testing :-)

Actualy: I started with TRUENAME in a CMD-box, but this command no longer exists (a long time ago TRUENAME was an undocumented, hidden command in COMMAND.COM)
 
#7
Another observation ... Above I pointed out that DIR /HL doesn't get names right in a subdirectory of a SUBST. In addition, it doesn't seem to do anything in the root directory of a SUBST.
Code:
w:\> subst
P:\: => L:\projects
S:\: => C:\Windows\system32
T:\: => H:\temp
U:\: => G:\uty
V:\: => H:\work
W:\: => C:\Windows

w:\> dir /m /k /hl no*
2009-07-13  21:14         179,712  notepad.exe

w:\> c:\Windows\

c:\windows> dir /m /k /hl no*
2009-07-13  21:14         179,712  notepad.exe
                                    = C:\Windows\System32\notepad.exe
                                    = C:\Windows\winsxs\x86_microsoft-windows-notepadwin_31bf3856ad364e35_6.1.7600.16385_none_42a023025c60a33a\notepad.exe
                                    = C:\Windows\winsxs\x86_microsoft-windows-notepad_31bf3856ad364e35_6.1.7600.16385_none_6ef0e39ed15350e4\notepad.exe
 
#8
And, no surprise, LINKS behaves the same.
Code:
w:\> subst
P:\: => L:\projects
S:\: => C:\Windows\system32
T:\: => H:\temp
U:\: => G:\uty
V:\: => H:\work
W:\: => C:\Windows

w:\> links notepad.exe

w:\> c:\windows\

c:\windows> links notepad.exe
C:\Windows\System32\notepad.exe
C:\Windows\notepad.exe
C:\Windows\winsxs\x86_microsoft-windows-notepadwin_31bf3856ad364e35_6.1.7600.16385_none_42a023025c60a33a\notepad.exe
C:\Windows\winsxs\x86_microsoft-windows-notepad_31bf3856ad364e35_6.1.7600.16385_none_6ef0e39ed15350e4\notepad.exe
 
#9
... which is odd because (given the limitations previously discussed) FindFirstFileNameW (et al.) is OK with files in the root of a SUBST. This code produces the expected four links. (Likewise if I CD to W:\ and use an unqualified file name.)
Code:
   DWORD dwLen;
   WCHAR szFile[MAX_PATH];
   HANDLE hFind = FindFirstFileNameW(L"w:\\notepad.exe", 0, &(dwLen = MAX_PATH), szFile);
   if ( hFind != NULL )
       do Printf(L"%s\r\n", szFile);
           while ( FindNextFileNameW(hFind, &(dwLen = MAX_PATH), szFile) );
   FindClose(hFind);
 
#10
Nice find! You should consider a career as a beta-tester :-)
I think this has also to do with the "interpretation" part: there is no path to interpret (if that is a correct English word...) in these cases.
 
Likes: evensenm
#11
Nice find! You should consider a career as a beta-tester :-)
I think this has also to do with the "interpretation" part: there is no path to interpret (if that is a correct English word...) in these cases.
I think it'd work if you passed the filename, as specified by the user (the API doesn't seem to mind if that involves a SUBST and a qualified or unqualified filename), then prepend to the result the real drive letter (only) with a colon.

For example, the API can deal with "w:\notepad.exe" (or "notepad.exe" if the current directory is w:\) and would return
Code:
\Windows\System32\notepad.exe
\Windows\notepad.exe
\Windows\winsxs\x86_microsoft-windows-notepadwin_31bf3856ad364e35_6.1.7600.16385_none_42a023025c60a33a\notepad.exe
\Windows\winsxs\x86_microsoft-windows-notepad_31bf3856ad364e35_6.1.7600.16385_none_6ef0e39ed15350e4\notepad.exe
After discovering that w: is a SUBST for c:\windows, prepend only the "c:".
 
#12
The original example is better in build 19.
Code:
v:\empty> dir /hl /m /k
2016-05-14  22:00         <DIR>    .
2016-05-14  22:00         <DIR>    ..
2016-09-07  00:05         <DIR>    foo
                                    = H:\work\empty\foo
2015-02-16  01:18         <DIR>    x64
                                    = H:\work\empty\x64
2016-05-14  21:59               0  a
                                    = H:\work\empty\a
But not with LINKS.
Code:
v:\> links v:\empty\foo
v:\empty\work\empty\foo
And not with LINKS or DIR /LH in the root of a SUBST.
Code:
v:\> links w:\notepad.exe

v:\> w:

w:\> links notepad.exe

w:\> dir /m /k /hl notepad.exe
2009-07-13  21:14         179,712  notepad.exe

w:\> v:

v:\> dir /m /k /hl w:\notepad.exe
2009-07-13  21:14         179,712  notepad.exe
 
#13
... which is odd because (given the limitations previously discussed) FindFirstFileNameW (et al.) is OK with files in the root of a SUBST. This code produces the expected four links. (Likewise if I CD to W:\ and use an unqualified file name.)
Code:
   DWORD dwLen;
   WCHAR szFile[MAX_PATH];
   HANDLE hFind = FindFirstFileNameW(L"w:\\notepad.exe", 0, &(dwLen = MAX_PATH), szFile);
   if ( hFind != NULL )
       do Printf(L"%s\r\n", szFile);
           while ( FindNextFileNameW(hFind, &(dwLen = MAX_PATH), szFile) );
   FindClose(hFind);

Don't know which programming language this is (I'm not a programmer) . As far as I can see it, you are halfway in writing your own Take Command :-)

EDIT: Which of course then should be called "FAKE Command" ( FAtica's Kernel Eplorations )
Sorry, could not resist. :-D
I mean no harm, of course


Are those (FindFirstFileNameW, FindNextFileNameW) the API's you can call with @WINAPI ?
 
#14
Don't know which programming language this is (I'm not a programmer) . As far as I can see it, you are halfway in writing your own Take Command :-)

EDIT: Which of course then should be called "FAKE Command" ( FAtica's Kernel Eplorations )
Sorry, could not resist. :-D
I mean no harm, of course


Are those (FindFirstFileNameW, FindNextFileNameW) the API's you can call with @WINAPI ?
Halfway? ... more like 1/1000000 of the way!

Those are functions in the Win32 API (for the "C" language). Don't be confused by the "32"; "Win32" is just a name. I'm quite sure those particular functions will not work with @WINAPI. For example, you could get started like this
Code:
v:\> echo %@winapi[kernel32.dll,FindFirstFileNameW,"c:\windows\notepad.exe",0,PDWORD=260,BUFFER]
\Windows\notepad.exe
But now, TCC has discarded (hope I'm wrong) the actual number that the function returned ... that number being a HANDLE to the search, and being required by the next function, FindNextFileNameW.

Perhaps a future TCC might get around this by letting pointer specs (locations in memory) be the names of environment variables where the input comes from and the output goes. Maybe something like this

set bufsize=260
set set buf="c:\windows\notepad.exe"
set FindHandle=%@winapi[kernel32.dll,FindFirstFileNameW,"c:\windows\notepad.exe",0,PDWORD=%%bufsize,BUFFER=%%buf]

Then you'd have FileHandle to use in the next function, the number of characters in the filename in %bufsize, and the name of the file in %buf.

But I'd bet (1) this would be hard and (2) it'll never make it to the top of Rex's to-do list.
 
#15
Those are functions in the Win32 API (for the "C" language). Don't be confused by the "32"; "Win32" is just a name. I'm quite sure those particular functions will not work with @WINAPI. For example, you could get started like this
Code:
v:\> echo %@winapi[kernel32.dll,FindFirstFileNameW,"c:\windows\notepad.exe",0,PDWORD=260,BUFFER]
\Windows\notepad.exe
But now, TCC has discarded (hope I'm wrong) the actual number that the function returned ... that number being a HANDLE to the search, and being required by the next function, FindNextFileNameW.
I love the width and the depth of knowledge in this forum!
Your explanation is very clear (that is: I could understand it) Thanks for elaborating!

But now, TCC has discarded (hope I'm wrong) the actual number that the function returned ... that number being a HANDLE to the search, and being required by the next function, FindNextFileNameW.

Perhaps a future TCC might get around this by letting pointer specs (locations in memory) be the names of environment variables where the input comes from and the output goes. Maybe something like this

set bufsize=260
set set buf="c:\windows\notepad.exe"
set FindHandle=%@winapi[kernel32.dll,FindFirstFileNameW,"c:\windows\notepad.exe",0,PDWORD=%%bufsize,BUFFER=%%buf]

Then you'd have FileHandle to use in the next function, the number of characters in the filename in %bufsize, and the name of the file in %buf.

But I'd bet (1) this would be hard and (2) it'll never make it to the top of Rex's to-do list.
By the sound of it (the way I understand it), the @WINAPI call starts some sort of a session and retrieves information. My guess is this handle is session-based; the next time you ask the same question, you will get another handle (like @fileopen does)
When the @WINAPI call is done, the session will be closed and the handle "deleted" (or whatever happens with handles) and cannot be used for subsequent "calls".
So I think that will be very hard to build-in, indeed.

Well, that is my theory anyway (with zero facts to prove it ..)
 
#16
By the sound of it (the way I understand it), the @WINAPI call starts some sort of a session and retrieves information. My guess is this handle is session-based; the next time you ask the same question, you will get another handle (like @fileopen does)
When the @WINAPI call is done, the session will be closed and the handle "deleted" (or whatever happens with the handle) and cannot be used for subsequent "calls".
So I think that will be very hard to build-in, indeed.
TCC is just calling the functions you specified It has no idea what you're trying to do or how the functions you call may interact with each other. As for sessions, it's more like this.

FindFirstFileNameW creates a session (a "find" session, if you like). That session is identified by a number (a HANDLE) which that function returns. In order to continue finding file names (with FindNextFileNameW) you have to identify the "find" session by giving FindNextFileNameW that handle. The HANDLE doesn't change; it continues to identify the find session until you're done (at which time you should use FindClose() on it so it doesn't hang around using 4 bytes of memory). TCC would have to let you know what that handle is so you can use it when you call FindNextFileNameW. I don't think that can be done as things are. When you use BUFFER in @WINAPI, @WINAPI[...] won't evaluate to the return value of the function; instead @WINAPI[...]'s value will be the string that the function put into the BUFFER
 
#17
I had to read it 3 times, but now I understand.
It does not seem like a good idea to open up the API's this way. Things get messy on the TCC side and things get messy on the user side (they have to "handle" (sic!) all the handles and calls. No problem when a few, selected ones are exposed (like the @fileopen -family) but in general ..

I wasn't asking for this functionality, btw. Just trying to understand how things work...