It seems like a good thing to avoid changing the process working directory, and the *at() functions are available on all recent OSes (macOS being the laggard, didn't add them until 10.10). I'm planning to do this elsewhere in `ntdll/unix/file.c` as well.
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/ntdll/unix/file.c | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 9fbd49e1821..963012ecddd 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -3347,9 +3347,10 @@ static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_re * * Recursively search directories from the dir queue for a given inode. */ -static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev ) +static NTSTATUS find_file_id( int root_fd, char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev ) { unsigned int pos; + int dir_fd; DIR *dir; struct dirent *de; NTSTATUS status; @@ -3358,7 +3359,12 @@ static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, d
while (!(status = next_dir_in_queue( name ))) { - if (!(dir = opendir( name ))) continue; + if ((dir_fd = openat( root_fd, name, O_RDONLY )) == -1) continue; + if (!(dir = fdopendir( dir_fd ))) + { + close(dir_fd); + continue; + } TRACE( "searching %s for %s\n", debugstr_a(name), wine_dbgstr_longlong(file_id) ); pos = strlen( name ); if (pos + MAX_DIR_ENTRY_LEN >= *len / sizeof(WCHAR)) @@ -3376,7 +3382,7 @@ static NTSTATUS find_file_id( char **unix_name, ULONG *len, ULONGLONG file_id, d { if (!strcmp( de->d_name, "." ) || !strcmp( de->d_name, ".." )) continue; strcpy( name + pos, de->d_name ); - if (lstat( name, &st ) == -1) continue; + if (fstatat( root_fd, name, &st, AT_SYMLINK_NOFOLLOW ) == -1) continue; if (st.st_dev != dev) continue; if (st.st_ino == file_id) { @@ -3405,7 +3411,7 @@ static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char * UNICODE_STRING *nt_name ) { enum server_fd_type type; - int old_cwd, root_fd, needs_close; + int root_fd, needs_close; char *unix_name; ULONG len; NTSTATUS status; @@ -3437,29 +3443,23 @@ static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char * goto done; }
- mutex_lock( &dir_mutex ); - if ((old_cwd = open( ".", O_RDONLY )) != -1 && fchdir( root_fd ) != -1) + /* shortcut for ".." */ + if (!fstatat( root_fd, "..", &st, 0 ) && st.st_dev == root_st.st_dev && st.st_ino == file_id) { - /* shortcut for ".." */ - if (!stat( "..", &st ) && st.st_dev == root_st.st_dev && st.st_ino == file_id) - { - strcpy( unix_name, ".." ); - status = STATUS_SUCCESS; - } - else - { - status = add_dir_to_queue( "." ); - if (!status) - status = find_file_id( &unix_name, &len, file_id, root_st.st_dev ); - if (!status) /* get rid of "./" prefix */ - memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 ); - flush_dir_queue(); - } - if (fchdir( old_cwd ) == -1) chdir( "/" ); + strcpy( unix_name, ".." ); + status = STATUS_SUCCESS; + } + else + { + mutex_lock( &dir_mutex ); + status = add_dir_to_queue( "." ); + if (!status) + status = find_file_id( root_fd, &unix_name, &len, file_id, root_st.st_dev ); + if (!status) /* get rid of "./" prefix */ + memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 ); + flush_dir_queue(); + mutex_unlock( &dir_mutex ); } - else status = errno_to_status( errno ); - mutex_unlock( &dir_mutex ); - if (old_cwd != -1) close( old_cwd );
done: if (status == STATUS_SUCCESS)
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/ntdll/unix/file.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index 963012ecddd..007e5a876c9 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -579,21 +579,19 @@ struct dir_name char name[1]; };
-static struct list dir_queue = LIST_INIT( dir_queue ); - -static NTSTATUS add_dir_to_queue( const char *name ) +static NTSTATUS add_dir_to_queue( struct list *queue, const char *name ) { int len = strlen( name ) + 1; struct dir_name *dir = malloc( offsetof( struct dir_name, name[len] )); if (!dir) return STATUS_NO_MEMORY; strcpy( dir->name, name ); - list_add_tail( &dir_queue, &dir->entry ); + list_add_tail( queue, &dir->entry ); return STATUS_SUCCESS; }
-static NTSTATUS next_dir_in_queue( char *name ) +static NTSTATUS next_dir_in_queue( struct list *queue, char *name ) { - struct list *head = list_head( &dir_queue ); + struct list *head = list_head( queue ); if (head) { struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry ); @@ -605,11 +603,11 @@ static NTSTATUS next_dir_in_queue( char *name ) return STATUS_OBJECT_NAME_NOT_FOUND; }
-static void flush_dir_queue(void) +static void flush_dir_queue( struct list *queue ) { struct list *head;
- while ((head = list_head( &dir_queue ))) + while ((head = list_head( queue ))) { struct dir_name *dir = LIST_ENTRY( head, struct dir_name, entry ); list_remove( &dir->entry ); @@ -3347,7 +3345,7 @@ static NTSTATUS find_drive_rootA( LPCSTR *ppath, unsigned int len, int *drive_re * * Recursively search directories from the dir queue for a given inode. */ -static NTSTATUS find_file_id( int root_fd, char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev ) +static NTSTATUS find_file_id( int root_fd, char **unix_name, ULONG *len, ULONGLONG file_id, dev_t dev, struct list *dir_queue ) { unsigned int pos; int dir_fd; @@ -3357,7 +3355,7 @@ static NTSTATUS find_file_id( int root_fd, char **unix_name, ULONG *len, ULONGLO struct stat st; char *name = *unix_name;
- while (!(status = next_dir_in_queue( name ))) + while (!(status = next_dir_in_queue( dir_queue, name ))) { if ((dir_fd = openat( root_fd, name, O_RDONLY )) == -1) continue; if (!(dir = fdopendir( dir_fd ))) @@ -3390,7 +3388,7 @@ static NTSTATUS find_file_id( int root_fd, char **unix_name, ULONG *len, ULONGLO return STATUS_SUCCESS; } if (!S_ISDIR( st.st_mode )) continue; - if ((status = add_dir_to_queue( name )) != STATUS_SUCCESS) + if ((status = add_dir_to_queue( dir_queue, name )) != STATUS_SUCCESS) { closedir( dir ); return status; @@ -3417,6 +3415,7 @@ static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char * NTSTATUS status; ULONGLONG file_id; struct stat st, root_st; + struct list dir_queue = LIST_INIT( dir_queue );
nt_name->Buffer = NULL; if (attr->ObjectName->Length != sizeof(ULONGLONG)) return STATUS_OBJECT_PATH_SYNTAX_BAD; @@ -3451,14 +3450,12 @@ static NTSTATUS file_id_to_unix_file_name( const OBJECT_ATTRIBUTES *attr, char * } else { - mutex_lock( &dir_mutex ); - status = add_dir_to_queue( "." ); + status = add_dir_to_queue( &dir_queue, "." ); if (!status) - status = find_file_id( root_fd, &unix_name, &len, file_id, root_st.st_dev ); + status = find_file_id( root_fd, &unix_name, &len, file_id, root_st.st_dev, &dir_queue ); if (!status) /* get rid of "./" prefix */ memmove( unix_name, unix_name + 2, strlen(unix_name) - 1 ); - flush_dir_queue(); - mutex_unlock( &dir_mutex ); + flush_dir_queue( &dir_queue ); }
done: