浏览代码

Bug 2056: File on local network share treated as folder when uploading

https://winscp.net/tracker/2056

Source commit: 8193faf5d491ec33d128da1405dc9f88faa123ab
Martin Prikryl 2 年之前
父节点
当前提交
c0b78d0056
共有 2 个文件被更改,包括 45 次插入23 次删除
  1. 44 22
      source/core/Common.cpp
  2. 1 1
      source/core/Common.h

+ 44 - 22
source/core/Common.cpp

@@ -1599,32 +1599,54 @@ void __fastcall ProcessLocalDirectory(UnicodeString DirName,
   }
 }
 //---------------------------------------------------------------------------
-int __fastcall FileGetAttrFix(const UnicodeString FileName)
+int __fastcall FileGetAttrFix(const UnicodeString & FileName)
 {
-  // The default for FileGetAttr is to follow links
-  bool FollowLink = true;
-  // WORKAROUND:
-  // But the FileGetAttr when called for link with FollowLink set will always fail
-  // as it calls InternalGetFileNameFromSymLink, which test for CheckWin32Version(6, 0)
-  if (!IsWinVista())
-  {
-    FollowLink = false;
-  }
+  // Already called with ApiPath
+
   int Result;
-  try
-  {
-    Result = FileGetAttr(FileName, FollowLink);
-  }
-  catch (EOSError & E)
-  {
-    Result = -1;
-  }
-  if (Result < 0)
+  int Tries = 2;
+  do
   {
-    // When referring to files in some special symlinked locations
-    // (like a deduplicated drive or a commvault archive), the first call to GetFileAttributes fails.
-    Result = FileGetAttr(FileName, FollowLink);
+    // WORKAROUND:
+    // FileGetAttr when called for link with FollowLink set (default) will always fail on pre-Vista
+    // as it calls InternalGetFileNameFromSymLink, which test for CheckWin32Version(6, 0)
+    Result = GetFileAttributes(FileName.c_str());
+    if ((Result >= 0) && FLAGSET(Result, faSymLink) && IsWinVista())
+    {
+      try
+      {
+        UnicodeString TargetName;
+        // WORKAROUND:
+        // On Samba, InternalGetFileNameFromSymLink fails and returns true but empty target.
+        // That confuses FileGetAttr, which returns attributes of the parent folder instead.
+        // Using FileGetSymLinkTarget solves the problem, as it returns false.
+        if (!FileGetSymLinkTarget(FileName, TargetName))
+        {
+          // FileGetAttr would return faInvalid (-1), but we want to allow an upload from Samba,
+          // so returning the symlink attributes => noop
+        }
+        else
+        {
+          Result = GetFileAttributes(ApiPath(TargetName).c_str());
+        }
+      }
+      catch (EOSError & E)
+      {
+        Result = -1;
+      }
+      catch (EDirectoryNotFoundException & E) // throws by FileSystemAttributes
+      {
+        Result = -1;
+      }
+    }
+    Tries--;
   }
+  // When referring to files in some special symlinked locations
+  // (like a deduplicated drive or a commvault archive), the first call to FileGetAttr failed.
+  // Possibly this issue is resolved by our re-implementation of FileGetAttr above.
+  // But as we have no way to test it, keeping the retry here.
+  while ((Result < 0) && (Tries > 0));
+
   return Result;
 }
 //---------------------------------------------------------------------------

+ 1 - 1
source/core/Common.h

@@ -231,7 +231,7 @@ int __fastcall FindNextChecked(TSearchRecChecked & F);
 int __fastcall FindNextUnchecked(TSearchRecChecked & F);
 void __fastcall ProcessLocalDirectory(UnicodeString DirName,
   TProcessLocalFileEvent CallBackFunc, void * Param = NULL, int FindAttrs = -1);
-int __fastcall FileGetAttrFix(const UnicodeString FileName);
+int __fastcall FileGetAttrFix(const UnicodeString & FileName);
 //---------------------------------------------------------------------------
 extern const wchar_t * DSTModeNames;
 enum TDSTMode