diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c index 4dbf17c8989..ff65aa75f5f 100644 --- a/dlls/kernel32/file.c +++ b/dlls/kernel32/file.c @@ -50,28 +50,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(file); -/* info structure for FindFirstFile handle */ -typedef struct -{ - DWORD magic; /* magic number */ - HANDLE handle; /* handle to directory */ - CRITICAL_SECTION cs; /* crit section protecting this structure */ - FINDEX_SEARCH_OPS search_op; /* Flags passed to FindFirst. */ - FINDEX_INFO_LEVELS level; /* Level passed to FindFirst */ - UNICODE_STRING path; /* NT path used to open the directory */ - BOOL is_root; /* is directory the root of the drive? */ - BOOL wildcard; /* did the mask contain wildcard characters? */ - UINT data_pos; /* current position in dir data */ - UINT data_len; /* length of dir data */ - UINT data_size; /* size of data buffer, or 0 when everything has been read */ - BYTE data[1]; /* directory data */ -} FIND_FIRST_INFO; - -#define FIND_FIRST_MAGIC 0xc0ffee11 - -static const UINT max_entry_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); - -static const WCHAR wildcardsW[] = { '*','?',0 }; static const WCHAR krnl386W[] = {'k','r','n','l','3','8','6','.','e','x','e','1','6',0}; /*********************************************************************** @@ -113,58 +91,6 @@ static HANDLE create_file_OF( LPCSTR path, INT mode ) } -/*********************************************************************** - * check_dir_symlink - * - * Check if a dir symlink should be returned by FindNextFile. - */ -static BOOL check_dir_symlink( FIND_FIRST_INFO *info, const FILE_BOTH_DIR_INFORMATION *file_info ) -{ - UNICODE_STRING str; - ANSI_STRING unix_name; - struct stat st, parent_st; - BOOL ret = TRUE; - DWORD len; - - str.MaximumLength = info->path.Length + sizeof(WCHAR) + file_info->FileNameLength; - if (!(str.Buffer = HeapAlloc( GetProcessHeap(), 0, str.MaximumLength ))) return TRUE; - memcpy( str.Buffer, info->path.Buffer, info->path.Length ); - len = info->path.Length / sizeof(WCHAR); - if (!len || str.Buffer[len-1] != '\\') str.Buffer[len++] = '\\'; - memcpy( str.Buffer + len, file_info->FileName, file_info->FileNameLength ); - str.Length = len * sizeof(WCHAR) + file_info->FileNameLength; - - unix_name.Buffer = NULL; - if (!wine_nt_to_unix_file_name( &str, &unix_name, OPEN_EXISTING, FALSE ) && - !stat( unix_name.Buffer, &st )) - { - char *p = unix_name.Buffer + unix_name.Length - 1; - - /* skip trailing slashes */ - while (p > unix_name.Buffer && *p == '/') p--; - - while (ret && p > unix_name.Buffer) - { - while (p > unix_name.Buffer && *p != '/') p--; - while (p > unix_name.Buffer && *p == '/') p--; - p[1] = 0; - if (!stat( unix_name.Buffer, &parent_st ) && - parent_st.st_dev == st.st_dev && - parent_st.st_ino == st.st_ino) - { - WARN( "suppressing dir symlink %s pointing to parent %s\n", - debugstr_wn( str.Buffer, str.Length/sizeof(WCHAR) ), - debugstr_a( unix_name.Buffer )); - ret = FALSE; - } - } - } - RtlFreeAnsiString( &unix_name ); - RtlFreeUnicodeString( &str ); - return ret; -} - - /*********************************************************************** * FILE_SetDosError * @@ -662,405 +588,6 @@ BOOL WINAPI ReplaceFileA(LPCSTR lpReplacedFileName,LPCSTR lpReplacementFileName, } -/************************************************************************* - * FindFirstFileExW (KERNEL32.@) - * - * NOTE: The FindExSearchLimitToDirectories is ignored - it gives the same - * results as FindExSearchNameMatch - */ -HANDLE WINAPI FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, - LPVOID data, FINDEX_SEARCH_OPS search_op, - LPVOID filter, DWORD flags) -{ - WCHAR *mask; - BOOL has_wildcard = FALSE; - FIND_FIRST_INFO *info = NULL; - UNICODE_STRING nt_name; - OBJECT_ATTRIBUTES attr; - IO_STATUS_BLOCK io; - NTSTATUS status; - DWORD size, device = 0; - - TRACE("%s %d %p %d %p %x\n", debugstr_w(filename), level, data, search_op, filter, flags); - - if (flags & ~FIND_FIRST_EX_LARGE_FETCH) - { - FIXME("flags not implemented 0x%08x\n", flags ); - } - if (search_op != FindExSearchNameMatch && search_op != FindExSearchLimitToDirectories) - { - FIXME("search_op not implemented 0x%08x\n", search_op); - SetLastError( ERROR_INVALID_PARAMETER ); - return INVALID_HANDLE_VALUE; - } - if (level != FindExInfoStandard && level != FindExInfoBasic) - { - FIXME("info level %d not implemented\n", level ); - SetLastError( ERROR_INVALID_PARAMETER ); - return INVALID_HANDLE_VALUE; - } - - if (!RtlDosPathNameToNtPathName_U( filename, &nt_name, &mask, NULL )) - { - SetLastError( ERROR_PATH_NOT_FOUND ); - return INVALID_HANDLE_VALUE; - } - - if (!mask && (device = RtlIsDosDeviceName_U( filename ))) - { - static const WCHAR dotW[] = {'.',0}; - WCHAR *dir = NULL; - - /* we still need to check that the directory can be opened */ - - if (HIWORD(device)) - { - if (!(dir = HeapAlloc( GetProcessHeap(), 0, HIWORD(device) + sizeof(WCHAR) ))) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - goto error; - } - memcpy( dir, filename, HIWORD(device) ); - dir[HIWORD(device)/sizeof(WCHAR)] = 0; - } - RtlFreeUnicodeString( &nt_name ); - if (!RtlDosPathNameToNtPathName_U( dir ? dir : dotW, &nt_name, &mask, NULL )) - { - HeapFree( GetProcessHeap(), 0, dir ); - SetLastError( ERROR_PATH_NOT_FOUND ); - goto error; - } - HeapFree( GetProcessHeap(), 0, dir ); - size = 0; - } - else if (!mask || !*mask) - { - SetLastError( ERROR_FILE_NOT_FOUND ); - goto error; - } - else - { - nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR); - has_wildcard = strpbrkW( mask, wildcardsW ) != NULL; - size = has_wildcard ? 8192 : max_entry_size; - } - - if (!(info = HeapAlloc( GetProcessHeap(), 0, offsetof( FIND_FIRST_INFO, data[size] )))) - { - SetLastError( ERROR_NOT_ENOUGH_MEMORY ); - goto error; - } - - /* check if path is the root of the drive, skipping the \??\ prefix */ - info->is_root = FALSE; - if (nt_name.Length >= 6 * sizeof(WCHAR) && nt_name.Buffer[5] == ':') - { - DWORD pos = 6; - while (pos * sizeof(WCHAR) < nt_name.Length && nt_name.Buffer[pos] == '\\') pos++; - info->is_root = (pos * sizeof(WCHAR) >= nt_name.Length); - } - - attr.Length = sizeof(attr); - attr.RootDirectory = 0; - attr.Attributes = OBJ_CASE_INSENSITIVE; - attr.ObjectName = &nt_name; - attr.SecurityDescriptor = NULL; - attr.SecurityQualityOfService = NULL; - - status = NtOpenFile( &info->handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, - FILE_SHARE_READ | FILE_SHARE_WRITE, - FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); - - if (status != STATUS_SUCCESS) - { - if (status == STATUS_OBJECT_NAME_NOT_FOUND) - SetLastError( ERROR_PATH_NOT_FOUND ); - else - SetLastError( RtlNtStatusToDosError(status) ); - goto error; - } - - RtlInitializeCriticalSection( &info->cs ); - info->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FIND_FIRST_INFO.cs"); - info->path = nt_name; - info->magic = FIND_FIRST_MAGIC; - info->wildcard = has_wildcard; - info->data_pos = 0; - info->data_len = 0; - info->data_size = size; - info->search_op = search_op; - info->level = level; - - if (device) - { - WIN32_FIND_DATAW *wfd = data; - - memset( wfd, 0, sizeof(*wfd) ); - memcpy( wfd->cFileName, filename + HIWORD(device)/sizeof(WCHAR), LOWORD(device) ); - wfd->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; - CloseHandle( info->handle ); - info->handle = 0; - } - else - { - UNICODE_STRING mask_str; - - RtlInitUnicodeString( &mask_str, mask ); - status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, &mask_str, TRUE ); - if (status) - { - FindClose( info ); - SetLastError( RtlNtStatusToDosError( status ) ); - return INVALID_HANDLE_VALUE; - } - - info->data_len = io.Information; - if (!has_wildcard) info->data_size = 0; /* we read everything */ - - if (!FindNextFileW( info, data )) - { - TRACE( "%s not found\n", debugstr_w(filename) ); - FindClose( info ); - SetLastError( ERROR_FILE_NOT_FOUND ); - return INVALID_HANDLE_VALUE; - } - if (!has_wildcard) /* we can't find two files with the same name */ - { - CloseHandle( info->handle ); - info->handle = 0; - } - } - return info; - -error: - HeapFree( GetProcessHeap(), 0, info ); - RtlFreeUnicodeString( &nt_name ); - return INVALID_HANDLE_VALUE; -} - - -/************************************************************************* - * FindNextFileW (KERNEL32.@) - */ -BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) -{ - FIND_FIRST_INFO *info; - FILE_BOTH_DIR_INFORMATION *dir_info; - BOOL ret = FALSE; - NTSTATUS status; - - TRACE("%p %p\n", handle, data); - - if (!handle || handle == INVALID_HANDLE_VALUE) - { - SetLastError( ERROR_INVALID_HANDLE ); - return ret; - } - info = handle; - if (info->magic != FIND_FIRST_MAGIC) - { - SetLastError( ERROR_INVALID_HANDLE ); - return ret; - } - - RtlEnterCriticalSection( &info->cs ); - - if (!info->handle) SetLastError( ERROR_NO_MORE_FILES ); - else for (;;) - { - if (info->data_pos >= info->data_len) /* need to read some more data */ - { - IO_STATUS_BLOCK io; - - if (info->data_size) - status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, - FileBothDirectoryInformation, FALSE, NULL, FALSE ); - else - status = STATUS_NO_MORE_FILES; - - if (status) - { - SetLastError( RtlNtStatusToDosError( status ) ); - if (status == STATUS_NO_MORE_FILES) - { - CloseHandle( info->handle ); - info->handle = 0; - } - break; - } - info->data_len = io.Information; - info->data_pos = 0; - } - - dir_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos); - - if (dir_info->NextEntryOffset) info->data_pos += dir_info->NextEntryOffset; - else info->data_pos = info->data_len; - - /* don't return '.' and '..' in the root of the drive */ - if (info->is_root) - { - if (dir_info->FileNameLength == sizeof(WCHAR) && dir_info->FileName[0] == '.') continue; - if (dir_info->FileNameLength == 2 * sizeof(WCHAR) && - dir_info->FileName[0] == '.' && dir_info->FileName[1] == '.') continue; - } - - /* check for dir symlink */ - if ((dir_info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && - (dir_info->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && - info->wildcard) - { - if (!check_dir_symlink( info, dir_info )) continue; - } - - data->dwFileAttributes = dir_info->FileAttributes; - data->ftCreationTime = *(FILETIME *)&dir_info->CreationTime; - data->ftLastAccessTime = *(FILETIME *)&dir_info->LastAccessTime; - data->ftLastWriteTime = *(FILETIME *)&dir_info->LastWriteTime; - data->nFileSizeHigh = dir_info->EndOfFile.QuadPart >> 32; - data->nFileSizeLow = (DWORD)dir_info->EndOfFile.QuadPart; - data->dwReserved0 = 0; - data->dwReserved1 = 0; - - memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength ); - data->cFileName[dir_info->FileNameLength/sizeof(WCHAR)] = 0; - - if (info->level != FindExInfoBasic) - { - memcpy( data->cAlternateFileName, dir_info->ShortName, dir_info->ShortNameLength ); - data->cAlternateFileName[dir_info->ShortNameLength/sizeof(WCHAR)] = 0; - } - else - data->cAlternateFileName[0] = 0; - - TRACE("returning %s (%s)\n", - debugstr_w(data->cFileName), debugstr_w(data->cAlternateFileName) ); - - ret = TRUE; - break; - } - - RtlLeaveCriticalSection( &info->cs ); - return ret; -} - - -/************************************************************************* - * FindClose (KERNEL32.@) - */ -BOOL WINAPI DECLSPEC_HOTPATCH FindClose( HANDLE handle ) -{ - FIND_FIRST_INFO *info = handle; - - if (!handle || handle == INVALID_HANDLE_VALUE) - { - SetLastError( ERROR_INVALID_HANDLE ); - return FALSE; - } - - __TRY - { - if (info->magic == FIND_FIRST_MAGIC) - { - RtlEnterCriticalSection( &info->cs ); - if (info->magic == FIND_FIRST_MAGIC) /* in case someone else freed it in the meantime */ - { - info->magic = 0; - if (info->handle) CloseHandle( info->handle ); - info->handle = 0; - RtlFreeUnicodeString( &info->path ); - info->data_pos = 0; - info->data_len = 0; - RtlLeaveCriticalSection( &info->cs ); - info->cs.DebugInfo->Spare[0] = 0; - RtlDeleteCriticalSection( &info->cs ); - HeapFree( GetProcessHeap(), 0, info ); - } - } - } - __EXCEPT_PAGE_FAULT - { - WARN("Illegal handle %p\n", handle); - SetLastError( ERROR_INVALID_HANDLE ); - return FALSE; - } - __ENDTRY - - return TRUE; -} - - -/************************************************************************* - * FindFirstFileA (KERNEL32.@) - */ -HANDLE WINAPI FindFirstFileA( LPCSTR lpFileName, WIN32_FIND_DATAA *lpFindData ) -{ - return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData, - FindExSearchNameMatch, NULL, 0); -} - -/************************************************************************* - * FindFirstFileExA (KERNEL32.@) - */ -HANDLE WINAPI FindFirstFileExA( LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, - LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, - LPVOID lpSearchFilter, DWORD dwAdditionalFlags) -{ - HANDLE handle; - WIN32_FIND_DATAA *dataA; - WIN32_FIND_DATAW dataW; - WCHAR *nameW; - - if (!(nameW = FILE_name_AtoW( lpFileName, FALSE ))) return INVALID_HANDLE_VALUE; - - handle = FindFirstFileExW(nameW, fInfoLevelId, &dataW, fSearchOp, lpSearchFilter, dwAdditionalFlags); - if (handle == INVALID_HANDLE_VALUE) return handle; - - dataA = lpFindFileData; - dataA->dwFileAttributes = dataW.dwFileAttributes; - dataA->ftCreationTime = dataW.ftCreationTime; - dataA->ftLastAccessTime = dataW.ftLastAccessTime; - dataA->ftLastWriteTime = dataW.ftLastWriteTime; - dataA->nFileSizeHigh = dataW.nFileSizeHigh; - dataA->nFileSizeLow = dataW.nFileSizeLow; - FILE_name_WtoA( dataW.cFileName, -1, dataA->cFileName, sizeof(dataA->cFileName) ); - FILE_name_WtoA( dataW.cAlternateFileName, -1, dataA->cAlternateFileName, - sizeof(dataA->cAlternateFileName) ); - return handle; -} - - -/************************************************************************* - * FindFirstFileW (KERNEL32.@) - */ -HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData ) -{ - return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData, - FindExSearchNameMatch, NULL, 0); -} - - -/************************************************************************* - * FindNextFileA (KERNEL32.@) - */ -BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data ) -{ - WIN32_FIND_DATAW dataW; - - if (!FindNextFileW( handle, &dataW )) return FALSE; - data->dwFileAttributes = dataW.dwFileAttributes; - data->ftCreationTime = dataW.ftCreationTime; - data->ftLastAccessTime = dataW.ftLastAccessTime; - data->ftLastWriteTime = dataW.ftLastWriteTime; - data->nFileSizeHigh = dataW.nFileSizeHigh; - data->nFileSizeLow = dataW.nFileSizeLow; - FILE_name_WtoA( dataW.cFileName, -1, data->cFileName, sizeof(data->cFileName) ); - FILE_name_WtoA( dataW.cAlternateFileName, -1, data->cAlternateFileName, - sizeof(data->cAlternateFileName) ); - return TRUE; -} - /************************************************************************** * FindFirstStreamW (KERNEL32.@) */ diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 87e31cf4574..d8b50426238 100644 --- a/dlls/kernel32/kernel32.spec +++ b/dlls/kernel32/kernel32.spec @@ -475,18 +475,18 @@ @ stdcall -import FindActCtxSectionStringW(long ptr long wstr ptr) @ stdcall FindAtomA(str) @ stdcall FindAtomW(wstr) -@ stdcall FindClose(long) +@ stdcall -import FindClose(long) @ stdcall -import FindCloseChangeNotification(long) @ stdcall -import FindFirstChangeNotificationA(str long long) @ stdcall -import FindFirstChangeNotificationW(wstr long long) -@ stdcall FindFirstFileA(str ptr) -@ stdcall FindFirstFileExA(str long ptr long ptr long) -@ stdcall FindFirstFileExW(wstr long ptr long ptr long) +@ stdcall -import FindFirstFileA(str ptr) +@ stdcall -import FindFirstFileExA(str long ptr long ptr long) +@ stdcall -import FindFirstFileExW(wstr long ptr long ptr long) # @ stub FindFirstFileNameTransactedW # @ stub FindFirstFileNameW # @ stub FindFirstFileTransactedA # @ stub FindFirstFileTransactedW -@ stdcall FindFirstFileW(wstr ptr) +@ stdcall -import FindFirstFileW(wstr ptr) # @ stub FindFirstStreamTransactedW @ stdcall FindFirstStreamW(wstr long ptr long) @ stdcall FindFirstVolumeA(ptr long) @@ -494,9 +494,9 @@ @ stdcall FindFirstVolumeMountPointW(wstr ptr long) @ stdcall FindFirstVolumeW(ptr long) @ stdcall -import FindNextChangeNotification(long) -@ stdcall FindNextFileA(long ptr) +@ stdcall -import FindNextFileA(long ptr) # @ stub FindNextFileNameW -@ stdcall FindNextFileW(long ptr) +@ stdcall -import FindNextFileW(long ptr) @ stdcall FindNextStreamW(long ptr) @ stdcall FindNextVolumeA(long ptr long) @ stub FindNextVolumeMountPointA diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 59ddcd9ed93..89c5f85cb20 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -43,6 +43,27 @@ WINE_DEFAULT_DEBUG_CHANNEL(file); +/* info structure for FindFirstFile handle */ +typedef struct +{ + DWORD magic; /* magic number */ + HANDLE handle; /* handle to directory */ + CRITICAL_SECTION cs; /* crit section protecting this structure */ + FINDEX_SEARCH_OPS search_op; /* Flags passed to FindFirst. */ + FINDEX_INFO_LEVELS level; /* Level passed to FindFirst */ + UNICODE_STRING path; /* NT path used to open the directory */ + BOOL is_root; /* is directory the root of the drive? */ + BOOL wildcard; /* did the mask contain wildcard characters? */ + UINT data_pos; /* current position in dir data */ + UINT data_len; /* length of dir data */ + UINT data_size; /* size of data buffer, or 0 when everything has been read */ + BYTE data[1]; /* directory data */ +} FIND_FIRST_INFO; + +#define FIND_FIRST_MAGIC 0xc0ffee11 + +static const UINT max_entry_size = offsetof( FILE_BOTH_DIRECTORY_INFORMATION, FileName[256] ); + const WCHAR windows_dir[] = {'C',':','\\','w','i','n','d','o','w','s',0}; const WCHAR system_dir[] = {'C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m','3','2',0}; @@ -734,6 +755,385 @@ BOOL WINAPI DECLSPEC_HOTPATCH FindNextChangeNotification( HANDLE handle ) } +/****************************************************************************** + * FindFirstFileExA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExA( const char *filename, FINDEX_INFO_LEVELS level, + void *data, FINDEX_SEARCH_OPS search_op, + void *filter, DWORD flags ) +{ + HANDLE handle; + WIN32_FIND_DATAA *dataA = data; + WIN32_FIND_DATAW dataW; + WCHAR *nameW; + + if (!(nameW = file_name_AtoW( filename, FALSE ))) return INVALID_HANDLE_VALUE; + + handle = FindFirstFileExW( nameW, level, &dataW, search_op, filter, flags ); + if (handle == INVALID_HANDLE_VALUE) return handle; + + dataA->dwFileAttributes = dataW.dwFileAttributes; + dataA->ftCreationTime = dataW.ftCreationTime; + dataA->ftLastAccessTime = dataW.ftLastAccessTime; + dataA->ftLastWriteTime = dataW.ftLastWriteTime; + dataA->nFileSizeHigh = dataW.nFileSizeHigh; + dataA->nFileSizeLow = dataW.nFileSizeLow; + file_name_WtoA( dataW.cFileName, -1, dataA->cFileName, sizeof(dataA->cFileName) ); + file_name_WtoA( dataW.cAlternateFileName, -1, dataA->cAlternateFileName, + sizeof(dataA->cAlternateFileName) ); + return handle; +} + + +/****************************************************************************** + * FindFirstFileExW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileExW( LPCWSTR filename, FINDEX_INFO_LEVELS level, + LPVOID data, FINDEX_SEARCH_OPS search_op, + LPVOID filter, DWORD flags ) +{ + WCHAR *mask; + BOOL has_wildcard = FALSE; + FIND_FIRST_INFO *info = NULL; + UNICODE_STRING nt_name; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + DWORD size, device = 0; + + TRACE( "%s %d %p %d %p %x\n", debugstr_w(filename), level, data, search_op, filter, flags ); + + if (flags & ~FIND_FIRST_EX_LARGE_FETCH) + { + FIXME("flags not implemented 0x%08x\n", flags ); + } + if (search_op != FindExSearchNameMatch && search_op != FindExSearchLimitToDirectories) + { + FIXME( "search_op not implemented 0x%08x\n", search_op ); + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + if (level != FindExInfoStandard && level != FindExInfoBasic) + { + FIXME("info level %d not implemented\n", level ); + SetLastError( ERROR_INVALID_PARAMETER ); + return INVALID_HANDLE_VALUE; + } + + if (!RtlDosPathNameToNtPathName_U( filename, &nt_name, &mask, NULL )) + { + SetLastError( ERROR_PATH_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + + if (!mask && (device = RtlIsDosDeviceName_U( filename ))) + { + static const WCHAR dotW[] = {'.',0}; + WCHAR *dir = NULL; + + /* we still need to check that the directory can be opened */ + + if (HIWORD(device)) + { + if (!(dir = HeapAlloc( GetProcessHeap(), 0, HIWORD(device) + sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + memcpy( dir, filename, HIWORD(device) ); + dir[HIWORD(device)/sizeof(WCHAR)] = 0; + } + RtlFreeUnicodeString( &nt_name ); + if (!RtlDosPathNameToNtPathName_U( dir ? dir : dotW, &nt_name, &mask, NULL )) + { + HeapFree( GetProcessHeap(), 0, dir ); + SetLastError( ERROR_PATH_NOT_FOUND ); + goto error; + } + HeapFree( GetProcessHeap(), 0, dir ); + size = 0; + } + else if (!mask || !*mask) + { + SetLastError( ERROR_FILE_NOT_FOUND ); + goto error; + } + else + { + nt_name.Length = (mask - nt_name.Buffer) * sizeof(WCHAR); + has_wildcard = wcspbrk( mask, L"*?" ) != NULL; + size = has_wildcard ? 8192 : max_entry_size; + } + + if (!(info = HeapAlloc( GetProcessHeap(), 0, offsetof( FIND_FIRST_INFO, data[size] )))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + goto error; + } + + /* check if path is the root of the drive, skipping the \??\ prefix */ + info->is_root = FALSE; + if (nt_name.Length >= 6 * sizeof(WCHAR) && nt_name.Buffer[5] == ':') + { + DWORD pos = 6; + while (pos * sizeof(WCHAR) < nt_name.Length && nt_name.Buffer[pos] == '\\') pos++; + info->is_root = (pos * sizeof(WCHAR) >= nt_name.Length); + } + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &nt_name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + status = NtOpenFile( &info->handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); + if (status != STATUS_SUCCESS) + { + if (status == STATUS_OBJECT_NAME_NOT_FOUND) + SetLastError( ERROR_PATH_NOT_FOUND ); + else + SetLastError( RtlNtStatusToDosError(status) ); + goto error; + } + + RtlInitializeCriticalSection( &info->cs ); + info->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FIND_FIRST_INFO.cs"); + info->path = nt_name; + info->magic = FIND_FIRST_MAGIC; + info->wildcard = has_wildcard; + info->data_pos = 0; + info->data_len = 0; + info->data_size = size; + info->search_op = search_op; + info->level = level; + + if (device) + { + WIN32_FIND_DATAW *wfd = data; + + memset( wfd, 0, sizeof(*wfd) ); + memcpy( wfd->cFileName, filename + HIWORD(device)/sizeof(WCHAR), LOWORD(device) ); + wfd->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + CloseHandle( info->handle ); + info->handle = 0; + } + else + { + UNICODE_STRING mask_str; + + RtlInitUnicodeString( &mask_str, mask ); + status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, + FileBothDirectoryInformation, FALSE, &mask_str, TRUE ); + if (status) + { + FindClose( info ); + SetLastError( RtlNtStatusToDosError( status ) ); + return INVALID_HANDLE_VALUE; + } + + info->data_len = io.Information; + if (!has_wildcard) info->data_size = 0; /* we read everything */ + + if (!FindNextFileW( info, data )) + { + TRACE( "%s not found\n", debugstr_w(filename) ); + FindClose( info ); + SetLastError( ERROR_FILE_NOT_FOUND ); + return INVALID_HANDLE_VALUE; + } + if (!has_wildcard) /* we can't find two files with the same name */ + { + CloseHandle( info->handle ); + info->handle = 0; + } + } + return info; + +error: + HeapFree( GetProcessHeap(), 0, info ); + RtlFreeUnicodeString( &nt_name ); + return INVALID_HANDLE_VALUE; +} + + +/****************************************************************************** + * FindFirstFileA (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileA( const char *filename, WIN32_FIND_DATAA *data ) +{ + return FindFirstFileExA( filename, FindExInfoStandard, data, FindExSearchNameMatch, NULL, 0 ); +} + + +/****************************************************************************** + * FindFirstFileW (kernelbase.@) + */ +HANDLE WINAPI DECLSPEC_HOTPATCH FindFirstFileW( const WCHAR *filename, WIN32_FIND_DATAW *data ) +{ + return FindFirstFileExW( filename, FindExInfoStandard, data, FindExSearchNameMatch, NULL, 0 ); +} + + +/****************************************************************************** + * FindNextFileA (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data ) +{ + WIN32_FIND_DATAW dataW; + + if (!FindNextFileW( handle, &dataW )) return FALSE; + data->dwFileAttributes = dataW.dwFileAttributes; + data->ftCreationTime = dataW.ftCreationTime; + data->ftLastAccessTime = dataW.ftLastAccessTime; + data->ftLastWriteTime = dataW.ftLastWriteTime; + data->nFileSizeHigh = dataW.nFileSizeHigh; + data->nFileSizeLow = dataW.nFileSizeLow; + file_name_WtoA( dataW.cFileName, -1, data->cFileName, sizeof(data->cFileName) ); + file_name_WtoA( dataW.cAlternateFileName, -1, data->cAlternateFileName, + sizeof(data->cAlternateFileName) ); + return TRUE; +} + + +/****************************************************************************** + * FindNextFileW (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data ) +{ + FIND_FIRST_INFO *info = handle; + FILE_BOTH_DIR_INFORMATION *dir_info; + BOOL ret = FALSE; + NTSTATUS status; + + TRACE( "%p %p\n", handle, data ); + + if (!handle || handle == INVALID_HANDLE_VALUE || info->magic != FIND_FIRST_MAGIC) + { + SetLastError( ERROR_INVALID_HANDLE ); + return ret; + } + + RtlEnterCriticalSection( &info->cs ); + + if (!info->handle) SetLastError( ERROR_NO_MORE_FILES ); + else for (;;) + { + if (info->data_pos >= info->data_len) /* need to read some more data */ + { + IO_STATUS_BLOCK io; + + if (info->data_size) + status = NtQueryDirectoryFile( info->handle, 0, NULL, NULL, &io, info->data, info->data_size, + FileBothDirectoryInformation, FALSE, NULL, FALSE ); + else + status = STATUS_NO_MORE_FILES; + + if (!set_ntstatus( status )) + { + if (status == STATUS_NO_MORE_FILES) + { + CloseHandle( info->handle ); + info->handle = 0; + } + break; + } + info->data_len = io.Information; + info->data_pos = 0; + } + + dir_info = (FILE_BOTH_DIR_INFORMATION *)(info->data + info->data_pos); + + if (dir_info->NextEntryOffset) info->data_pos += dir_info->NextEntryOffset; + else info->data_pos = info->data_len; + + /* don't return '.' and '..' in the root of the drive */ + if (info->is_root) + { + if (dir_info->FileNameLength == sizeof(WCHAR) && dir_info->FileName[0] == '.') continue; + if (dir_info->FileNameLength == 2 * sizeof(WCHAR) && + dir_info->FileName[0] == '.' && dir_info->FileName[1] == '.') continue; + } + + data->dwFileAttributes = dir_info->FileAttributes; + data->ftCreationTime = *(FILETIME *)&dir_info->CreationTime; + data->ftLastAccessTime = *(FILETIME *)&dir_info->LastAccessTime; + data->ftLastWriteTime = *(FILETIME *)&dir_info->LastWriteTime; + data->nFileSizeHigh = dir_info->EndOfFile.QuadPart >> 32; + data->nFileSizeLow = (DWORD)dir_info->EndOfFile.QuadPart; + data->dwReserved0 = 0; + data->dwReserved1 = 0; + + memcpy( data->cFileName, dir_info->FileName, dir_info->FileNameLength ); + data->cFileName[dir_info->FileNameLength/sizeof(WCHAR)] = 0; + + if (info->level != FindExInfoBasic) + { + memcpy( data->cAlternateFileName, dir_info->ShortName, dir_info->ShortNameLength ); + data->cAlternateFileName[dir_info->ShortNameLength/sizeof(WCHAR)] = 0; + } + else + data->cAlternateFileName[0] = 0; + + TRACE( "returning %s (%s)\n", + debugstr_w(data->cFileName), debugstr_w(data->cAlternateFileName) ); + + ret = TRUE; + break; + } + + RtlLeaveCriticalSection( &info->cs ); + return ret; +} + + +/****************************************************************************** + * FindClose (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH FindClose( HANDLE handle ) +{ + FIND_FIRST_INFO *info = handle; + + if (!handle || handle == INVALID_HANDLE_VALUE) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + __TRY + { + if (info->magic == FIND_FIRST_MAGIC) + { + RtlEnterCriticalSection( &info->cs ); + if (info->magic == FIND_FIRST_MAGIC) /* in case someone else freed it in the meantime */ + { + info->magic = 0; + if (info->handle) CloseHandle( info->handle ); + info->handle = 0; + RtlFreeUnicodeString( &info->path ); + info->data_pos = 0; + info->data_len = 0; + RtlLeaveCriticalSection( &info->cs ); + info->cs.DebugInfo->Spare[0] = 0; + RtlDeleteCriticalSection( &info->cs ); + HeapFree( GetProcessHeap(), 0, info ); + } + } + } + __EXCEPT_PAGE_FAULT + { + WARN( "illegal handle %p\n", handle ); + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + __ENDTRY + + return TRUE; +} + + /****************************************************************************** * GetCompressedFileSizeA (kernelbase.@) */ diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 61215107e4a..032e379dc06 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -350,24 +350,24 @@ @ stdcall FillConsoleOutputCharacterW(long long long long ptr) @ stdcall FindActCtxSectionGuid(long ptr long ptr ptr) @ stdcall FindActCtxSectionStringW(long ptr long wstr ptr) -@ stdcall FindClose(long) kernel32.FindClose +@ stdcall FindClose(long) @ stdcall FindCloseChangeNotification(long) @ stdcall FindFirstChangeNotificationA(str long long) @ stdcall FindFirstChangeNotificationW(wstr long long) -@ stdcall FindFirstFileA(str ptr) kernel32.FindFirstFileA -@ stdcall FindFirstFileExA(str long ptr long ptr long) kernel32.FindFirstFileExA -@ stdcall FindFirstFileExW(wstr long ptr long ptr long) kernel32.FindFirstFileExW +@ stdcall FindFirstFileA(str ptr) +@ stdcall FindFirstFileExA(str long ptr long ptr long) +@ stdcall FindFirstFileExW(wstr long ptr long ptr long) # @ stub FindFirstFileNameW -@ stdcall FindFirstFileW(wstr ptr) kernel32.FindFirstFileW +@ stdcall FindFirstFileW(wstr ptr) @ stdcall FindFirstFreeAce(ptr ptr) @ stdcall FindFirstStreamW(wstr long ptr long) kernel32.FindFirstStreamW @ stdcall FindFirstVolumeW(ptr long) kernel32.FindFirstVolumeW @ stdcall FindNLSString(long long wstr long wstr long ptr) @ stdcall FindNLSStringEx(wstr long wstr long wstr long ptr ptr ptr long) @ stdcall FindNextChangeNotification(long) -@ stdcall FindNextFileA(long ptr) kernel32.FindNextFileA +@ stdcall FindNextFileA(long ptr) # @ stub FindNextFileNameW -@ stdcall FindNextFileW(long ptr) kernel32.FindNextFileW +@ stdcall FindNextFileW(long ptr) @ stdcall FindNextStreamW(long ptr) kernel32.FindNextStreamW @ stdcall FindNextVolumeW(long ptr long) kernel32.FindNextVolumeW # @ stub FindPackagesByPackageFamily