Cygwin: fhandler_procsys::readdir: fix NtQueryDirectoryObject usage

As outlined in the previous patch, the non-atomicity of iterating
over a directory in the NT namespace via NtQueryDirectoryObject
one entry each, results in potential duplication of directory entries.

Fix this for fhandler_procsys::readdir as well by fetching the entire
dir inside fhandler_procsys::opendir, storing it in a buffer, and just
return buffer content from fhandler_procsys::readdir.

Fixes: 43f65cdd7d ("fhandler_procsys.cc: New file.")
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2021-08-19 21:17:11 +02:00
parent 2f05de4dbf
commit 5036d447c5
1 changed files with 58 additions and 24 deletions

View File

@ -309,59 +309,88 @@ fhandler_procsys::opendir (int fd)
UNICODE_STRING path; UNICODE_STRING path;
OBJECT_ATTRIBUTES attr; OBJECT_ATTRIBUTES attr;
NTSTATUS status; NTSTATUS status;
HANDLE h; HANDLE dir_hdl;
DIR *dir; DIR *dir;
mk_unicode_path (&path); mk_unicode_path (&path);
InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL); InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = NtOpenDirectoryObject (&h, DIRECTORY_QUERY, &attr); status = NtOpenDirectoryObject (&dir_hdl, DIRECTORY_QUERY, &attr);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
{ {
__seterrno_from_nt_status (status); __seterrno_from_nt_status (status);
return NULL; return NULL;
} }
void *dbi_buf = NULL;
ULONG size = 65536;
ULONG context = 0;
int iter;
for (iter = 0; iter <= 3; ++iter) /* Allows for a 512K buffer */
{
void *new_buf = realloc (dbi_buf, size);
if (!new_buf)
goto err;
dbi_buf = new_buf;
status = NtQueryDirectoryObject (dir_hdl, dbi_buf, size, FALSE, TRUE,
&context, NULL);
if (!NT_SUCCESS (status))
{
__seterrno_from_nt_status (status);
goto err;
}
if (status != STATUS_MORE_ENTRIES)
break;
size <<= 1;
}
if (iter > 3)
{
__seterrno_from_nt_status (STATUS_INSUFFICIENT_RESOURCES);
goto err;
}
if (!(dir = fhandler_virtual::opendir (fd))) if (!(dir = fhandler_virtual::opendir (fd)))
NtClose (h); goto err;
else /* Note that dir->__handle points to the buffer, it does NOT contain an
dir->__handle = h; actual handle! */
dir->__handle = dbi_buf;
/* dir->__d_internal contains the number of objects returned in the buffer. */
dir->__d_internal = context;
return dir; return dir;
err:
NtClose (dir_hdl);
free (dbi_buf);
return NULL;
} }
int int
fhandler_procsys::readdir (DIR *dir, dirent *de) fhandler_procsys::readdir (DIR *dir, dirent *de)
{ {
NTSTATUS status; PDIRECTORY_BASIC_INFORMATION dbi;
struct fdbi
{
DIRECTORY_BASIC_INFORMATION dbi;
WCHAR buf[2][NAME_MAX + 1];
} f;
int res = EBADF; int res = EBADF;
if (dir->__handle != INVALID_HANDLE_VALUE) if (dir->__handle != INVALID_HANDLE_VALUE)
{ {
BOOLEAN restart = dir->__d_position ? FALSE : TRUE; dbi = ((PDIRECTORY_BASIC_INFORMATION) dir->__handle);
status = NtQueryDirectoryObject (dir->__handle, &f, sizeof f, TRUE, dbi += dir->__d_position;
restart, (PULONG) &dir->__d_position, if (dir->__d_position >= (__int32_t) dir->__d_internal
NULL); || dbi->ObjectName.Length == 0)
if (!NT_SUCCESS (status))
res = ENMFILE; res = ENMFILE;
else else
{ {
sys_wcstombs (de->d_name, NAME_MAX + 1, f.dbi.ObjectName.Buffer, sys_wcstombs (de->d_name, NAME_MAX + 1, dbi->ObjectName.Buffer,
f.dbi.ObjectName.Length / sizeof (WCHAR)); dbi->ObjectName.Length / sizeof (WCHAR));
de->d_ino = hash_path_name (get_ino (), de->d_name); de->d_ino = hash_path_name (get_ino (), de->d_name);
if (RtlEqualUnicodeString (&f.dbi.ObjectTypeName, &ro_u_natdir, if (RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natdir, FALSE))
FALSE))
de->d_type = DT_DIR; de->d_type = DT_DIR;
else if (RtlEqualUnicodeString (&f.dbi.ObjectTypeName, &ro_u_natsyml, else if (RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natsyml,
FALSE)) FALSE))
de->d_type = DT_LNK; de->d_type = DT_LNK;
else if (!RtlEqualUnicodeString (&f.dbi.ObjectTypeName, &ro_u_natdev, else if (!RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natdev,
FALSE)) FALSE))
de->d_type = DT_CHR; de->d_type = DT_CHR;
else /* Can't nail down "Device" objects without further testing. */ else /* Can't nail down "Device" objects without further testing. */
de->d_type = DT_UNKNOWN; de->d_type = DT_UNKNOWN;
++dir->__d_position;
res = 0; res = 0;
} }
} }
@ -378,7 +407,12 @@ fhandler_procsys::telldir (DIR *dir)
void void
fhandler_procsys::seekdir (DIR *dir, long pos) fhandler_procsys::seekdir (DIR *dir, long pos)
{ {
dir->__d_position = pos; if (pos < 0)
dir->__d_position = 0;
else if (pos > (__int32_t) dir->__d_internal)
dir->__d_position = (__int32_t) dir->__d_internal;
else
dir->__d_position = pos;
} }
int int
@ -386,7 +420,7 @@ fhandler_procsys::closedir (DIR *dir)
{ {
if (dir->__handle != INVALID_HANDLE_VALUE) if (dir->__handle != INVALID_HANDLE_VALUE)
{ {
NtClose (dir->__handle); free (dir->__handle);
dir->__handle = INVALID_HANDLE_VALUE; dir->__handle = INVALID_HANDLE_VALUE;
} }
return fhandler_virtual::closedir (dir); return fhandler_virtual::closedir (dir);