ntdll: Use swapping method to return . and .. as first entries.
This commit is contained in:
parent
fbf537fb60
commit
006413afe4
|
@ -1486,22 +1486,56 @@ static int read_directory_vfat( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG
|
||||||
#endif /* VFAT_IOCTL_READDIR_BOTH */
|
#endif /* VFAT_IOCTL_READDIR_BOTH */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_GETDENTS
|
||||||
|
/***********************************************************************
|
||||||
|
* read_first_dent_name
|
||||||
|
*
|
||||||
|
* reads name of first or second dentry (if they have inodes).
|
||||||
|
*/
|
||||||
|
static char *read_first_dent_name( int which, int fd, off_t second_offs, KERNEL_DIRENT64 *de_first_two,
|
||||||
|
char *buffer, size_t size, BOOL *buffer_changed )
|
||||||
|
{
|
||||||
|
KERNEL_DIRENT64 *de;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
de = de_first_two;
|
||||||
|
if (de != NULL)
|
||||||
|
{
|
||||||
|
if (which == 1)
|
||||||
|
de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
|
||||||
|
|
||||||
|
return de->d_ino ? de->d_name : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buffer_changed = TRUE;
|
||||||
|
lseek( fd, which == 1 ? second_offs : 0, SEEK_SET );
|
||||||
|
res = getdents64( fd, buffer, size );
|
||||||
|
if (res <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
de = (KERNEL_DIRENT64 *)buffer;
|
||||||
|
return de->d_ino ? de->d_name : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* read_directory_getdents
|
* read_directory_getdents
|
||||||
*
|
*
|
||||||
* Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile.
|
* Read a directory using the Linux getdents64 system call; helper for NtQueryDirectoryFile.
|
||||||
*/
|
*/
|
||||||
#ifdef USE_GETDENTS
|
|
||||||
static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
|
static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, ULONG length,
|
||||||
BOOLEAN single_entry, const UNICODE_STRING *mask,
|
BOOLEAN single_entry, const UNICODE_STRING *mask,
|
||||||
BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
|
BOOLEAN restart_scan, FILE_INFORMATION_CLASS class )
|
||||||
{
|
{
|
||||||
off_t old_pos = 0;
|
static off_t second_entry_pos;
|
||||||
|
static struct file_identity last_dir_id;
|
||||||
|
off_t old_pos = 0, next_pos;
|
||||||
size_t size = length;
|
size_t size = length;
|
||||||
int res, fake_dot_dot = 1;
|
|
||||||
char *data, local_buffer[8192];
|
char *data, local_buffer[8192];
|
||||||
KERNEL_DIRENT64 *de;
|
KERNEL_DIRENT64 *de, *de_first_two = NULL;
|
||||||
union file_directory_info *info, *last_info = NULL;
|
union file_directory_info *info, *last_info = NULL;
|
||||||
|
const char *filename;
|
||||||
|
BOOL data_buffer_changed;
|
||||||
|
int res, swap_to;
|
||||||
|
|
||||||
if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
|
if (size <= sizeof(local_buffer) || !(data = RtlAllocateHeap( GetProcessHeap(), 0, size )))
|
||||||
{
|
{
|
||||||
|
@ -1510,7 +1544,7 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restart_scan) lseek( fd, 0, SEEK_SET );
|
if (restart_scan) lseek( fd, 0, SEEK_SET );
|
||||||
else if (length < max_dir_info_size(class)) /* we may have to return a partial entry here */
|
else
|
||||||
{
|
{
|
||||||
old_pos = lseek( fd, 0, SEEK_CUR );
|
old_pos = lseek( fd, 0, SEEK_CUR );
|
||||||
if (old_pos == -1 && errno == ENOENT)
|
if (old_pos == -1 && errno == ENOENT)
|
||||||
|
@ -1522,6 +1556,21 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U
|
||||||
}
|
}
|
||||||
|
|
||||||
io->u.Status = STATUS_SUCCESS;
|
io->u.Status = STATUS_SUCCESS;
|
||||||
|
de = (KERNEL_DIRENT64 *)data;
|
||||||
|
|
||||||
|
/* if old_pos is not 0 we don't know how many entries have been returned already,
|
||||||
|
* so maintain second_entry_pos to know when to return '..' */
|
||||||
|
if (old_pos != 0 && (last_dir_id.dev != curdir.dev || last_dir_id.ino != curdir.ino))
|
||||||
|
{
|
||||||
|
lseek( fd, 0, SEEK_SET );
|
||||||
|
res = getdents64( fd, data, size );
|
||||||
|
if (res > 0)
|
||||||
|
{
|
||||||
|
second_entry_pos = de->d_off;
|
||||||
|
last_dir_id = curdir;
|
||||||
|
}
|
||||||
|
lseek( fd, old_pos, SEEK_SET );
|
||||||
|
}
|
||||||
|
|
||||||
res = getdents64( fd, data, size );
|
res = getdents64( fd, data, size );
|
||||||
if (res == -1)
|
if (res == -1)
|
||||||
|
@ -1534,52 +1583,46 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
de = (KERNEL_DIRENT64 *)data;
|
if (old_pos == 0 && res > 0)
|
||||||
|
|
||||||
if (restart_scan)
|
|
||||||
{
|
{
|
||||||
/* check if we got . and .. from getdents */
|
second_entry_pos = de->d_off;
|
||||||
if (res > 0)
|
last_dir_id = curdir;
|
||||||
{
|
if (res > de->d_reclen)
|
||||||
if (!strcmp( de->d_name, "." ) && res > de->d_reclen)
|
de_first_two = de;
|
||||||
{
|
|
||||||
KERNEL_DIRENT64 *next_de = (KERNEL_DIRENT64 *)(data + de->d_reclen);
|
|
||||||
if (!strcmp( next_de->d_name, ".." )) fake_dot_dot = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* make sure we have enough room for both entries */
|
|
||||||
if (fake_dot_dot)
|
|
||||||
{
|
|
||||||
const ULONG min_info_size = dir_info_size( class, 1 ) + dir_info_size( class, 2 );
|
|
||||||
if (length < min_info_size || single_entry)
|
|
||||||
{
|
|
||||||
FIXME( "not enough room %u/%u for fake . and .. entries\n", length, single_entry );
|
|
||||||
fake_dot_dot = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fake_dot_dot)
|
|
||||||
{
|
|
||||||
if ((info = append_entry( buffer, io, length, ".", NULL, mask, class )))
|
|
||||||
last_info = info;
|
|
||||||
if ((info = append_entry( buffer, io, length, "..", NULL, mask, class )))
|
|
||||||
last_info = info;
|
|
||||||
|
|
||||||
/* check if we still have enough space for the largest possible entry */
|
|
||||||
if (last_info && io->Information + max_dir_info_size(class) > length)
|
|
||||||
{
|
|
||||||
lseek( fd, 0, SEEK_SET ); /* reset pos to first entry */
|
|
||||||
res = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (res > 0)
|
while (res > 0)
|
||||||
{
|
{
|
||||||
res -= de->d_reclen;
|
res -= de->d_reclen;
|
||||||
if (de->d_ino &&
|
next_pos = de->d_off;
|
||||||
!(fake_dot_dot && (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))) &&
|
filename = NULL;
|
||||||
(info = append_entry( buffer, io, length, de->d_name, NULL, mask, class )))
|
|
||||||
|
/* we must return first 2 entries as "." and "..", but getdents64()
|
||||||
|
* can return them anywhere, so swap first entries with "." and ".." */
|
||||||
|
if (old_pos == 0)
|
||||||
|
filename = ".";
|
||||||
|
else if (old_pos == second_entry_pos)
|
||||||
|
filename = "..";
|
||||||
|
else if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." ))
|
||||||
|
{
|
||||||
|
swap_to = !strcmp( de->d_name, "." ) ? 0 : 1;
|
||||||
|
data_buffer_changed = FALSE;
|
||||||
|
|
||||||
|
filename = read_first_dent_name( swap_to, fd, second_entry_pos, de_first_two,
|
||||||
|
data, size, &data_buffer_changed );
|
||||||
|
if (filename != NULL && (!strcmp( filename, "." ) || !strcmp( filename, ".." )))
|
||||||
|
filename = read_first_dent_name( swap_to ^ 1, fd, second_entry_pos, NULL,
|
||||||
|
data, size, &data_buffer_changed );
|
||||||
|
if (data_buffer_changed)
|
||||||
|
{
|
||||||
|
lseek( fd, next_pos, SEEK_SET );
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (de->d_ino)
|
||||||
|
filename = de->d_name;
|
||||||
|
|
||||||
|
if (filename && (info = append_entry( buffer, io, length, filename, NULL, mask, class )))
|
||||||
{
|
{
|
||||||
last_info = info;
|
last_info = info;
|
||||||
if (io->u.Status == STATUS_BUFFER_OVERFLOW)
|
if (io->u.Status == STATUS_BUFFER_OVERFLOW)
|
||||||
|
@ -1590,17 +1633,18 @@ static int read_directory_getdents( int fd, IO_STATUS_BLOCK *io, void *buffer, U
|
||||||
/* check if we still have enough space for the largest possible entry */
|
/* check if we still have enough space for the largest possible entry */
|
||||||
if (single_entry || io->Information + max_dir_info_size(class) > length)
|
if (single_entry || io->Information + max_dir_info_size(class) > length)
|
||||||
{
|
{
|
||||||
if (res > 0) lseek( fd, de->d_off, SEEK_SET ); /* set pos to next entry */
|
if (res > 0) lseek( fd, next_pos, SEEK_SET ); /* set pos to next entry */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
old_pos = de->d_off;
|
old_pos = next_pos;
|
||||||
/* move on to the next entry */
|
/* move on to the next entry */
|
||||||
if (res > 0) de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
|
if (res > 0) de = (KERNEL_DIRENT64 *)((char *)de + de->d_reclen);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res = getdents64( fd, data, size );
|
res = getdents64( fd, data, size );
|
||||||
de = (KERNEL_DIRENT64 *)data;
|
de = (KERNEL_DIRENT64 *)data;
|
||||||
|
de_first_two = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,16 +215,9 @@ static void test_flags_NtQueryDirectoryFile(OBJECT_ATTRIBUTES *attr, const char
|
||||||
}
|
}
|
||||||
ok(numfiles < max_test_dir_size, "too many loops\n");
|
ok(numfiles < max_test_dir_size, "too many loops\n");
|
||||||
|
|
||||||
for (i=0; testfiles[i].name; i++) {
|
for (i=0; testfiles[i].name; i++)
|
||||||
if ((strcmp(testfiles[i].name, ".") == 0 || strcmp(testfiles[i].name, "..") == 0) && (single_entry || !restart_flag)) {
|
ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (ReturnSingleEntry=%d,RestartScan=%d)\n",
|
||||||
todo_wine
|
testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag);
|
||||||
ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (ReturnSingleEntry=%d,RestartScan=%d)\n",
|
|
||||||
testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag);
|
|
||||||
} else {
|
|
||||||
ok(testfiles[i].nfound == 1, "Wrong number %d of %s files found (ReturnSingleEntry=%d,RestartScan=%d)\n",
|
|
||||||
testfiles[i].nfound, testfiles[i].description, single_entry, restart_flag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pNtClose(dirh);
|
pNtClose(dirh);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue