Bug 106

Summary: 1Cv77: Не работает определение существования каталога
Product: WINE@Etersoft Reporter: Vitaly Lipatov <lav>
Component: Файловые операцииAssignee: Константин Кондратюк <kondratyuk>
Status: CLOSED FIXED QA Contact: Денис Баранов <baraka>
Severity: normal    
Priority: P2 CC: baraka, ivan
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Whiteboard:
Заявки RT: Связано с:
Дата напоминания:
Bug Depends on: 59, 129, 901    
Bug Blocks: 42, 584, 3589    
Attachments: 1C обработка

Description Vitaly Lipatov 2006-04-26 19:26:10 MSD
Нужно проверить функционирование следующего кода: 
 
	Если ФС.СуществуетФайл("C:\WINDOWS\")=0 тогда  
		Сообщить("Нет каталога");  
	шначе  
		Сообщить("Есть каталог");  
	КонецЕсли;
Comment 1 Константин Петров 2006-04-27 13:19:48 MSD
Тестирование показало разлисие в результатах функции ФС.СуществуетФайл:

Строка              Windows              Wine
C:                  Есть                 Нет
C:\                 Нет                  Нет
С                   Нет                  Нет
COM                 Нет                  Нет
COM:                Нет                  Нет
COM1                Есть                 Нет
NUL                 Есть                 Нет
PRN                 Есть                 Нет
AUX                 Есть                 Нет
COM9                Есть                 Нет
C:\Program files    Есть                 Есть
C:\Program files\   Есть                 Нет
C:\windows\         Есть                 Нет
C:\windows          Есть                 Есть
C:\windows\notepad.exe   Есть            Есть
C:\windows\notepad.exe\  Есть            Нет
AUX\                Есть                 Нет
LPT9\               Есть                 Нет


Comment 2 Константин Петров 2006-04-27 15:02:54 MSD
тестирование функции FindFirstFileExW с параметром fSearchOp=FindSearchNameMatch 
обнаружило только различия в:
Путь            Windows            Wine
С:              есть (в cFileName  нет
                показывает
                название текущего
                каталога)
NUL             есть               нет
PRN             есть               нет
AUX             есть               нет
COM9            есть               нет
+ остальные Дос-устройства
Comment 3 Константин Петров 2006-05-08 21:01:42 MSD
При тестированиии была допущена ошибка.
Конфигурация Wine соответствовала Windows 98, а сравнение проводилось с Windows 
2003.
После нахождения этой ошибки выяснилось, что в Windows 95\98\Me пути со знаком 
'\' на конце не определялись. В последующих версиях Windows (NT4 SP5, 2000, XP, 
2003) такие пути определяются и аналогичны пути без '\' на конце.

Поскольку для нужд 1С в Wine используется режим Windows 98 -> выше указанная 
ситуация является корректной
Comment 4 Vitaly Lipatov 2006-05-09 01:09:30 MSD
Принято решение изменить поведение WINE на соответствующее W2K.
Comment 5 Константин Петров 2006-05-11 12:57:12 MSD
Отредактирован тестировщик для функции FindFirstFileExW.
Конечное тестирование показало несоответствие определения путей:
   Путь                В Win98 существует (1), не существует (0)
{"C:",                                           1},
{"NUL",                                          1},
{"AUX",                                          1},
{"PRN",                                          1},
{"NUL:   ",                                      1},
{"NUL:.txt",                                     1},
{"NUL:357jhg",                                   1},
{"NUL:::",                                       1},
{"lpt1:",                                        1},
{"CoM4:",                                        1},
{"lpt9:",                                        1},
{"con:",                                         1},
{"\\LPT8",                                       1},
{"\\NUL",                                        1},
{"/NUL::::..  11,,354--++",                      1},
{"\\NUL::::..  11,,354--++",                     1},
{"e:NUl",                                        1},
{"e:nuL::::..  11,,354--++",                     1},
{"c:nuL::::..  11,,354--++",                     1},
{"C:///nuL::::..  11,,354--++",                  1},
{"c:///prograM fIleS\\LpT8::::..  11,,354--++",  1},
{"c:NUL",                                        1},
{"c:\\nul::",                                    1},
{"c:prn:aaa",                                    1},
{"c:PRN:.txt",                                   1},
{"c:aux:.txt...",                                1},
{"c:prn:.txt:",                                  1},
{"c:nul:aaa",                                    1},
{"c:com5:",                                      1},
{"c:nul. . . :",                                 1},
{"c:nul . . :",                                  1},
{"c:prn     ",                                   1},
{"c:prn.......",                                 1},
{"c:prn... ...",                                 1},
{"c:NUL  ....  ",                                1}

К сожалению не была проверена функция RtlIsDosDeviceName_U на распознавание Dos-
путей и все исправления вводились исходя из выше приведенного теста. Патч для 
ntdll/path.c:
Index: path.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/path.c,v
retrieving revision 1.37
diff -u -u -r1.37 path.c
--- path.c      28 Nov 2005 20:10:41 -0000      1.37
+++ path.c      11 May 2006 08:37:39 -0000
@@ -339,27 +339,25 @@
     }

     end = dos_name + strlenW(dos_name) - 1;
-    if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' */

     /* find start of file name */
     for (start = end; start >= dos_name; start--)
     {
         if (IS_SEPARATOR(start[0])) break;
         /* check for ':' but ignore if before extension (for things like NUL:.
txt) */
-        if (start[0] == ':' && start[1] != '.') break;
     }
     start++;

     /* remove extension */
-    if ((p = strchrW( start, '.' )))
+    if ((p = strchrW( start, ':' )))
     {
         end = p - 1;
-        if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' 
before extension */
+        while (end >= dos_name && ((*end == ' ') || (*end == '.') || (*end == 
':'))) end--;  /* remove trailing ':', ' ', '.' before extension */
     }
     else
     {
-        /* no extension, remove trailing spaces */
-        while (end >= dos_name && *end == ' ') end--;
+        /* no extension, remove trailing ' ', '.' */
+        while (end >= dos_name && ((*end == ' ') || (*end == '.'))) end--;
     }

     /* now we have a potential device name between start and end, check it */

ш патч для kernel/file.c:

Index: file.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/file.c,v
retrieving revision 1.51
diff -u -u -r1.51 file.c
--- file.c      3 Apr 2006 19:46:58 -0000       1.51
+++ file.c      11 May 2006 08:54:19 -0000
@@ -1546,12 +1546,14 @@
                                 LPVOID data, FINDEX_SEARCH_OPS search_op,
                                 LPVOID filter, DWORD flags)
 {
-    WCHAR *mask, *p;
+    WCHAR *mask, *p, *tmp;
     FIND_FIRST_INFO *info = NULL;
     UNICODE_STRING nt_name;
     OBJECT_ATTRIBUTES attr;
     IO_STATUS_BLOCK io;
     NTSTATUS status;
+    ULONG dos_device;
+    DWORD sz;

     TRACE("%s %d %p %d %p %lx\n", debugstr_w(filename), level, data, search_op, 
filter, flags);

@@ -1573,38 +1575,136 @@
         return INVALID_HANDLE_VALUE;
     }

-    if (!mask || !*mask)
+    if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info))))
     {
-        SetLastError( ERROR_FILE_NOT_FOUND );
-        goto error;
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        RtlFreeUnicodeString( &nt_name );
+        return INVALID_HANDLE_VALUE;
     }

-    if (!(info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info))))
+    if(RtlDetermineDosPathNameType_U( filename ) == RELATIVE_DRIVE_PATH )
     {
-        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
-        goto error;
+        if (!(tmp = RtlAllocateHeap(GetProcessHeap(), 0, MAX_PATH*sizeof(WCHAR
))))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            goto error;
+        }
+        if(!(sz = GetCurrentDirectoryW(MAX_PATH, tmp)))
+        {
+            RtlFreeHeap( GetProcessHeap(), 0, tmp );
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            goto error;
+        }
+        /* for current DRIVE or NOT */
+        if( nt_name.Buffer[4] == tmp[0])
+        {
+            tmp[sz] = '\\';
+            sz++;
+            memcpy((void *)(tmp + sz), (void *)(filename + 2), 
(strlenW(filename)-1)*sizeof(WCHAR) );
+        }
+        else
+        {
+            memcpy((void *)tmp, (void *)filename, 2*sizeof(WCHAR) );
+            tmp[2] = '\\';
+            memcpy((void *)(tmp + 3), (void *)(filename + 2), 
(strlenW(filename)-1)*sizeof(WCHAR) );
+        }
+        dos_device = RtlIsDosDeviceName_U( tmp );
+    }
+    else
+    {
+        if (!(tmp = RtlAllocateHeap(GetProcessHeap(), 0, (strlenW(filename) + 
1)*sizeof(WCHAR))))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            goto error;
+        }
+        memcpy(tmp, filename, (strlenW(filename) + 1)*sizeof(WCHAR) );
     }

-    if (!RtlCreateUnicodeString( &info->mask, mask ))
+
+    if ((!mask && !dos_device) || (mask && !*mask))
     {
-        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        SetLastError( ERROR_FILE_NOT_FOUND );
         goto error;
     }

-    /* truncate dir name before mask */
-    *mask = 0;
-    nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR);
-
-    /* check if path is the root of the drive */
-    info->is_root = FALSE;
-    p = nt_name.Buffer + 4;  /* skip \ш\ prefix */
-    if (p[0] && p[1] == ':')
-    {
-        p += 2;
-        while (*p == '\\') p++;
-        info->is_root = (*p == 0);
+    /*If there is a DOS device*/
+    if(!mask && dos_device)
+    {
+        WIN32_FIND_DATAW *d = data;
+
+        /*LOWORD(dos_device) - quantity of symbols in the DOS device name (NUL, 
LPT1,...)
+          filename + HIWORD(dos_device) - points to the Dos device name's 
beginning */
+        /*Initialize info.mask with NULL*/
+        if (!( RtlInitUnicodeStringEx( &info->mask, NULL ) == STATUS_SUCCESS) )
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            goto error;
+        }
+        /* Free old nt_name */
+        RtlFreeUnicodeString( &nt_name );
+
+        info->is_root = FALSE;
+        /*Initialize nt_name*/
+        if(HIWORD(dos_device))
+        {
+            nt_name.Length        = 4*sizeof(WCHAR) + HIWORD(dos_device);
+            nt_name.MaximumLength = nt_name.Length + sizeof(WCHAR);
+        }
+        /* for "PRN", "NUL" & etc. */
+        else
+        {
+            nt_name.Length        = 5*sizeof(WCHAR);
+            nt_name.MaximumLength = nt_name.Length + sizeof(WCHAR);
+        }
+
+        if (!(nt_name.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, nt_name.
MaximumLength)))
+        {
+            RtlFreeUnicodeString( &info->mask );
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            HeapFree( GetProcessHeap(), 0, info );
+            return INVALID_HANDLE_VALUE;
+        }
+        /*convert to NT' style path*/
+        nt_name.Buffer[0] = '\\';
+        nt_name.Buffer[1] = nt_name.Buffer[2] = '?';
+        nt_name.Buffer[3] = '\\';
+        memcpy( (void *)(nt_name.Buffer + 4), tmp, HIWORD(dos_device));
+        nt_name.Buffer[nt_name.Length/sizeof(WCHAR)] = 0;
+        /* for "\NUL" & for "PRN", "NUL" & etc. */
+        if(!HIWORD(dos_device) || nt_name.Buffer[4] == '\\')
+        {
+            nt_name.Buffer[4] = '/';
+        };
+
+        memset(d, 0, sizeof(*d));
+        d->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
+        memcpy( d->cFileName, tmp + HIWORD(dos_device)/sizeof(WCHAR), 
LOWORD(dos_device));
+
+        RtlFreeHeap( GetProcessHeap(), 0, tmp );
     }
+    else
+    {
+        if (!RtlCreateUnicodeString( &info->mask, mask ))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            goto error;
+        }

+        /* truncate dir name before mask */
+        *mask = 0;
+        nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR);
+
+        /* check if path is the root of the drive */
+        info->is_root = FALSE;
+        p = nt_name.Buffer + 4;  /* skip \ш\ prefix */
+        if (p[0] && p[1] == ':')
+        {
+            p += 2;
+            while (*p == '\\') p++;
+            info->is_root = (*p == 0);
+        }
+    }
+    /*Initialize attr*/
     attr.Length = sizeof(attr);
     attr.RootDirectory = 0;
     attr.Attributes = OBJ_CASE_INSENSITIVE;
@@ -1612,6 +1712,7 @@
     attr.SecurityDescriptor = NULL;
     attr.SecurityQualityOfService = NULL;

+    /* check if nt_name is existing directory */
     status = NtOpenFile( &info->handle, GENERIC_READ, &attr, &io,
                          FILE_SHARE_READ | FILE_SHARE_WRITE,
                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
@@ -1630,12 +1731,12 @@
     info->data_len = 0;
     info->search_op = search_op;

-    if (!FindNextFileW( (HANDLE)info, data ))
+    if (!dos_device && !FindNextFileW( (HANDLE)info, data ))
     {
         TRACE( "%s not found\n", debugstr_w(filename) );
         FindClose( (HANDLE)info );
         SetLastError( ERROR_FILE_NOT_FOUND );
-        return INVALID_HANDLE_VALUE;
+        goto error;
     }
     return (HANDLE)info;

Но поскольку не была оттестирована функция RtlIsDosDeviceName_U, принимать эти 
исправления в счет нельзя.
Comment 6 Vitaly Lipatov 2006-05-11 14:33:56 MSD
1. Так и нет и намёка на исправление проблемы с 
C:\WINDOWS\ 
2. Тест не приложен к баге (через Создать приложение - не надо вписывать патчи 
в комментарии) 
Comment 7 Vitaly Lipatov 2006-05-15 20:28:49 MSD
Проблема в том, что 1С на NT системе убирает слэш в конце строки, если он  
есть, а в Win9X - не убирает.  
Вывод - указание \ в конце пути неверно и не должно использоваться.  
Выставить версию WINE в Win2000 не представляется возможным из-за баги #129. 
Comment 8 Константин Кондратюк 2007-10-10 09:31:46 MSD
К сборке добавлен патч, убирающий последний '\' при вызове FindFirstFileExA().
Пока что убирает для всех версий windows, и это неправильно, потому что в NT-системах файл с именем "c:\windows\" не будет найден.
Comment 9 Константин Кондратюк 2007-10-10 10:01:07 MSD
Добавил проверку версии Windows. Теперь при вызове в режиме WinXP функциональность идентична windows. Осталось проверить для win98.
Comment 10 Синицын Иван 2007-10-11 10:10:46 MSD
Тест запущенный на windows 98 показал

Target file is c:\windows.
The first file found is windows

Target file is c:\windows\.
Invalid File Handle

Comment 11 Константин Кондратюк 2007-10-11 10:21:28 MSD
Тест показывает, что и в Windows 98 "c:\windows\" не понимается.
Если нужно, можно оставить хак, добавляющий эту "лишнюю" функциональность. Только нужно ли?
Comment 12 Синицын Иван 2007-10-11 10:44:00 MSD
Created attachment 176 [details]
1C обработка

Эта обработка просто проверяет существует или нет указанный каталог. Проверяемый каталог возможно указать при работе с обработкой.
Comment 13 Vitaly Lipatov 2007-11-08 17:33:04 MSK
Бага в непонятном состоянии.
Добавление / приводит к зависанию установщика конфигурации 1С 8.1
на FindFirstFile("C:\WINDOWS\TEMP") - оно бесконечно вызывается, с FindNextFile...
Comment 14 Константин Кондратюк 2007-11-14 13:10:58 MSK
Решено проверкой на etersoft_1version()
Теперь преобразование только для 1С 7.7
Comment 15 Vitaly Lipatov 2008-06-20 15:44:20 MSD
К 1.0.6-eter11 патч восстановлен:
Created commit 15d89e9: remove last slash from the path (fix eterbug#106)

Comment 16 Vitaly Lipatov 2009-07-20 18:56:46 MSD
Начиная с 1.0.11 включили режим Windows XP для 1С 7.7 по умолчанию.