[PATCH] server: Try to get file name for regular files and dirs in alloc_file_handle().

Paul Gofman pgofman at codeweavers.com
Thu May 14 15:05:12 CDT 2020


Std handles can be set from Unix fds in ntdll:init_user_process_params().
The resulting handles may refer to regular files but without unix_name
set in server struct fd. As a result, GetFileType() returns FILE_TYPE_DISK
but NtQueryInformationFile() fails through server_get_unix_name().

Fixes crash on start in Unity of Command II (failure is from python38.dll).

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/ntdll/tests/file.c | 12 +++++++++
 server/fd.c             | 17 +++++++++---
 server/file.c           | 60 ++++++++++++++++++++++++++++++++++++++---
 server/file.h           |  4 ++-
 server/mapping.c        |  2 +-
 5 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 31c18454f0..78a6ac2ee5 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -1406,6 +1406,7 @@ static void test_file_all_information(void)
       FILE_ALL_INFORMATION fai;
       WCHAR buf[256];
     } fai_buf;
+    DWORD file_type;
     HANDLE h;
     int res;
     int attrib_mask = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL;
@@ -1463,6 +1464,17 @@ static void test_file_all_information(void)
     todo_wine ok ( (fai_buf.fai.BasicInformation.FileAttributes & attrib_mask) == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fai_buf.fai.BasicInformation.FileAttributes );
 
     CloseHandle( h );
+
+    h = GetStdHandle(STD_OUTPUT_HANDLE);
+    if ((file_type = GetFileType(h)) == FILE_TYPE_DISK)
+    {
+        res = pNtQueryInformationFile(h, &io, &fai_buf.fai, sizeof fai_buf, FileAllInformation);
+        ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    }
+    else
+    {
+        skip("Skipping test for stdout file type %#x.\n", file_type);
+    }
 }
 
 static void delete_object( WCHAR *path )
diff --git a/server/fd.c b/server/fd.c
index 39fb419f25..5f38128a48 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1945,10 +1945,10 @@ error:
     return NULL;
 }
 
-/* create an fd for an anonymous file */
+/* create an fd for an unix fd */
 /* if the function fails the unix fd is closed */
-struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, struct object *user,
-                                unsigned int options )
+struct fd *create_fd_from_unix_fd( const struct fd_ops *fd_user_ops, int unix_fd, struct object *user,
+                                unsigned int options, const char *unix_name )
 {
     struct fd *fd = alloc_fd_object();
 
@@ -1957,12 +1957,23 @@ struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, s
         set_fd_user( fd, fd_user_ops, user );
         fd->unix_fd = unix_fd;
         fd->options = options;
+        if (unix_name)
+            fd->unix_name = strdup( unix_name );
+
         return fd;
     }
     close( unix_fd );
     return NULL;
 }
 
+/* create an fd for an anonymous file */
+/* if the function fails the unix fd is closed */
+struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops, int unix_fd, struct object *user,
+                                unsigned int options )
+{
+    return create_fd_from_unix_fd( fd_user_ops, unix_fd, user, options, NULL );
+}
+
 /* retrieve the object that is using an fd */
 void *get_fd_user( struct fd *fd )
 {
diff --git a/server/file.c b/server/file.c
index bce202138e..f43373b085 100644
--- a/server/file.c
+++ b/server/file.c
@@ -118,7 +118,7 @@ static const struct fd_ops file_fd_ops =
 
 /* create a file from a file descriptor */
 /* if the function fails the fd is closed */
-struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing )
+struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing, int needs_name )
 {
     struct file *file;
     struct stat st;
@@ -139,8 +139,60 @@ struct file *create_file_for_fd( int fd, unsigned int access, unsigned int shari
     file->mode = st.st_mode;
     file->access = default_fd_map_access( &file->obj, access );
     list_init( &file->kernel_object );
-    if (!(file->fd = create_anonymous_fd( &file_fd_ops, fd, &file->obj,
-                                          FILE_SYNCHRONOUS_IO_NONALERT )))
+
+#if defined(linux)
+    if (needs_name && (S_ISREG(file->mode) || S_ISDIR(file->mode)))
+    {
+        char *file_name = NULL;
+        char link[35];
+        ssize_t ret, current_size = MAX_PATH + 1;
+
+        sprintf( link, "/proc/self/fd/%u", fd );
+
+        while (1)
+        {
+            if (!(file_name = malloc( current_size )))
+            {
+                set_error( STATUS_NO_MEMORY );
+                break;
+            }
+
+            if ((ret = readlink( link, file_name, current_size )) < 0)
+            {
+                file_set_error();
+                free(file_name);
+                file_name = NULL;
+                break;
+            }
+
+            if (ret < current_size)
+            {
+                file_name[ret] = 0;
+                break;
+            }
+
+            current_size *= 2;
+            free(file_name);
+        }
+
+        if (!file_name)
+        {
+            release_object( file );
+            close( fd );
+            return NULL;
+        }
+        file->fd = create_fd_from_unix_fd( &file_fd_ops, fd, &file->obj,
+                                        FILE_SYNCHRONOUS_IO_NONALERT, file_name );
+        free(file_name);
+    }
+    else
+#endif
+    {
+        file->fd = create_anonymous_fd( &file_fd_ops, fd, &file->obj,
+                                                  FILE_SYNCHRONOUS_IO_NONALERT );
+    }
+
+    if (!file->fd)
     {
         release_object( file );
         return NULL;
@@ -758,7 +810,7 @@ DECL_HANDLER(alloc_file_handle)
         set_error( STATUS_INVALID_HANDLE );
         return;
     }
-    if ((file = create_file_for_fd( fd, req->access, FILE_SHARE_READ | FILE_SHARE_WRITE )))
+    if ((file = create_file_for_fd( fd, req->access, FILE_SHARE_READ | FILE_SHARE_WRITE, 1 )))
     {
         reply->handle = alloc_handle( current->process, file, req->access, req->attributes );
         release_object( file );
diff --git a/server/file.h b/server/file.h
index 4c454de4d4..69ec1634d2 100644
--- a/server/file.h
+++ b/server/file.h
@@ -82,6 +82,8 @@ extern struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t
                            unsigned int access, unsigned int sharing, unsigned int options );
 extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops,
                                        int unix_fd, struct object *user, unsigned int options );
+extern struct fd *create_fd_from_unix_fd( const struct fd_ops *fd_user_ops,
+                int unix_fd, struct object *user, unsigned int options, const char *unix_name );
 extern struct fd *dup_fd_object( struct fd *orig, unsigned int access, unsigned int sharing,
                                  unsigned int options );
 extern struct fd *get_fd_object_for_mapping( struct fd *fd, unsigned int access, unsigned int sharing );
@@ -156,7 +158,7 @@ extern const char *get_timeout_str( timeout_t timeout );
 extern struct file *get_file_obj( struct process *process, obj_handle_t handle,
                                   unsigned int access );
 extern int get_file_unix_fd( struct file *file );
-extern struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing );
+extern struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing, int needs_name );
 extern struct file *create_file_for_fd_obj( struct fd *fd, unsigned int access, unsigned int sharing );
 extern void file_set_error(void);
 extern struct object_type *file_get_type( struct object *obj );
diff --git a/server/mapping.c b/server/mapping.c
index 6970c86ffc..e833d51750 100644
--- a/server/mapping.c
+++ b/server/mapping.c
@@ -481,7 +481,7 @@ static int build_shared_mapping( struct mapping *mapping, int fd,
     /* create a temp file for the mapping */
 
     if ((shared_fd = create_temp_file( total_size )) == -1) return 0;
-    if (!(file = create_file_for_fd( shared_fd, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0 ))) return 0;
+    if (!(file = create_file_for_fd( shared_fd, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, 0 ))) return 0;
 
     if (!(buffer = malloc( max_size ))) goto error;
 
-- 
2.26.2




More information about the wine-devel mailing list