1

I like to use System.IO.File.WriteAllBytes() to keep things simple. But it seems, that this method can not be used everywhere. To write on my local system it works fine.

But when I use System.IO.File.WriteAllBytes() to write on a Windows share it produces an empty file and fails with an Exception:

System.UnauthorizedAccessException: Access to the path '/var/windowsshare/file.bin' is denied.
---> System.IO.IOException: Permission denied

If I look at the source at https://github.com/dotnet/runtime/blob/c72b54243ade2e1118ab24476220a2eba6057466/src/libraries/System.IO.FileSystem/src/System/IO/File.cs#L421 I found the following code working under the hood:

using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
    fs.Write(bytes, 0, bytes.Length);
}

If I change the code and use FileShare.None instead of FileShare.Read it works. So I have a workaround and I have to keep in mind that System.IO.File.WriteAllBytes() is not waterproof (is it correct?).

Unfortunately, my analysis ended up with a few related questions:

So what is the best practice if the target path is configurable? Does the developer have to avoid System.IO.File.WriteAllBytes() or does the system administrator have to find another way to mount the share? What is wrong with FileShare.Read? Does the Windows share change permissions/locking while System.IO.File.WriteAllBytes() is writing? Are there some tips to mount the Windows share?

Update 1

WriteAllBytes():

// WriteAllBytes() Throws System.UnauthorizedAccessException
System.IO.File.WriteAllBytes("/var/windowsshare/file.bin", bytes);

Create and move with C#

// Create local and move + optional overwrite works!
var tmp = Path.GetTempFileName(); // local file
System.IO.File.WriteAllBytes(tmp, bytes); // write local
System.IO.File.Move(tmp, "/var/windowsshare/file.bin", true); // optional overwrite

ls:

# ls -l /var/windowsshare/file.bin
-rw-rw-rw-. 1 apache apache 20 Feb  9 11:43 /var/windowsshare/file.bin
# ls -Z /var/windowsshare/file.bin
system_u:object_r:cifs_t:s0 /var/windowsshare/file.bin

mount ...

# mount -l
//1.2.3.4/windowsshare on /var/windowsshare type cifs (rw,relatime,vers=3.1.1,cache=strict,username=luke,domain=dom,uid=48,forceuid,gid=48,forcegid,addr=1.2.3.4,file_mode=0666,dir_mode=0777,soft,nounix,nodfs,nouser_xattr,mapposix,noperm,rsize=4194304,wsize=4194304,bsize=1048576,echo_interval=60,actimeo=1,_netdev)

# stat -f -c %T /var/windowsshare/file.bin
smb2
koalabruder
  • 2,794
  • 9
  • 33
  • 40
  • I'm guessing this is an NTFS volume mounted under linux? Is there anything funky with the way it's mounted: permissions, etc? – canton7 Feb 09 '21 at 11:11
  • First get writing working by using a file explorer and try copying and editing files using the explorer. This is not a c# issue, but a permission issue for the account being used. – jdweng Feb 09 '21 at 12:42
  • @jdweng Write in temp file and move it works. var tmp = Path.GetTempFileName(); System.IO.File.WriteAllBytes(tmp, bytes); System.IO.File.Move(tmp, target, true); – koalabruder Feb 09 '21 at 13:17
  • Are you an Admin? Are you running from inside VS? When you normally start VS you do not have Admin privileges. To run from VS with Admin, you need to right click shortcut and select Run As Admin. I suspect it is working from the explorer because you have Admin rights and not working from VS because you do not have Admin rights. – jdweng Feb 09 '21 at 13:24
  • @jdweng The program is an asp.net core service running on Red Hat linux as user apache. The service runs in the network of a customer. So my VS has no connection to the "Windows share". I do not have tested it with explorer but what I can say is that System.IO.File.Move() works while System.IO.File.WriteAllBytes() fails both with the user apache. – koalabruder Feb 09 '21 at 17:21
  • WriteAllBytes creates a new file while move uses an existing file. So you need to compare the file properties on Linux using ls - l to see differences. – jdweng Feb 09 '21 at 17:26
  • (at)jdweng, @conton7 I have updated the question inspired by your comments. – koalabruder Feb 09 '21 at 19:24

1 Answers1

1

The following thread (https://github.com/dotnet/runtime/issues/42790) on Github helped me out. In the end I remounted my CIFS shares with the nobrl option.

In the thread they also came to the conclusion that using FileShare.None works, but the root cause seems to be that the CIFS server we are using does not support byte range locks.

I am not sure what all the implications of this is, but in my case there is no need to write the file more than once and there should be no two processes trying to write to the same file.

dvanrensburg
  • 1,351
  • 1
  • 14
  • 21