Hi,
I have written a patch to add basic UNC pathname support which tries to handle UNC pathnames according to the MSDN spec.
The patch satisfies some of the UNC pathname tests for GetLongPathNameA, but is actually intended to fix GetLongPathNameW.
I am not entirely satisfied with the patch, and would appreciate some comments.
Some issues:
1) Paths prefixed by '\?' require GetLongPathName to handle strings of length 32767. I don't like the fact that the patch creates two such large strings on the local stack, should the memory be dynamically allocated only when needed?
2) The handling of shares \hostname\C$ is implemented, but no other shares are handled. And even in this case, only the host on which wine is executed is handled, no other hosts are handled. I have read that Windows 7 disables this share by default. So should wine be handling this particular URL, or should more general sharing be implemented?
The patch is included as an attachment to this message.
Kind regards Alexandre
Hi Alexandre,
On 12/11/2009 09:02 AM, Alexandre Hardy wrote:
Hi,
I have written a patch to add basic UNC pathname support which tries to handle UNC pathnames according to the MSDN spec.
The patch satisfies some of the UNC pathname tests for GetLongPathNameA, but is actually intended to fix GetLongPathNameW.
I am not entirely satisfied with the patch, and would appreciate some comments.
Some issues:
- Paths prefixed by '\?' require GetLongPathName to handle
strings of length 32767. I don't like the fact that the patch creates two such large strings on the local stack, should the memory be dynamically allocated only when needed?
Dynamic is preferred. We maybe needs some tests to see what Windows does if we pass longer strings (without the '\?') to the A- and W-version and variants of this (including '\?').
- The handling of shares \hostname\C$ is implemented, but no other
shares are handled. And even in this case, only the host on which wine
We need a more general way of handling UNC paths. We can't just have this single one (only local host and only C: drive) to satisfy the tests. This probably goes beyond GetLongPathName though.
is executed is handled, no other hosts are handled. I have read that Windows 7 disables this share by default. So should wine be handling this particular URL, or should more general sharing be implemented?
The test results on test.winehq.org tells us a different thing though. It looks like this is only the case for the Home versions of Windows.
And (again) I think we need more general sharing to be implemented not just one case to have a test succeed.
The patch is included as an attachment to this message.
Kind regards Alexandre
For what bug was this?
Hi Paul,
I guess it could fall under bug #19807. I was actually testing it with Nokia Ovi Installer.
So, If I take out the \host\C$ part, then this patch won't change which tests are passed. So I think we need to add GetLongPathNameW tests. I'm going to give it a bash...
I would prefer not to work further on UNC pathnames that refer to shares, I would rather try to solve other problems...
Kind regards Alexandre
On Fri, Dec 11, 2009 at 10:32 AM, Paul Vriens paul.vriens.wine@gmail.com wrote:
Hi Alexandre,
On 12/11/2009 09:02 AM, Alexandre Hardy wrote:
Hi,
I have written a patch to add basic UNC pathname support which tries to handle UNC pathnames according to the MSDN spec.
The patch satisfies some of the UNC pathname tests for GetLongPathNameA, but is actually intended to fix GetLongPathNameW.
I am not entirely satisfied with the patch, and would appreciate some comments.
Some issues:
- Paths prefixed by '\?' require GetLongPathName to handle
strings of length 32767. I don't like the fact that the patch creates two such large strings on the local stack, should the memory be dynamically allocated only when needed?
Dynamic is preferred. We maybe needs some tests to see what Windows does if we pass longer strings (without the '\?') to the A- and W-version and variants of this (including '\?').
- The handling of shares \hostname\C$ is implemented, but no other
shares are handled. And even in this case, only the host on which wine
We need a more general way of handling UNC paths. We can't just have this single one (only local host and only C: drive) to satisfy the tests. This probably goes beyond GetLongPathName though.
is executed is handled, no other hosts are handled. I have read that Windows 7 disables this share by default. So should wine be handling this particular URL, or should more general sharing be implemented?
The test results on test.winehq.org tells us a different thing though. It looks like this is only the case for the Home versions of Windows.
And (again) I think we need more general sharing to be implemented not just one case to have a test succeed.
The patch is included as an attachment to this message.
Kind regards Alexandre
For what bug was this?
-- Cheers,
Paul.
Hi Alexandre,
On 12/11/2009 09:49 AM, Alexandre Hardy wrote:
Hi Paul,
I guess it could fall under bug #19807. I was actually testing it with Nokia Ovi Installer.
(Please bottom post on wine-devel).
So to fix 19807 you need to handle the '\?' cases only so it seems?
Remember that GetLongPathNameA is forward to GetLongPathNameW and from the bug report one can't see which one is actually called. So you need a log file with either a +file trace or change the TRACE() in GetLongPathNameA to a FIXME().
Hi Paul,
Stupid question: What do you mean by bottom post? I have added wine-devel as a CC to these messages, is that sufficient?
According to my log it seems to be a GetLongPathNameW.
GetLongPathNameA does not exhibit the same problems as GetLongPathNameW. So GetLongPathNameA(path, NULL, 0) succeeds, while GetLongPathNameW(path, NULL, 0) segfaults. This is because GetLongPathNameA provides its own not NULL buffer to GetLongPathNameW.
It also seems to me that only GetLongPathNameW is required to handle UNC pathnames that begin with '\?' (at least, that is how I understand the MSDN).
Kind regards Alexandre
On Fri, Dec 11, 2009 at 10:56 AM, Paul Vriens paul.vriens.wine@gmail.com wrote:
Hi Alexandre,
On 12/11/2009 09:49 AM, Alexandre Hardy wrote:
Hi Paul,
I guess it could fall under bug #19807. I was actually testing it with Nokia Ovi Installer.
(Please bottom post on wine-devel).
So to fix 19807 you need to handle the '\?' cases only so it seems?
Remember that GetLongPathNameA is forward to GetLongPathNameW and from the bug report one can't see which one is actually called. So you need a log file with either a +file trace or change the TRACE() in GetLongPathNameA to a FIXME().
-- Cheers,
Paul.
On 12/11/2009 10:03 AM, Alexandre Hardy wrote:
Hi Paul,
Stupid question: What do you mean by bottom post? I have added wine-devel as a CC to these messages, is that sufficient?
http://en.wikipedia.org/wiki/Posting_style#Bottom-posting
According to my log it seems to be a GetLongPathNameW.
GetLongPathNameA does not exhibit the same problems as GetLongPathNameW. So GetLongPathNameA(path, NULL, 0) succeeds, while GetLongPathNameW(path, NULL, 0) segfaults. This is because GetLongPathNameA provides its own not NULL buffer to GetLongPathNameW.
It also seems to me that only GetLongPathNameW is required to handle UNC pathnames that begin with '\?' (at least, that is how I understand the MSDN).
Well, the tests show that the A-version also handles this case.
Hi Paul,
On Fri, Dec 11, 2009 at 11:09 AM, Paul Vriens paul.vriens.wine@gmail.com wrote:
Well, the tests show that the A-version also handles this case.
Okay, no problem there, but since the edge cases for GetLongPathNameW are not tested by GetLongPathNameA, how about adding a few of these tests?
A proposed patch with such tests is attached.
Thanks for the friendly advice!
Kinf regards Alexandre
A proposed patch with such tests is attached.
There is a bug, the call to GetShortPathName should be GetShortPathNameW(tempfile, shortpath + 4, MAX_PATH - 4);
Kind regards Alexandre
On 12/11/2009 10:57 AM, Alexandre Hardy wrote:
@@ -1067,6 +1067,12 @@ static void test_GetLongPathNameW(void) { DWORD length; WCHAR empty[MAX_PATH];
WCHAR tempfile[MAX_PATH];
WCHAR longpath[MAX_PATH];
WCHAR shortpath[MAX_PATH];
WCHAR longfilename[] = {'l', 'o', 'n', 'g', 'f', 'i', 'l', 'e', 'n', 'a', 'm', 'e', '.', 'l', 'o', 'n', 'g', 'e', 'x', 't', 0};
WCHAR uncprefix[] = {'\', '\' , '?', '\', 0};
HANDLE file;
/* Not present in all windows versions */ if(pGetLongPathNameW)
@@ -1086,6 +1092,29 @@ static void test_GetLongPathNameW(void) length = pGetLongPathNameW(empty,NULL,0); ok(0==length,"GetLongPathNameW returned %d but expected 0\n",length); ok(GetLastError()==ERROR_PATH_NOT_FOUND,"GetLastError returned %d but expected ERROR_PATH_NOT_FOUND\n",GetLastError());
- GetTempPathW(MAX_PATH, tempfile);
- lstrcatW(tempfile, longfilename);
- file = CreateFileW(tempfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- CloseHandle(file);
- memset(longpath, 0, MAX_PATH * sizeof(WCHAR));
Don't think this memset makes sense as you do lstrcpyW after that (and yes it does not make sense in the A-test I did either ;) ).
- lstrcpyW(longpath, uncprefix);
- lstrcatW(longpath, tempfile);
- length = pGetLongPathNameW(longpath,NULL,0);
- todo_wine
- ok(lstrlenW(longpath) + 1==length,"GetLongPathNameW returned %d but expected 0\n",length);
You're saying that length should be 0 but the test tests something else.
- memset(shortpath, 0, MAX_PATH * sizeof(WCHAR));
- memset(longpath, 0, MAX_PATH * sizeof(WCHAR));
You're setting longpath but it's not used anymore after that.
- lstrcpyW(shortpath, uncprefix);
- GetShortPathNameW(shortpath + 4, tempfile, MAX_PATH - 4);
- length = pGetLongPathNameW(shortpath,NULL,0);
This doesn't make sense to me. You are fill tempfile but not using it afterwards and then you call pGetLongPathNameW with NULL?
- todo_wine
- ok(lstrlenW(longpath) + 5==length,"GetLongPathNameW returned %d but expected 0\n",length);
- DeleteFileW(tempfile); } }
These are added tests but Wine crashes right now with these?
Hi Paul,
Don't think this memset makes sense as you do lstrcpyW after that (and yes it does not make sense in the A-test I did either ;) ).
Okay, that can be removed...
You're saying that length should be 0 but the test tests something else.
WIll correct that (copy and paste -> I was in too much of a hurry)
- memset(shortpath, 0, MAX_PATH * sizeof(WCHAR));
- memset(longpath, 0, MAX_PATH * sizeof(WCHAR));
I should remove that as well :)
You're setting longpath but it's not used anymore after that.
- lstrcpyW(shortpath, uncprefix);
- GetShortPathNameW(shortpath + 4, tempfile, MAX_PATH - 4);
- length = pGetLongPathNameW(shortpath,NULL,0);
This doesn't make sense to me. You are fill tempfile but not using it afterwards and then you call pGetLongPathNameW with NULL?
Yes, sorry, that should be GetShortPathNameW(tempfile, shortpath + 4, MAX_PATH - 4);
These are added tests but Wine crashes right now with these?
Yes, wine crashes right now with these.
Kind regards Alexandre
Hi Paul,
Thanks for the help with the tests. Here is a new set of tests. I'll send the patch that makes these tests succeed shortly. I would appreciate it if you could have a look at it.
Kind regards Alexandre
On Fri, Dec 11, 2009 at 12:25 PM, Alexandre Hardy alexandre.hardy@gmail.com wrote:
Hi Paul,
Don't think this memset makes sense as you do lstrcpyW after that (and yes it does not make sense in the A-test I did either ;) ).
Okay, that can be removed...
You're saying that length should be 0 but the test tests something else.
WIll correct that (copy and paste -> I was in too much of a hurry)
- memset(shortpath, 0, MAX_PATH * sizeof(WCHAR));
- memset(longpath, 0, MAX_PATH * sizeof(WCHAR));
I should remove that as well :)
You're setting longpath but it's not used anymore after that.
- lstrcpyW(shortpath, uncprefix);
- GetShortPathNameW(shortpath + 4, tempfile, MAX_PATH - 4);
- length = pGetLongPathNameW(shortpath,NULL,0);
This doesn't make sense to me. You are fill tempfile but not using it afterwards and then you call pGetLongPathNameW with NULL?
Yes, sorry, that should be GetShortPathNameW(tempfile, shortpath + 4, MAX_PATH - 4);
These are added tests but Wine crashes right now with these?
Yes, wine crashes right now with these.
Kind regards Alexandre
--
Alexandre Hardy http://www.ahardy.za.net
Hi Paul,
Here is the patch for GetLongPathNameW. Would you mind looking at it to see if it makes sense?
(Forgot to bottom post in the last message :-( )
Kind regards Alexandre
On 12/11/2009 02:00 PM, Alexandre Hardy wrote:
DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen ) {
- WCHAR tmplongpath[MAX_PATHNAME_LEN];
- LPWSTR tmplongpath; LPCWSTR p; DWORD sp = 0, lp = 0; DWORD tmplen;
@@ -309,13 +310,44 @@ DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen return 0; }
- if (!(tmplongpath = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, MAX_LONGPATHNAME_LEN * sizeof(WCHAR) ))) {
SetLastError( ERROR_OUTOFMEMORY );
return 0;
- }
This is not really dynamic, is it? You are still allocating that same amount. Now, I'm not sure how to fix this as you need some way of calculating the needed length in any case. Is that cast necessary btw?
Now as I understand the function it can only be a maximum of 32767 when you use '\?', right? If so then this allocation should only be needed in the '\?' case. This however is something you have to add tests for as well. So create a path > MAX_PATH (if that's possible as I guess the path/file has to exist as well) and see what is returned on Windows.
As you are now covering the '\?' case the ERR should go down as well and should maybe be a FIXME now?
Hi Paul,
On Fri, Dec 11, 2009 at 3:20 PM, Paul Vriens paul.vriens.wine@gmail.com wrote:
- if (!(tmplongpath = (WCHAR *)HeapAlloc( GetProcessHeap(), 0,
MAX_LONGPATHNAME_LEN * sizeof(WCHAR) ))) {
- SetLastError( ERROR_OUTOFMEMORY );
- return 0;
- }
This is not really dynamic, is it? You are still allocating that same amount. Now, I'm not sure how to fix this as you need some way of calculating the needed length in any case. Is that cast necessary btw?
It is not the same amount of memory allocated as previously. Before it was somewhere in the order of 255 WCHARS, now it is 32767 WCHARS. Although I can allocate this statically, I am a little concerned that there may be too much pressure on the available stack space. Not sure about this though. The value can't really be determined dynamically, I need enough memory to store whatever path may be returned. (Similar to GetLongPathNameA).
Now as I understand the function it can only be a maximum of 32767 when you use '\?', right? If so then this allocation should only be needed in the '\?' case. This however is something you have to add tests for as well. So create a path > MAX_PATH (if that's possible as I guess the path/file has to exist as well) and see what is returned on Windows.
Actually, I'm not certain that the maximum is not always 32767 WCHARS. The MSDN states that GetLongPathNameA is limited to MAX_PATH characters, and that you should use "\?" to increase the limit to 32767 characters. I'm not entirely sure what GetLongPathNameW has as a limit.
As you are now covering the '\?' case the ERR should go down as well and should maybe be a FIXME now?
Share names are still not covered. So it is a slite improvement for UNC pathnames, but not a complete improvement. I'll follow your guidelines on this point.
Kind regards Alexandre
On 12/11/2009 02:40 PM, Alexandre Hardy wrote:
Now as I understand the function it can only be a maximum of 32767 when you use '\?', right? If so then this allocation should only be needed in the '\?' case. This however is something you have to add tests for as well. So create a path> MAX_PATH (if that's possible as I guess the path/file has to exist as well) and see what is returned on Windows.
Actually, I'm not certain that the maximum is not always 32767 WCHARS. The MSDN states that GetLongPathNameA is limited to MAX_PATH characters, and that you should use "\?" to increase the limit to 32767 characters. I'm not entirely sure what GetLongPathNameW has as a limit.
So that's why we need the tests ;)
On 12/11/2009 01:58 PM, Alexandre Hardy wrote:
- GetTempPathW(MAX_PATH, tempfile);
- lstrcatW(tempfile, longfilename);
- file = CreateFileW(tempfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- CloseHandle(file);
- lstrcpyW(longpath, uncprefix);
- lstrcatW(longpath, tempfile);
- length = pGetLongPathNameW(longpath,NULL,0);
- todo_wine
- ok(lstrlenW(longpath) + 1==length,"GetLongPathNameW returned %d but expected %d\n",length, lstrlenW(longpath) + 1);
How can you be sure about longpath being actually a long path? Maybe should do a proper call to pGetLongPathNameW just to be sure you're starting point is correct.
I say this as on my W2K3 box I get:
tempfile : (C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\longfilename.longext)
And that results in:
path.c:1109: Test failed: GetLongPathNameW returned 85 but expected 60 path.c:1115: Test failed: GetLongPathNameW returned 85 but expected 60
(linenumbers don't match because I add some trace()'s)
- lstrcpyW(shortpath, uncprefix);
- GetShortPathNameW(tempfile, shortpath + 4, MAX_PATH - 4);
- length = pGetLongPathNameW(shortpath,NULL,0);
- todo_wine
- ok(lstrlenW(longpath) + 1==length,"GetLongPathNameW returned %d but expected %d\n",length, lstrlenW(longpath) + 1);
- todo_wine
- length = pGetLongPathNameW(shortpath,temppath,MAX_PATH);
- ok(lstrlenW(longpath)==length,"GetLongPathNameW returned %d but expected %d\n",length, lstrlenW(longpath));
If longpath is indeed a long path you could compare it with temppath?
- DeleteFileW(tempfile);
Hi Paul,
On Fri, Dec 11, 2009 at 3:38 PM, Paul Vriens paul.vriens.wine@gmail.com wrote:
How can you be sure about longpath being actually a long path? Maybe should do a proper call to pGetLongPathNameW just to be sure you're starting point is correct.
Hmm, I assumed that it was. Bad assumption!
If longpath is indeed a long path you could compare it with temppath?
Yes, I can do that!
Updates to follow.
Kind regards Alexandre