From 264af40e629d2996194fcb5805c8002168109761 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Mon, 14 Dec 2020 19:20:09 -0800 Subject: [PATCH] Enable File.Copy to EXFAT volumes when not running as root (#46027) * Allow File.Copy to succeed when futimens fails with EPERM * Manual test * Update pal_io.c --- src/libraries/Native/Unix/System.Native/pal_io.c | 9 ++++++--- .../tests/ManualTests/ManualTests.cs | 23 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_io.c b/src/libraries/Native/Unix/System.Native/pal_io.c index 2b780b0..3d7420e 100644 --- a/src/libraries/Native/Unix/System.Native/pal_io.c +++ b/src/libraries/Native/Unix/System.Native/pal_io.c @@ -1165,14 +1165,17 @@ int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) while ((ret = futimes(outFd, origTimes)) < 0 && errno == EINTR); #endif } - if (ret != 0) + // If we copied to a filesystem (eg EXFAT) that does not preserve POSIX ownership, all files appear + // to be owned by root. If we aren't running as root, then we won't be an owner of our new file, and + // attempting to copy metadata to it will fail with EPERM. We have copied successfully, we just can't + // copy metadata. The best thing we can do is skip copying the metadata. + if (ret != 0 && errno != EPERM) { return -1; } - // Then copy permissions. while ((ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) < 0 && errno == EINTR); - if (ret != 0) + if (ret != 0 && errno != EPERM) // See EPERM comment above { return -1; } diff --git a/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs b/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs index c363e65..fe0395a8 100644 --- a/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs +++ b/src/libraries/System.IO.FileSystem/tests/ManualTests/ManualTests.cs @@ -76,6 +76,29 @@ namespace System.IO.ManualTests }); } + [ConditionalFact(nameof(ManualTestsEnabled))] + [PlatformSpecific(TestPlatforms.Linux)] + public static void FileCopy_WorksToExFatVolume() + { + // We copy attributes after copying the file; when copying to EXFAT, + // where all files appeared to be owned by root, we can't copy attributes + // (unless we're root) and should skip silently + + /* This test requires an EXFAT partition. That can be created in memory like this: + + sudo mkdir /mnt/ramdisk + sudo mount -t ramfs ramfs /mnt/ramdisk + sudo dd if=/dev/zero of=/mnt/ramdisk/exfat.image bs=1M count=512 + sudo mkfs.exfat /mnt/ramdisk/exfat.image + sudo mkdir /mnt/exfatrd + sudo mount -o loop /mnt/ramdisk/exfat.image /mnt/exfatrd + + */ + + File.WriteAllText("/mnt/exfatrd/1", "content"); + File.Copy("/mnt/exfatrd/1", "/mnt/exfatrd/2"); + Assert.True(File.Exists("/mnt/exfatrd/2")); + } const long InitialFileSize = 1024; -- 2.7.4