DIR reports meaningless SYMLINK information

Feb 12, 2019
5
0
DIR %USERPROFILE%\AppData\Local\microsoft\windowsapps shows a bunch of "SYMLINKS" though they're actually IO_REPARSE_TAG_APPEXECLINK, not SYMLINK. But I digress.

What's supposed to appear in the brackets on the right? For an actual symbolic link it appears to be the target e.g.
7/31/2019 19:01 <SYMLINK> python3.7.exe [C:\Users\howardk\AppData\Local\microsoft\windowsapps\python3.7.exe]
but there's 20+ IO_REPARSE_TAG_* definitions. What's expected for non-symlinks tags?

Do you just fill in the absolute path for the reparse point if there's an error, or it's a tag you don't recognize, or...? Current behavior seems kinda pointless.

DIR /REPARSE:TAGNAME to show APPEXECLINK etc instead of SYMLINK for all would be nice.
DIR /REPARSE:VERBOSE to show more details about the reparse point in the brackets would be nice.

Enhancement request?...
 
May 20, 2008
10,574
78
Syracuse, NY, USA
Can someone explain all this to me?

TCC v24 shows this.

Code:
c:\users\vefatica\appdata\local\microsoft\windowsapps> dir /k /m excel.exe
2019-08-31  14:02               0  excel.exe [C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe]

c:\users\vefatica\appdata\local\microsoft\windowsapps> attrib excel.exe
___A____L________  C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe
TCC v25 shows this.

Code:
c:\users\vefatica\appdata\local\microsoft\windowsapps> dir /k /m excel.exe
2019-08-31  14:02               0  excel.exe

c:\users\vefatica\appdata\local\microsoft\windowsapps> attrib excel.exe
___A_____________  C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe
CMD shows this.

Code:
C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps> dir excel.exe
 Volume in drive C is OS
 Volume Serial Number is 547B-A92D

 Directory of C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps

2019-08-31  14:02                 0 excel.exe
               1 File(s)              0 bytes
               0 Dir(s)  458,879,655,936 bytes free

C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps> attrib excel.exe
The target of the symbolic link C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe does not exist
There must be something right about this EXCEL.EXE because CMD can execute it. TCC can't (why not?).

Code:
c:\users\vefatica\appdata\local\microsoft\windowsapps> excel.exe
TCC: (Sys) The file cannot be accessed by the system.
 "C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe"
 

rconn

Administrator
Staff member
May 14, 2008
11,915
133
TCC attempts to determine the file (subsystem) type so it can execute it properly. Windows is returning an error (1920 - "The file cannot be accessed by the system"), so TCC doesn't go any further.

CMD doesn't make that call (or support all the subsystem types), and only tries to call CreateProcess (which works).

But my question is - why do you think that TCC *should* be executing that (well hidden) file?
 
May 20, 2008
10,574
78
Syracuse, NY, USA
But my question is - why do you think that TCC *should* be executing that (well hidden) file?
Which well hidden file?

"C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe" is the one TCC gets from %PATH.

It's a IO_REPARSE_TAG_APPEXECLINK.

That link points to C:\Program Files\WindowsApps\Microsoft.Office.Desktop.Excel_16051.11929.20254.0_x86__8wekyb3d8bbwe\Office16\EXCEL.exe.
 
May 20, 2008
10,574
78
Syracuse, NY, USA
You can avoid the 1920 error like this (for example).

Code:
 HANDLE hFile = CreateFile(L"c:\\users\\vefatica\\appdata\\local\\microsoft\\windowsapps\\excel.exe", 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL);
With some defs and typedefs you can find HERE (or you may have already) you can get the target of the link. For example (this is crude):

Code:
    HANDLE hFile = CreateFile(L"c:\\users\\vefatica\\appdata\\local\\microsoft\\windowsapps\\excel.exe", 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL);

    BYTE buf[4096];
    DWORD dwReturned;

    BOOL bOK = DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, 4096, &dwReturned, NULL);

    if ( bOK )
        wprintf(L"Returned: %lu bytes\n", dwReturned);
    else
    {
        wprintf(L"DeviceIoControl failed; last error = %lu\n", GetLastError());
        return -1;
    }

    REPARSE_DATA_BUFFER *p = (REPARSE_DATA_BUFFER*) buf;

    wprintf(L"Reparse tag: %x\n", p->ReparseTag);

    WCHAR *q = (WCHAR*) p->AppExecLinkReparseBuffer.StringList; // multi-string list

    for ( INT i=0; i<p->AppExecLinkReparseBuffer.StringCount; i++ )
    {
        wprintf(L"String %d: %s\n", i, q);
        while ( *q++ != 0 );
    }
It produces this.

Code:
Returned: 420 bytes
Reparse tag: 8000001b
String 0: Microsoft.Office.Desktop_8wekyb3d8bbwe
String 1: Microsoft.Office.Desktop_8wekyb3d8bbwe!Excel
String 2: C:\Program Files\WindowsApps\Microsoft.Office.Desktop.Excel_16051.11929.20254.0_x86__8wekyb3d8bbwe\Office16\EXCEL.exe
 

rconn

Administrator
Staff member
May 14, 2008
11,915
133
Which well hidden file?

"C:\Users\vefatica\AppData\Local\Microsoft\WindowsApps\excel.exe" is the one TCC gets from %PATH.

It's a IO_REPARSE_TAG_APPEXECLINK.

That link points to C:\Program Files\WindowsApps\Microsoft.Office.Desktop.Excel_16051.11929.20254.0_x86__8wekyb3d8bbwe\Office16\EXCEL.exe.
How is it not? An undocumented file type in a hidden/system directory returning a Windows error?
 
May 20, 2008
10,574
78
Syracuse, NY, USA
How is it not? An undocumented file type in a hidden/system directory returning a Windows error?
I'm not sure what to say. I didn't invent this scenario. I'm trying to help. Microsoft chose to put those links (of undocumented type) in the PATH, so that's what TCC finds first when I give a command like "excel". TCC can't open the file and it gives up. I wanted excel to run and it doesn't.
 
May 20, 2008
10,574
78
Syracuse, NY, USA
@vefatica

Maybe a BIT more info about the IO_REPARSE_TAG_APPEXECLINK here (if you need):

https://tyranidslair.blogspot.com/2019/09/overview-of-windows-execution-aliases.html
Interesting! If that's correct, then the info I found is a bit inaccurate. I found this:

Code:
        // Structure for IO_REPARSE_TAG_APPEXECLINK
        struct
        {
            ULONG StringCount;                  // Number of the strings in the StringList, separated by '\0'
            WCHAR StringList[1];                // Multistring (strings separated by '\0', terminated by '\0\0')
        } AppExecLinkReparseBuffer;
The strings acutally number 4 and there's not a double NUL at the end. And the ULONG 3 at the beginning of the data may well be a version number. This can be seen with Windows's FSUTIL.EXE.

Code:
c:\users\vefatica\appdata\local\microsoft\windowsapps> fsutil reparsepoint query excel.exe
Reparse Tag Value : 0x8000001b
Tag value: Microsoft

Reparse Data Length: 0x0000019c
Reparse Data:
0000:  03 00 00 00 4d 00 69 00  63 00 72 00 6f 00 73 00  ....M.i.c.r.o.s.
0010:  6f 00 66 00 74 00 2e 00  4f 00 66 00 66 00 69 00  o.f.t...O.f.f.i.
0020:  63 00 65 00 2e 00 44 00  65 00 73 00 6b 00 74 00  c.e...D.e.s.k.t.
0030:  6f 00 70 00 5f 00 38 00  77 00 65 00 6b 00 79 00  o.p._.8.w.e.k.y.
0040:  62 00 33 00 64 00 38 00  62 00 62 00 77 00 65 00  b.3.d.8.b.b.w.e.
0050:  00 00 4d 00 69 00 63 00  72 00 6f 00 73 00 6f 00  ..M.i.c.r.o.s.o.
0060:  66 00 74 00 2e 00 4f 00  66 00 66 00 69 00 63 00  f.t...O.f.f.i.c.
0070:  65 00 2e 00 44 00 65 00  73 00 6b 00 74 00 6f 00  e...D.e.s.k.t.o.
0080:  70 00 5f 00 38 00 77 00  65 00 6b 00 79 00 62 00  p._.8.w.e.k.y.b.
0090:  33 00 64 00 38 00 62 00  62 00 77 00 65 00 21 00  3.d.8.b.b.w.e.!.
00a0:  45 00 78 00 63 00 65 00  6c 00 00 00 43 00 3a 00  E.x.c.e.l...C.:.
00b0:  5c 00 50 00 72 00 6f 00  67 00 72 00 61 00 6d 00  \.P.r.o.g.r.a.m.
00c0:  20 00 46 00 69 00 6c 00  65 00 73 00 5c 00 57 00   .F.i.l.e.s.\.W.
00d0:  69 00 6e 00 64 00 6f 00  77 00 73 00 41 00 70 00  i.n.d.o.w.s.A.p.
00e0:  70 00 73 00 5c 00 4d 00  69 00 63 00 72 00 6f 00  p.s.\.M.i.c.r.o.
00f0:  73 00 6f 00 66 00 74 00  2e 00 4f 00 66 00 66 00  s.o.f.t...O.f.f.
0100:  69 00 63 00 65 00 2e 00  44 00 65 00 73 00 6b 00  i.c.e...D.e.s.k.
0110:  74 00 6f 00 70 00 2e 00  45 00 78 00 63 00 65 00  t.o.p...E.x.c.e.
0120:  6c 00 5f 00 31 00 36 00  30 00 35 00 31 00 2e 00  l._.1.6.0.5.1...
0130:  31 00 31 00 39 00 32 00  39 00 2e 00 32 00 30 00  1.1.9.2.9...2.0.
0140:  32 00 35 00 34 00 2e 00  30 00 5f 00 78 00 38 00  2.5.4...0._.x.8.
0150:  36 00 5f 00 5f 00 38 00  77 00 65 00 6b 00 79 00  6._._.8.w.e.k.y.
0160:  62 00 33 00 64 00 38 00  62 00 62 00 77 00 65 00  b.3.d.8.b.b.w.e.
0170:  5c 00 4f 00 66 00 66 00  69 00 63 00 65 00 31 00  \.O.f.f.i.c.e.1.
0180:  36 00 5c 00 45 00 58 00  43 00 45 00 4c 00 2e 00  6.\.E.X.C.E.L...
0190:  65 00 78 00 65 00 00 00  30 00 00 00              e.x.e...0...
 
May 20, 2008
10,574
78
Syracuse, NY, USA
Yes, I can't understand the EOF with the 0x30 (ASCII = 0) between ... definitive not 2 escaped 0 (null, byte 0) (ASCII = NUL) in a row ...
The whole thing is Unicode, so the '0' (actually L'0' in C) is 0x3000. And the 0x0000 after it is the Unicode NUL terminating the fourth and final Unicode string (L"0").

I imagine that whole thing came right out of the file system. Conveniences like EOFs and double NULs at the end of multiple strings are probably wasteful. All the user needs to know (and does) is the number of bytes of data.