From ceb069628acc263e5c1a9ed274f8d00eeb9c4dd8 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
Date: Wed, 1 May 2019 12:06:20 -0600
Subject: ntdll: Always report symbolic links as containing zero bytes.

Signed-off-by: Erich E. Hoover <erich.e.hoover@gmail.com>
---
 dlls/ntdll/tests/file.c | 46 +++++++++++++++++++++++++++++++++++++++--
 dlls/ntdll/unix/file.c  |  2 ++
 server/fd.c             |  3 ++-
 3 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index ddd3d693659..e0a6e3b3143 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -5037,7 +5037,9 @@ static void test_reparse_points(void)
     static const WCHAR dotW[] = {'.',0};
     REPARSE_DATA_BUFFER *buffer = NULL;
     DWORD dwret, dwLen, dwFlags, err;
+    WIN32_FILE_ATTRIBUTE_DATA fad;
     INT buffer_len, string_len;
+    WCHAR buf[] = {0,0,0,0};
     HANDLE handle, token;
     IO_STATUS_BLOCK iosb;
     UNICODE_STRING nameW;
@@ -5165,8 +5167,6 @@ static void test_reparse_points(void)
        "Unexpected junction point attributes (0x%x != 0x410)!\n", dwret);
     bret = RemoveDirectoryW(target_path);
     ok(bret, "Failed to delete junction point target!\n");
-    bret = CreateDirectoryW(target_path, NULL);
-    ok(bret, "Failed to create junction point target directory.\n");
 
     /* Establish permissions for symlink creation */
     bret = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token);
@@ -5191,6 +5191,13 @@ static void test_reparse_points(void)
     /* Delete the junction point directory and create a blank slate for symlink tests */
     bret = RemoveDirectoryW(reparse_path);
     ok(bret, "Failed to delete junction point!\n");
+    handle = CreateFileW(target_path, GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0);
+    ok(handle != INVALID_HANDLE_VALUE, "Failed to create symlink target file.\n");
+    bret = WriteFile(handle, fooW, sizeof(fooW), &dwLen, NULL);
+    ok(bret, "Failed to write data to the symlink target file.\n");
+    ok(GetFileSize(handle, NULL) == sizeof(fooW), "target size is incorrect (%d vs %d)\n",
+       GetFileSize(handle, NULL), sizeof(fooW));
+    CloseHandle(handle);
 
     /* Create the file symlink */
     HeapFree(GetProcessHeap(), 0, buffer);
@@ -5204,6 +5211,37 @@ static void test_reparse_points(void)
     ok(bret, "Failed to create symlink! (0x%x)\n", GetLastError());
     CloseHandle(handle);
 
+    /* Check the size of the symlink */
+    bret = GetFileAttributesExW(reparse_path, GetFileExInfoStandard, &fad);
+    ok(bret, "Failed to read file attributes from the symlink target.\n");
+    ok(fad.nFileSizeLow == 0 && fad.nFileSizeHigh == 0, "Size of symlink is not zero.\n");
+    handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
+                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
+    ok(handle != INVALID_HANDLE_VALUE, "Failed to open symlink file.\n");
+    todo_wine ok(GetFileSize(handle, NULL) == 0, "symlink size is not zero\n");
+    bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
+    ok(bret, "Failed to read data from the symlink.\n");
+    todo_wine ok(dwLen == 0, "Length of symlink data is not zero.\n");
+    CloseHandle(handle);
+
+    /* Check the size/data of the symlink target */
+    handle = CreateFileW(reparse_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING,
+                         FILE_FLAG_BACKUP_SEMANTICS, 0);
+    if (handle == INVALID_HANDLE_VALUE)
+    {
+        win_skip("Failed to open symlink file handle (0x%x).\n", GetLastError());
+        goto cleanup;
+    }
+    ok(GetFileSize(handle, NULL) == sizeof(fooW), "symlink target size does not match (%d != %d)\n",
+       GetFileSize(handle, NULL), sizeof(fooW));
+    bret = ReadFile(handle, &buf, sizeof(buf), &dwLen, NULL);
+    ok(bret, "Failed to read data from the symlink.\n");
+    ok(dwLen == sizeof(fooW), "Length of symlink target data does not match (%d != %d).\n",
+       dwLen, sizeof(fooW));
+    ok(!memcmp(fooW, &buf, sizeof(fooW)), "Symlink target data does not match (%s != %s).\n",
+       wine_dbgstr_wn(buf, dwLen), wine_dbgstr_w(fooW));
+    CloseHandle(handle);
+
     /* Check deleting a file symlink as if it were a directory */
     bret = RemoveDirectoryW(reparse_path);
     ok(!bret, "Succeeded in deleting file symlink as a directory!\n");
@@ -5225,6 +5263,10 @@ static void test_reparse_points(void)
     dwret = GetFileAttributesW(reparse_path);
     ok(dwret != (DWORD)~0, "Path doesn't exist (attributes: 0x%x)!\n", dwret);
     ok(!(dwret & FILE_ATTRIBUTE_REPARSE_POINT), "File is already a reparse point! (attributes: %d)\n", dwret);
+    bret = DeleteFileW(target_path);
+    ok(bret, "Failed to delete symlink target!\n");
+    bret = CreateDirectoryW(target_path, NULL);
+    ok(bret, "Failed to create symlink target directory.\n");
 
     /* Create the directory symlink */
     HeapFree(GetProcessHeap(), 0, buffer);
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c
index ca21a799231..bdc8b8c756f 100644
--- a/dlls/ntdll/unix/file.c
+++ b/dlls/ntdll/unix/file.c
@@ -1507,6 +1507,8 @@ static int get_file_info( const char *path, struct stat *st, ULONG *attr )
 
         /* return information about the destination (unless this is a dangling symlink) */
         stat( path, st );
+        /* symbolic links always report size 0 */
+        st->st_size = 0;
         /* symbolic links (either junction points or NT symlinks) are "reparse points" */
         *attr |= FILE_ATTRIBUTE_REPARSE_POINT;
         /* whether a reparse point is a file or a directory is stored inside the link target */
diff --git a/server/fd.c b/server/fd.c
index 9e087917c0d..03966cd3067 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1969,6 +1969,7 @@ struct fd *open_fd( struct fd *root, const char *name, int flags, mode_t *mode,
     {
         fd->unlink_name = path;
         fd->unix_name = realpath( path, NULL );
+        if (!fd->unix_name) fd->unix_name = dup_fd_name( root, name ); /* dangling symlink */
     }
 
     closed_fd->unix_fd = fd->unix_fd;
@@ -2487,7 +2488,7 @@ static void set_fd_disposition( struct fd *fd, int unlink )
             file_set_error();
             return;
         }
-        if (S_ISREG( st.st_mode ))  /* can't unlink files we don't have permission to write */
+        if (S_ISREG( st.st_mode ) || S_ISLNK( st.st_mode ))  /* can't unlink files we don't have permission to write */
         {
             if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
             {
-- 
2.17.1

