|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2011-02-27 12:05 UTC] carsten_sttgt at gmx dot de
Description:
------------
tsrm_realpath_r is having a problem if working with/inside a junction point and
"read data/list directory" is denied on this junction point.
That's the implementation for many common directories on Windows7/Vista in e.g. %USERPROFILE%, %ALLUSERSPROFILE% like "%USERPROFILE%\SendTo".
The problem is a call to CreateFile with GENERIC_READ access in tsrm_realpath_r. Well, the MSDN is writing:
> If this parameter is zero, the application can query certain metadata
> such as file, directory, or device attributes without accessing that
> file or device, even if GENERIC_READ access would have been denied.
I think "zero" is save at this point and that's what I'm doing in my patch.
Regards,
Carsten
Test script:
---------------
in the shell:
md test1
mklink /j test test1
icacls test /deny *S-1-1-0:(rd)
php -r "var_dump(realpath('test'));"
php -r "var_dump(fopen('test/test.txt', 'w'));"
Expected result:
----------------
string(31) "C:\Users\Public\Documents\test1"
resource(5) of type (stream)
Actual result:
--------------
bool(false)
Warning: fopen(test/test.txt): failed to open stream: No such file or directory
in Command line code on line 1
PatchesTSRM-tsrm_virtual_cwd.c.diff (last revision 2011-02-27 11:06 UTC by carsten_sttgt at gmx dot de)Pull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sun Oct 26 18:00:01 2025 UTC |
> I'm not sure your diagnostic is correct. It's no problem to do a: echo foo>test\test.txt but it's not possible to do a: php -r "file_put_contents('test/test.txt', 'foo');" "dir test" is not possible because "list directory" is denied. (This deny is only for the junction object itself (only this dir), but not for objects inside the junction target.) Thus, "md test1\test2 && dir test\test2" would work. > Junction or links are supported and work well. Well, it's working with a symlink, but not with a junction point. BTW: realpath() on a non existent junction target is also not working as expected. The output is the name of non existent junction point target. With a non existent symlink it's the expected false.> Junction does work very well. You have tested this? | mklink test1 nonexistent | mklink /j test2 nonexistent | php -r "var_dump(realpath('test1'));" | php -r "var_dump(realpath('test2'));" What did you expect? (You can also test it with the sample exe below) > We had similar cases where someone wanted to access stat > info from an unreadable file. It's a difference, if generic read access is denied, or data read access. This still allows me to other things with an object, e.g reading attributes or permissions... > I'm somehow not convinced that we should allow that now. PHP can't allow me, what is not allowed/possible from the ACL. It's just working wrong in this piece of code. Because: - I have full rights on e.g. test1\test.php - But PHP don't let me do anything with this file That's just wrong. Maybe we should think about what this function tsrm_realpath_r is doing. It's just resolving a relative path to an absolute one. And it's testing if the target exists. Nothing else. It's something like: | #include <windows.h> | #include <tchar.h> | #include <stdio.h> | | void _tmain(int argc, TCHAR *argv[]) { | HANDLE hFile; | TCHAR buffer[MAX_PATH]=TEXT(""); | | if (GetFullPathName(argv[1], MAX_PATH, buffer, NULL)) { | hFile = CreateFile( | buffer, 0, 0, NULL, OPEN_EXISTING, | FILE_FLAG_BACKUP_SEMANTICS, NULL | ); | if (hFile != INVALID_HANDLE_VALUE) { | _tprintf(TEXT("The full path name is: %s\n"), buffer); | CloseHandle(hFile); | exit(EXIT_SUCCESS); | } | } | _tprintf(TEXT("GetFullPathName failed (%d)\n"), GetLastError()); | exit(EXIT_FAILURE); | } (ok, this simple example does not resolve the final pathname from a symlink) Such a function must not have GENERIC_READ as DesiredAccess, because it doesn't want read any data from any object. Especially it doesn't know what I want do with this file(name) at another place. The final access control and opening the handle for the real work is done at another place. And of course. If generic access is denied from the ACL, CreateFile with "0" also fail. (you can't do things which are not allowed...)After taking a look at the stuff I've realized that there is a FindFirstFile invocation in TSRM/tsrm_virtual_cwd.c around line 852. There is a check for INVALID_HANDLE_VALUE after which "return -1" happens. If I do the following after FindFirstFile fails DWORD dw = GetLastError(); if (ERROR_ACCESS_DENIED == dw) { /* inside junction ? */ } I see that it happens for exactly the case trying to read "test\test.txt" from the initial example. A 'not found' status is given for nonexistent files A solution I'm thinking about is to traverse all the parents until the first junction, after that the junction target can be resolved and the complete real path could be built. This could be done with the check above for this specific case. Not sure if there would be impacts on dependant functionality