Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Pfister <steve.pfister@microsoft.com>2019-10-31 05:01:51 +0300
committerSteve Pfister <steve.pfister@microsoft.com>2019-10-31 05:01:51 +0300
commitd994544ba1118bfb477ded7ff9e3b40d73c0f45d (patch)
treefa60bb63bc1c198dd6555fecdd00adba7bf3072a
parent49f1c453f75e36948d0386d862378eb0dff51455 (diff)
Backport of https://github.com/dotnet/corefx/pull/34560
Fixes https://github.com/mono/mono/issues/17304
-rw-r--r--src/Common/src/Interop/Unix/System.Native/Interop.LChflags.cs26
-rw-r--r--src/Native/Unix/Common/pal_config.h.in2
-rw-r--r--src/Native/Unix/System.Native/pal_io.c28
-rw-r--r--src/Native/Unix/System.Native/pal_io.h22
-rw-r--r--src/System.IO.FileSystem/src/System.IO.FileSystem.csproj3
-rw-r--r--src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs32
-rw-r--r--src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs35
-rw-r--r--src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs13
8 files changed, 150 insertions, 11 deletions
diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.LChflags.cs b/src/Common/src/Interop/Unix/System.Native/Interop.LChflags.cs
new file mode 100644
index 0000000000..bf0ecf72b7
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Native/Interop.LChflags.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [Flags]
+ internal enum UserFlags : uint
+ {
+ UF_HIDDEN = 0x8000
+ }
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LChflags", SetLastError = true)]
+ internal static extern int LChflags(string path, uint flags);
+
+ internal static readonly bool CanSetHiddenFlag = (LChflagsCanSetHiddenFlag() != 0);
+
+ [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_LChflagsCanSetHiddenFlag")]
+ private static extern int LChflagsCanSetHiddenFlag();
+ }
+} \ No newline at end of file
diff --git a/src/Native/Unix/Common/pal_config.h.in b/src/Native/Unix/Common/pal_config.h.in
index c76d309940..033e390489 100644
--- a/src/Native/Unix/Common/pal_config.h.in
+++ b/src/Native/Unix/Common/pal_config.h.in
@@ -16,6 +16,8 @@
#cmakedefine01 HAVE_STAT_TIMESPEC
#cmakedefine01 HAVE_STAT_TIM
#cmakedefine01 HAVE_STAT_NSEC
+#cmakedefine01 HAVE_STAT_FLAGS
+#cmakedefine01 HAVE_LCHFLAGS
#cmakedefine01 HAVE_GNU_STRERROR_R
#cmakedefine01 HAVE_READDIR_R
#cmakedefine01 HAVE_DIRENT_NAME_LEN
diff --git a/src/Native/Unix/System.Native/pal_io.c b/src/Native/Unix/System.Native/pal_io.c
index c3b5aff83b..ddd56b91c9 100644
--- a/src/Native/Unix/System.Native/pal_io.c
+++ b/src/Native/Unix/System.Native/pal_io.c
@@ -166,6 +166,12 @@ static void ConvertFileStatus(const struct stat_* src, struct FileStatus* dst)
dst->BirthTime = 0;
dst->BirthTimeNsec = 0;
#endif
+
+#if defined(HAVE_STAT_FLAGS) && defined(UF_HIDDEN)
+ dst->UserFlags = ((src->st_flags & UF_HIDDEN) == UF_HIDDEN) ? PAL_UF_HIDDEN : 0;
+#else
+ dst->UserFlags = 0;
+#endif
}
// CoreCLR expects the "2" suffixes on these: they should be cleaned up in our
@@ -1460,6 +1466,28 @@ int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length,
return ret;
}
+int32_t SystemNative_LChflags(const char* path, uint32_t flags)
+{
+#if HAVE_LCHFLAGS
+ int32_t result;
+ while ((result = lchflags(path, flags)) < 0 && errno == EINTR);
+ return result;
+#else
+ (void)path, (void)flags;
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
+int32_t SystemNative_LChflagsCanSetHiddenFlag(void)
+{
+#if defined(UF_HIDDEN) && defined(HAVE_STAT_FLAGS) && defined(HAVE_LCHFLAGS)
+ return true;
+#else
+ return false;
+#endif
+}
+
int32_t SystemNative_Symlink(const char* target, const char* linkPath)
{
return symlink(target, linkPath);
diff --git a/src/Native/Unix/System.Native/pal_io.h b/src/Native/Unix/System.Native/pal_io.h
index aab12e6ed3..3a181971c6 100644
--- a/src/Native/Unix/System.Native/pal_io.h
+++ b/src/Native/Unix/System.Native/pal_io.h
@@ -154,6 +154,14 @@ enum
};
/**
+ * Constants for interpreting FileStatus.UserFlags.
+ */
+enum
+{
+ PAL_UF_HIDDEN = 0x8000
+};
+
+/**
* Constants from dirent.h for the inode type returned from readdir variants
*/
enum NodeType
@@ -751,6 +759,20 @@ DLLEXPORT int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid);
DLLEXPORT int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType);
/**
+* Changes the file flags of the file whose location is specified in path
+*
+* Returns 0 for success, -1 for failure. Sets errno for failure.
+*/
+DLLEXPORT int32_t SystemNative_LChflags(const char* path, uint32_t flags);
+
+/**
+ * Determines if the current platform supports setting UF_HIDDEN (0x8000) flag
+ *
+ * Returns true (non-zero) if supported, false (zero) if not.
+ */
+DLLEXPORT int32_t SystemNative_LChflagsCanSetHiddenFlag(void);
+
+/**
* Creates a symbolic link at "linkPath", pointing at "target".
* "target" may or may not exist (dangling symbolic links are valid filesystem objects)
* Returns 0 on success; otherwise, returns -1 and errno is set.
diff --git a/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj b/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj
index df9ff7154e..b7eb4c460c 100644
--- a/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj
+++ b/src/System.IO.FileSystem/src/System.IO.FileSystem.csproj
@@ -284,6 +284,9 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Read.cs">
<Link>Common\Interop\Unix\Interop.Read.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.LChflags.cs">
+ <Link>Common\Interop\Unix\Interop.LChflags.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Rename.cs">
<Link>Common\Interop\Unix\Interop.Rename.cs</Link>
</Compile>
diff --git a/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs b/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs
index 3acb4c5747..670317d4db 100644
--- a/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs
+++ b/src/System.IO.FileSystem/src/System/IO/FileStatus.Unix.cs
@@ -84,8 +84,8 @@ namespace System.IO
if (_isDirectory)
attributes |= FileAttributes.Directory;
- // If the filename starts with a period, it's hidden.
- if (fileName.Length > 0 && fileName[0] == '.')
+ // If the filename starts with a period or has UF_HIDDEN flag set, it's hidden.
+ if (fileName.Length > 0 && (fileName[0] == '.' || (_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == (uint)Interop.Sys.UserFlags.UF_HIDDEN))
attributes |= FileAttributes.Hidden;
return attributes != default ? attributes : FileAttributes.Normal;
@@ -98,10 +98,10 @@ namespace System.IO
const FileAttributes allValidFlags =
FileAttributes.Archive | FileAttributes.Compressed | FileAttributes.Device |
FileAttributes.Directory | FileAttributes.Encrypted | FileAttributes.Hidden |
- FileAttributes.Hidden | FileAttributes.IntegrityStream | FileAttributes.Normal |
- FileAttributes.NoScrubData | FileAttributes.NotContentIndexed | FileAttributes.Offline |
- FileAttributes.ReadOnly | FileAttributes.ReparsePoint | FileAttributes.SparseFile |
- FileAttributes.System | FileAttributes.Temporary;
+ FileAttributes.IntegrityStream | FileAttributes.Normal | FileAttributes.NoScrubData |
+ FileAttributes.NotContentIndexed | FileAttributes.Offline | FileAttributes.ReadOnly |
+ FileAttributes.ReparsePoint | FileAttributes.SparseFile | FileAttributes.System |
+ FileAttributes.Temporary;
if ((attributes & ~allValidFlags) != 0)
{
// Using constant string for argument to match historical throw
@@ -113,6 +113,26 @@ namespace System.IO
if (!_exists)
FileSystemInfo.ThrowNotFound(path);
+ if (Interop.Sys.CanSetHiddenFlag)
+ {
+ if ((attributes & FileAttributes.Hidden) != 0)
+ {
+ if ((_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == 0)
+ {
+ // If Hidden flag is set and cached file status does not have the flag set then set it
+ Interop.CheckIo(Interop.Sys.LChflags(path, (_fileStatus.UserFlags | (uint)Interop.Sys.UserFlags.UF_HIDDEN)), path, InitiallyDirectory);
+ }
+ }
+ else
+ {
+ if ((_fileStatus.UserFlags & (uint)Interop.Sys.UserFlags.UF_HIDDEN) == (uint)Interop.Sys.UserFlags.UF_HIDDEN)
+ {
+ // If Hidden flag is not set and cached file status does have the flag set then remove it
+ Interop.CheckIo(Interop.Sys.LChflags(path, (_fileStatus.UserFlags & ~(uint)Interop.Sys.UserFlags.UF_HIDDEN)), path, InitiallyDirectory);
+ }
+ }
+ }
+
// The only thing we can reasonably change is whether the file object is readonly by changing permissions.
int newMode = _fileStatus.Mode;
diff --git a/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs b/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs
index 408666e819..434af1cb5f 100644
--- a/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/Base/FileGetSetAttributes.cs
@@ -16,9 +16,16 @@ namespace System.IO.Tests
public void SettingAttributes_Unix(FileAttributes attributes)
{
string path = CreateItem();
- SetAttributes(path, attributes);
- Assert.Equal(attributes, GetAttributes(path));
- SetAttributes(path, 0);
+ AssertSettingAttributes(path, attributes);
+ }
+
+ [Theory]
+ [InlineData(FileAttributes.Hidden)]
+ [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.FreeBSD)]
+ public void SettingAttributes_OSXAndFreeBSD(FileAttributes attributes)
+ {
+ string path = CreateItem();
+ AssertSettingAttributes(path, attributes);
}
[Theory]
@@ -33,6 +40,11 @@ namespace System.IO.Tests
public void SettingAttributes_Windows(FileAttributes attributes)
{
string path = CreateItem();
+ AssertSettingAttributes(path, attributes);
+ }
+
+ private void AssertSettingAttributes(string path, FileAttributes attributes)
+ {
SetAttributes(path, attributes);
Assert.Equal(attributes, GetAttributes(path));
SetAttributes(path, 0);
@@ -48,8 +60,16 @@ namespace System.IO.Tests
public void SettingInvalidAttributes_Unix(FileAttributes attributes)
{
string path = CreateItem();
- SetAttributes(path, attributes);
- Assert.Equal(FileAttributes.Normal, GetAttributes(path));
+ AssertSettingInvalidAttributes(path, attributes);
+ }
+
+ [Theory]
+ [InlineData(FileAttributes.Hidden)]
+ [PlatformSpecific(TestPlatforms.AnyUnix & ~(TestPlatforms.OSX | TestPlatforms.FreeBSD))]
+ public void SettingInvalidAttributes_UnixExceptOSXAndFreeBSD(FileAttributes attributes)
+ {
+ string path = CreateItem();
+ AssertSettingInvalidAttributes(path, attributes);
}
[Theory]
@@ -62,6 +82,11 @@ namespace System.IO.Tests
public void SettingInvalidAttributes_Windows(FileAttributes attributes)
{
string path = CreateItem();
+ AssertSettingInvalidAttributes(path, attributes);
+ }
+
+ private void AssertSettingInvalidAttributes(string path, FileAttributes attributes)
+ {
SetAttributes(path, attributes);
Assert.Equal(FileAttributes.Normal, GetAttributes(path));
}
diff --git a/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs b/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs
index 89a92f4fb2..1bdfb98db2 100644
--- a/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs
+++ b/src/System.IO.FileSystem/tests/FileInfo/GetSetAttributes.cs
@@ -28,5 +28,18 @@ namespace System.IO.Tests
test.Refresh();
Assert.Equal(false, test.IsReadOnly);
}
+
+ [Theory]
+ [InlineData(".", true)]
+ [InlineData("", false)]
+ [PlatformSpecific(TestPlatforms.OSX)]
+ public void HiddenAttributeSetCorrectly_OSX(string filePrefix, bool hidden)
+ {
+ string testFilePath = Path.Combine(TestDirectory, $"{filePrefix}{GetTestFileName()}");
+ FileInfo fileInfo = new FileInfo(testFilePath);
+ fileInfo.Create().Dispose();
+
+ Assert.Equal(hidden, (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden);
+ }
}
}