ntdll: Search the whole filesystem under the starting directory when opening files by id.
This commit is contained in:
parent
58b007b0b7
commit
6509044087
|
@ -71,6 +71,7 @@
|
||||||
#include "ntdll_misc.h"
|
#include "ntdll_misc.h"
|
||||||
#include "wine/unicode.h"
|
#include "wine/unicode.h"
|
||||||
#include "wine/server.h"
|
#include "wine/server.h"
|
||||||
|
#include "wine/list.h"
|
||||||
#include "wine/library.h"
|
#include "wine/library.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
|
@ -249,6 +250,54 @@ static inline unsigned int max_dir_info_size( FILE_INFORMATION_CLASS class )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* support for a directory queue for filesystem searches */
|
||||||
|
|
||||||
|
struct dir_name
|
||||||
|
{
|
||||||
|
struct list entry;
|
||||||
|
char name[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct list dir_queue = LIST_INIT( dir_queue );
|
||||||
|
|
||||||
|
static NTSTATUS add_dir_to_queue( const char *name )
|
||||||
|
{
|
||||||
|
int len = strlen( name ) + 1;
|
||||||
|
struct dir_name *dir = RtlAllocateHeap( GetProcessHeap(), 0,
|
||||||
|
FIELD_OFFSET( struct dir_name, name[len] ));
|
||||||
|
if (!dir) return STATUS_NO_MEMORY;
|
||||||
|
strcpy( dir->name, name );
|
||||||
|
list_add_tail( &dir_queue, &dir->entry );
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS next_dir_in_queue( char *name )
|
||||||
|
{
|
||||||
|
struct list *head = list_head( &dir_queue );
|
||||||
|
if (head)
|
||||||
|
{
|
||||||
|
struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
|
||||||
|
strcpy( name, dir->name );
|
||||||
|
list_remove( &dir->entry );
|
||||||
|
RtlFreeHeap( GetProcessHeap(), 0, dir );
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_dir_queue(void)
|
||||||
|
{
|
||||||
|
struct list *head;
|
||||||
|
|
||||||
|
while ((head = list_head( &dir_queue )))
|
||||||
|
{
|
||||||
|
struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry );
|
||||||
|
list_remove( &dir->entry );
|
||||||
|
RtlFreeHeap( GetProcessHeap(), 0, dir );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* get_default_com_device
|
* get_default_com_device
|
||||||
*
|
*
|
||||||
|
@ -2217,82 +2266,135 @@ static inline int get_dos_prefix_len( const UNICODE_STRING *name )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
* find_file_id
|
||||||
|
*
|
||||||
|
* Recursively search directories from the dir queue for a given inode.
|
||||||
|
*/
|
||||||
|
static NTSTATUS find_file_id( ANSI_STRING *unix_name, ULONGLONG file_id, dev_t dev )
|
||||||
|
{
|
||||||
|
unsigned int pos;
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *de;
|
||||||
|
NTSTATUS status;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
while (!(status = next_dir_in_queue( unix_name->Buffer )))
|
||||||
|
{
|
||||||
|
if (!(dir = opendir( unix_name->Buffer ))) continue;
|
||||||
|
TRACE( "searching %s for %s\n", unix_name->Buffer, wine_dbgstr_longlong(file_id) );
|
||||||
|
pos = strlen( unix_name->Buffer );
|
||||||
|
if (pos + MAX_DIR_ENTRY_LEN >= unix_name->MaximumLength/sizeof(WCHAR))
|
||||||
|
{
|
||||||
|
char *new = RtlReAllocateHeap( GetProcessHeap(), 0, unix_name->Buffer,
|
||||||
|
unix_name->MaximumLength * 2 );
|
||||||
|
if (!new)
|
||||||
|
{
|
||||||
|
closedir( dir );
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
unix_name->MaximumLength *= 2;
|
||||||
|
unix_name->Buffer = new;
|
||||||
|
}
|
||||||
|
unix_name->Buffer[pos++] = '/';
|
||||||
|
while ((de = readdir( dir )))
|
||||||
|
{
|
||||||
|
if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue;
|
||||||
|
strcpy( unix_name->Buffer + pos, de->d_name );
|
||||||
|
if (lstat( unix_name->Buffer, &st ) == -1) continue;
|
||||||
|
if (st.st_dev != dev) continue;
|
||||||
|
if (st.st_ino == file_id)
|
||||||
|
{
|
||||||
|
closedir( dir );
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
if (!S_ISDIR( st.st_mode )) continue;
|
||||||
|
if ((status = add_dir_to_queue( unix_name->Buffer )) != STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
closedir( dir );
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir( dir );
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* file_id_to_unix_file_name
|
* file_id_to_unix_file_name
|
||||||
*
|
*
|
||||||
* Lookup a file from its file id instead of its name.
|
* Lookup a file from its file id instead of its name.
|
||||||
*/
|
*/
|
||||||
NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name_ret )
|
NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, ANSI_STRING *unix_name )
|
||||||
{
|
{
|
||||||
enum server_fd_type type;
|
enum server_fd_type type;
|
||||||
int old_cwd, root_fd, needs_close;
|
int old_cwd, root_fd, needs_close;
|
||||||
char *unix_name;
|
|
||||||
int unix_len;
|
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
ULONGLONG file_id;
|
ULONGLONG file_id;
|
||||||
struct stat st, root_st;
|
struct stat st, root_st;
|
||||||
DIR *dir;
|
|
||||||
struct dirent *de;
|
|
||||||
|
|
||||||
if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
|
if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||||||
if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
|
if (!attr->RootDirectory) return STATUS_INVALID_PARAMETER;
|
||||||
memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
|
memcpy( &file_id, attr->ObjectName->Buffer, sizeof(file_id) );
|
||||||
|
|
||||||
unix_len = MAX_DIR_ENTRY_LEN + 1;
|
unix_name->MaximumLength = 2 * MAX_DIR_ENTRY_LEN + 4;
|
||||||
if (!(unix_name = RtlAllocateHeap( GetProcessHeap(), 0, unix_len )))
|
if (!(unix_name->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, unix_name->MaximumLength )))
|
||||||
return STATUS_NO_MEMORY;
|
return STATUS_NO_MEMORY;
|
||||||
unix_name[0] = 0;
|
strcpy( unix_name->Buffer, "." );
|
||||||
|
|
||||||
if (!(status = server_get_unix_fd( attr->RootDirectory, FILE_READ_DATA, &root_fd,
|
if ((status = server_get_unix_fd( attr->RootDirectory, 0, &root_fd, &needs_close, &type, NULL )))
|
||||||
&needs_close, &type, NULL )))
|
goto done;
|
||||||
|
|
||||||
|
if (type != FD_TYPE_DIR)
|
||||||
{
|
{
|
||||||
if (type != FD_TYPE_DIR)
|
status = STATUS_OBJECT_TYPE_MISMATCH;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstat( root_fd, &root_st );
|
||||||
|
if (root_st.st_ino == file_id) /* shortcut for "." */
|
||||||
|
{
|
||||||
|
status = STATUS_SUCCESS;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlEnterCriticalSection( &dir_section );
|
||||||
|
if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
|
||||||
|
{
|
||||||
|
/* shortcut for ".." */
|
||||||
|
if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id)
|
||||||
{
|
{
|
||||||
if (needs_close) close( root_fd );
|
strcpy( unix_name->Buffer, ".." );
|
||||||
status = STATUS_OBJECT_TYPE_MISMATCH;
|
status = STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fstat( root_fd, &root_st );
|
status = add_dir_to_queue( "." );
|
||||||
RtlEnterCriticalSection( &dir_section );
|
if (!status)
|
||||||
if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1)
|
status = find_file_id( unix_name, file_id, root_st.st_dev );
|
||||||
{
|
if (!status) /* get rid of "./" prefix */
|
||||||
if (!(dir = opendir( "." ))) status = FILE_GetNtStatus();
|
memmove( unix_name->Buffer, unix_name->Buffer + 2, strlen(unix_name->Buffer) - 1 );
|
||||||
else
|
flush_dir_queue();
|
||||||
{
|
|
||||||
while ((de = readdir( dir )))
|
|
||||||
{
|
|
||||||
if (stat( de->d_name, &st ) == -1) continue;
|
|
||||||
if (st.st_dev == root_st.st_dev && st.st_ino == file_id)
|
|
||||||
{
|
|
||||||
strcpy( unix_name, de->d_name );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir( dir );
|
|
||||||
if (!unix_name[0]) status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
||||||
}
|
|
||||||
if (fchdir( old_cwd ) == -1) chdir( "/" );
|
|
||||||
}
|
|
||||||
else status = FILE_GetNtStatus();
|
|
||||||
RtlLeaveCriticalSection( &dir_section );
|
|
||||||
if (old_cwd != -1) close( old_cwd );
|
|
||||||
if (needs_close) close( root_fd );
|
|
||||||
}
|
}
|
||||||
|
if (fchdir( old_cwd ) == -1) chdir( "/" );
|
||||||
}
|
}
|
||||||
|
else status = FILE_GetNtStatus();
|
||||||
|
RtlLeaveCriticalSection( &dir_section );
|
||||||
|
if (old_cwd != -1) close( old_cwd );
|
||||||
|
|
||||||
|
done:
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name) );
|
TRACE( "%s -> %s\n", wine_dbgstr_longlong(file_id), debugstr_a(unix_name->Buffer) );
|
||||||
unix_name_ret->Buffer = unix_name;
|
unix_name->Length = strlen( unix_name->Buffer );
|
||||||
unix_name_ret->Length = strlen(unix_name);
|
|
||||||
unix_name_ret->MaximumLength = unix_len;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TRACE( "%s not found in %s\n", wine_dbgstr_longlong(file_id), unix_name );
|
TRACE( "%s not found in dir %p\n", wine_dbgstr_longlong(file_id), attr->RootDirectory );
|
||||||
RtlFreeHeap( GetProcessHeap(), 0, unix_name );
|
RtlFreeHeap( GetProcessHeap(), 0, unix_name->Buffer );
|
||||||
}
|
}
|
||||||
|
if (needs_close) close( root_fd );
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -234,7 +234,7 @@ static void create_file_test(void)
|
||||||
static void open_file_test(void)
|
static void open_file_test(void)
|
||||||
{
|
{
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
HANDLE dir, handle;
|
HANDLE dir, root, handle;
|
||||||
WCHAR path[MAX_PATH];
|
WCHAR path[MAX_PATH];
|
||||||
BYTE data[8192];
|
BYTE data[8192];
|
||||||
OBJECT_ATTRIBUTES attr;
|
OBJECT_ATTRIBUTES attr;
|
||||||
|
@ -256,6 +256,13 @@ static void open_file_test(void)
|
||||||
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
pRtlFreeUnicodeString( &nameW );
|
pRtlFreeUnicodeString( &nameW );
|
||||||
|
|
||||||
|
path[3] = 0; /* root of the drive */
|
||||||
|
pRtlDosPathNameToNtPathName_U( path, &nameW, NULL, NULL );
|
||||||
|
status = pNtOpenFile( &root, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_DIRECTORY_FILE );
|
||||||
|
ok( !status, "open %s failed %x\n", wine_dbgstr_w(nameW.Buffer), status );
|
||||||
|
pRtlFreeUnicodeString( &nameW );
|
||||||
|
|
||||||
/* test opening system dir with RootDirectory set to windows dir */
|
/* test opening system dir with RootDirectory set to windows dir */
|
||||||
GetSystemDirectoryW( path, MAX_PATH );
|
GetSystemDirectoryW( path, MAX_PATH );
|
||||||
while (path[len] == '\\') len++;
|
while (path[len] == '\\') len++;
|
||||||
|
@ -306,6 +313,7 @@ static void open_file_test(void)
|
||||||
nameW.Buffer = (WCHAR *)&info->FileId;
|
nameW.Buffer = (WCHAR *)&info->FileId;
|
||||||
nameW.Length = sizeof(info->FileId);
|
nameW.Length = sizeof(info->FileId);
|
||||||
info->FileName[info->FileNameLength/sizeof(WCHAR)] = 0;
|
info->FileName[info->FileNameLength/sizeof(WCHAR)] = 0;
|
||||||
|
attr.RootDirectory = dir;
|
||||||
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
FILE_OPEN_BY_FILE_ID |
|
FILE_OPEN_BY_FILE_ID |
|
||||||
|
@ -330,6 +338,16 @@ static void open_file_test(void)
|
||||||
"mismatched write time for %s\n", wine_dbgstr_w(info->FileName));
|
"mismatched write time for %s\n", wine_dbgstr_w(info->FileName));
|
||||||
}
|
}
|
||||||
CloseHandle( handle );
|
CloseHandle( handle );
|
||||||
|
|
||||||
|
/* try same thing from drive root */
|
||||||
|
attr.RootDirectory = root;
|
||||||
|
status = pNtOpenFile( &handle, GENERIC_READ, &attr, &io,
|
||||||
|
FILE_SHARE_READ|FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN_BY_FILE_ID |
|
||||||
|
((info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_DIRECTORY_FILE : 0) );
|
||||||
|
ok( status == STATUS_SUCCESS || status == STATUS_NOT_IMPLEMENTED,
|
||||||
|
"open %s failed %x\n", wine_dbgstr_w(info->FileName), status );
|
||||||
|
if (!status) CloseHandle( handle );
|
||||||
}
|
}
|
||||||
next:
|
next:
|
||||||
if (!info->NextEntryOffset) break;
|
if (!info->NextEntryOffset) break;
|
||||||
|
@ -338,6 +356,7 @@ static void open_file_test(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle( dir );
|
CloseHandle( dir );
|
||||||
|
CloseHandle( root );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_file_test(void)
|
static void delete_file_test(void)
|
||||||
|
|
Loading…
Reference in New Issue