Ridicilous!

  • Thread starter Thread starter Ayende Rahien
  • Start date Start date
A

Ayende Rahien

<rant warning="I'm pissed off and have to vent" request="need help>
For the last couple of hours I'm struggling with a very *annoying* problem.
How to check if a user has a write access to a file?
Considerring that .Net is supposed to be a system for writing applications
for servers & clients, which in both cases has may have multi-users, I'm
amazed that this is not possible in the framework.
Initially I looked for something like:

System.IO.File.Access(string Filename, PermissionAccess);

Nothing!

Then I thought about System.Security.Permissions, and there is was, clear as
day FileIOPermission a class which "Controls the ability to access files
and folders."
But no! FileIOPermission works for .Net permissions, it doesn't do Win32
permissions!

Considerring that the framework is supposed to run on server class
platforms, why the omission?

In C & C++ I can do _access() and get the info I want, in .Net? I get to
delve into:

A> Interop code.
B> Win32 Security.

I can handle Interop if I absolutely have to, but even on a good day, using
C++ I don't like Win32 Security, and all the tricks that are used there.
I checked ACLs in .Net
http://www.gotdotnet.com/community/...mpleguid=e6098575-dda0-48b8-9abf-e0705af065d9
No go, it requires me to add a component that is bigger then my code just to
do so, and add several level of complexities to my life.
Not to mention, again, having to deal with all the minor details of Win32
security.

To remind you, I'm trying to find out if my code has write access to a file,
on a framework that support multiply users, networks and such, this should
be a snap.
Instead...

Opening a file and catching the exception is the only easy way that I've,
and that is *not acceptable*.
</rant>

Seriously now, I really need a way to solve this problem, preferably with
something like:
FileAccess.Verify(filename,WriteAccess);

Thanks in advance,
Ayende Rahien
 
Yeah, I've seen that (see my original post), my problem is that my code
is a utility class, which can't have another library attached to it.
Beyond that, I could not fathom an easy way to see if I've an access to
a file.
 
<rant warning="I'm pissed off and have to vent" request="need help>
For the last couple of hours I'm struggling with a very *annoying*
problem. How to check if a user has a write access to a file?
Considerring that .Net is supposed to be a system for writing
applications for servers & clients, which in both cases has may have
multi-users, I'm amazed that this is not possible in the framework.
Initially I looked for something like:

System.IO.File.Access(string Filename, PermissionAccess);

Nothing!


I happened to run across some code in this very same newsgroup asking
basically the same question - I'll repost it for you assuming that you
didn't see it. This isn't my code and I haven't tested it, so use at your
own risk... I've included a few of the headers as well.

------------------

Subject: Re: Get ACL
From: "Willy Denoyette [MVP]" <[email protected]>
Newsgroups: microsoft.public.dotnet.languages.csharp

You shouldn't use "unsupported" stuff like Win32Security.dll, use the
System.DirectoryServices (XP and higher) or System.Management namespace
instead.
Next is a complete example illustrating how to dump the ACE's from a File
object DACL using System.Management classes.

using System;
using System.Management;
using System.Collections;
// Access mask (see AccessMask property)
[Flags]
enum Mask : uint
{
FileReadData = 0x00000001,
FileWriteData = 0x00000002,
FileAppendData = 0x00000004,
FileReadEA = 0x00000008,
FileWriteEA = 0x00000010,
FileExecute = 0x00000020,
FileDeleteChild = 0x00000040,
FileReadAttributes = 0x00000080,
FileWriteAttributes= 0x00000100,

Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,

AccessSystemSecurity = 0x01000000,
MaximumAllowed = 0x02000000,

GenericAll = 0x10000000,
GenericExecute= 0x20000000,
GenericWrite = 0x40000000,
GenericRead = 0x80000000
}
[Flags]
enum AceFlags : int
{
ObjectInheritAce = 1,
ContainerInheritAce = 2,
NoPropagateInheritAce = 4,
InheritOnlyAce = 8,
InheritedAce = 16
}

[Flags]
enum AceType : int
{
AccessAllowed = 0,
AccessDenied = 1,
Audit = 2
}
class Tester {
public static void Main() {
string fileObject = @"c:\\pipo\\t.txt"; // Watch the double Backslashes
using(ManagementObject lfs = new
ManagementObject(@"Win32_LogicalFileSecuritySetting.Path=" + "'" +
fileObject + "'"))
{
// Get the security descriptor for this object
// Dump all trustees (this includes owner)
ManagementBaseObject outParams =
lfs.InvokeMethod("GetSecurityDescriptor", null, null);
if (((uint)(outParams.Properties["ReturnValue"].Value)) == 0) // if
success
{
ManagementBaseObject secDescriptor =
((ManagementBaseObject)(outParams.Properties["Descriptor"].Value));
//The DACL is an array of Win32_ACE objects.
ManagementBaseObject[] dacl =
((ManagementBaseObject[])(secDescriptor.Properties["Dacl"].Value));
DumpACEs(dacl);

}
}
}

static void DumpACEs(ManagementBaseObject[] dacl)
{
foreach(ManagementBaseObject mbo in dacl){
Console.WriteLine("\n---------\nMask: {0:X} - Flags: {1} - Type: {2}",
mbo["AccessMask"], mbo["AceFlags"], mbo["AceType"]);
// Access allowed/denied ACE
if(Convert.ToInt32(mbo["AceType"]) == (int)AceType.AccessDenied)
Console.WriteLine("DENIED ACE TYPE");
else
Console.WriteLine("ALLOWED ACE TYPE");
// Dump trustees
ManagementBaseObject Trustee = ((ManagementBaseObject)(mbo["Trustee"]));
Console.WriteLine("Name: {0} - Domain: {1} - SID {2}\n",
Trustee.Properties["Name"].Value,
Trustee.Properties["Domain"].Value,
Trustee.Properties["SIDString"].Value);
// Dump ACE mask in readable form
UInt32 mask = (UInt32)mbo["AccessMask"];
Console.WriteLine(Enum.Format(typeof(Mask), mask, "g"));
}
}
}
 
Heyas

FileIOPermission is a Code Access Security permission, not a Windows
role-based security permission. What you want is the ability to examine the
security descriptor on the file and examine the rights of the various
Windows trustees in the entries in the DACL.

There are a couple of ways to do this. You could use COM interop and the
IADsSecurityUtility interface in ADSI, or you could use WMI, or use
the Win32 security .NET wrapper on GotDotNet that talks directly to the
Windows APIs.

Hint: The wrapper is the one you want.

-Enjoy!!

-Evan
 
Wow I posted late I guess, if your doing a library then you want to use the
WMI method. I believe one of the other posters posted a previous example of
doing this from a former thread.

-Enjoy!!

-Evan
 
Michael said:
I happened to run across some code in this very same newsgroup asking
basically the same question - I'll repost it for you assuming that you
didn't see it. This isn't my code and I haven't tested it, so use at your
own risk... I've included a few of the headers as well.

Thanks a lot.
Could you show me how to use this to check if I've write access to the file?
------------------

Subject: Re: Get ACL
From: "Willy Denoyette [MVP]" <[email protected]>
Newsgroups: microsoft.public.dotnet.languages.csharp

You shouldn't use "unsupported" stuff like Win32Security.dll, use the
System.DirectoryServices (XP and higher) or System.Management namespace
instead.
Next is a complete example illustrating how to dump the ACE's from a File
object DACL using System.Management classes.

using System;
using System.Management;
using System.Collections;
// Access mask (see AccessMask property)
[Flags]
enum Mask : uint
{
FileReadData = 0x00000001,
FileWriteData = 0x00000002,
FileAppendData = 0x00000004,
FileReadEA = 0x00000008,
FileWriteEA = 0x00000010,
FileExecute = 0x00000020,
FileDeleteChild = 0x00000040,
FileReadAttributes = 0x00000080,
FileWriteAttributes= 0x00000100,

Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,

AccessSystemSecurity = 0x01000000,
MaximumAllowed = 0x02000000,

GenericAll = 0x10000000,
GenericExecute= 0x20000000,
GenericWrite = 0x40000000,
GenericRead = 0x80000000
}
[Flags]
enum AceFlags : int
{
ObjectInheritAce = 1,
ContainerInheritAce = 2,
NoPropagateInheritAce = 4,
InheritOnlyAce = 8,
InheritedAce = 16
}

[Flags]
enum AceType : int
{
AccessAllowed = 0,
AccessDenied = 1,
Audit = 2
}
class Tester {
public static void Main() {
string fileObject = @"c:\\pipo\\t.txt"; // Watch the double Backslashes
using(ManagementObject lfs = new
ManagementObject(@"Win32_LogicalFileSecuritySetting.Path=" + "'" +
fileObject + "'"))
{
// Get the security descriptor for this object
// Dump all trustees (this includes owner)
ManagementBaseObject outParams =
lfs.InvokeMethod("GetSecurityDescriptor", null, null);
if (((uint)(outParams.Properties["ReturnValue"].Value)) == 0) // if
success
{
ManagementBaseObject secDescriptor =
((ManagementBaseObject)(outParams.Properties["Descriptor"].Value));
//The DACL is an array of Win32_ACE objects.
ManagementBaseObject[] dacl =
((ManagementBaseObject[])(secDescriptor.Properties["Dacl"].Value));
DumpACEs(dacl);

}
}
}

static void DumpACEs(ManagementBaseObject[] dacl)
{
foreach(ManagementBaseObject mbo in dacl){
Console.WriteLine("\n---------\nMask: {0:X} - Flags: {1} - Type: {2}",
mbo["AccessMask"], mbo["AceFlags"], mbo["AceType"]);
// Access allowed/denied ACE
if(Convert.ToInt32(mbo["AceType"]) == (int)AceType.AccessDenied)
Console.WriteLine("DENIED ACE TYPE");
else
Console.WriteLine("ALLOWED ACE TYPE");
// Dump trustees
ManagementBaseObject Trustee = ((ManagementBaseObject)(mbo["Trustee"]));
Console.WriteLine("Name: {0} - Domain: {1} - SID {2}\n",
Trustee.Properties["Name"].Value,
Trustee.Properties["Domain"].Value,
Trustee.Properties["SIDString"].Value);
// Dump ACE mask in readable form
UInt32 mask = (UInt32)mbo["AccessMask"];
Console.WriteLine(Enum.Format(typeof(Mask), mask, "g"));
}
}
}
 
Ayende Rahien said:
Thanks a lot.
Could you show me how to use this to check if I've write access to the
file?

As I said, its not my code and I haven't tested it. It just seemed
relevant to what you were looking for. I don't have any direct knowledge
of how it works, so I can't help you here. The post is a current post in
this newsgroup - perhaps you could reset your newsreader and search for
this and ask the original author.

-mdb
 
It uses WMI to get the permissions. So go take a look at the WMI iformation
on MSDN.

-Enjoy!!

-Evan
 
You hit the nail on the head when you talked about multi-user environments. In a multi-user environment the only time it is safe to assume that a user has write access to a file is at the point when the user successfully opens the file for writing. With your proposed File.Access type functions you can get race conditions between multiple threads, e.g,

User A - have I got write access - yes, so I can open the fil
User B - opens the same file for writing, which denies anyone else write acces
User A - now tries to open the file - but they no longer have write access, so this fails

That's why File.Open throws an UnauthorizedAccessException, so you can take appropriate action (e.g., pause for a bit, loop round, and then try again). The C _access function is not suitable for multi-user environments.
 
I suppose you can't just write to the file and see if it works? A write
access check won't tell you about disk quota problems, for instance.
 
This is nice, but won't do me any good, as teh FileInfo doesn't contain
permission checking, only attributes (Yes, I'll have access to the
read-only, but that won't help if I don't have write access to the file.)
 
John said:
I suppose you can't just write to the file and see if it works? A write
access check won't tell you about disk quota problems, for instance.

It's a very small utility, and I'm going to do a very intensive use of it.
Exceptions are *slow*, really(!!) slow.
 
What's wrong with simply attempting to open the file in Write mode and if it doesn't work (i.e. exception thrown) then you know that you don't have write access to it? Something like:

public class Form1 : System.Windows.Forms.Form
{
private System.ComponentModel.IContainer components;
private FileStream myFile = null;

public Form1()
{
try
{
myFile = File.OpenWrite( @"C:\test.txt" );
}
catch( Exception e )
{
MessageBox.Show( "Cannot open file. Error: " + e.Message );
}
...
}

gary said:
see attatched vs .net project file

the main thing happens here:

private void frmFileAttributes_Load(object sender, System.EventArgs e)
{
string[] lvData = new string[5]; // store file data
string[] stringFiles = Directory.GetFiles( @"C:\" );
string stringFileName = "";
string attribute = "";
Int64 lFileSize = 0;

//loop through files in C:\
foreach ( string stringFile in stringFiles )
{
stringFileName = stringFile;
FileInfo objFileInfo = new FileInfo( stringFileName );
lFileSize = objFileInfo.Length;

// file attributes are here :
attribute = objFileInfo.Attributes.ToString(); // right here !

//..... store the data
lvData[0] = GetPathName(stringFileName);
lvData[1] = lFileSize.ToString();
lvData[2] = attribute;

//Create actual list item
ListViewItem lvItem = new ListViewItem( lvData, 0 );
lv.Items.Add( lvItem );
}
}

my pleasure - hope you find it useful
8-)

[microsoft.public.dotnet.languages.csharp]
 
astanley said:
You hit the nail on the head when you talked about multi-user environments. In a multi-user environment the only time it is safe to assume that a user has write access to a file is at the point when the user successfully opens the file for writing. With your proposed File.Access type functions you can get race conditions between multiple threads, e.g,.

User A - have I got write access - yes, so I can open the file
User B - opens the same file for writing, which denies anyone else write access
User A - now tries to open the file - but they no longer have write access, so this fails.

For this I can use mutexes, if I've to.
I don't see this as a possible problem, because I already uses
multi-threading and syncronize between them.

That's why File.Open throws an UnauthorizedAccessException, so you can take appropriate action (e.g., pause for a bit, loop round, and then try again). The C _access function is not suitable for multi-user environments.

Yes, except that I've a tight loop there, and exceptions are very slow.
Beside, I need to give the user input, and that isn't possible with
exceptions.
Beside, I *really* dislike the try-catch as a replacement for a function
call that should exist.
 
Yes, exceptions are expensive - if thrown. The question is, do you
anticipate most of the requests to be denied? If only a small percentage of
them are going to be attempts to use a file the user has no access to - then
you should be fine, as you will only pay the penalty of throwing/catching
exceptions in this case, which isn't going to amount to that much.
 
Ayende Rahien said:
It's a very small utility, and I'm going to do a very intensive use of it.
Exceptions are *slow*, really(!!) slow.

The first exception thrown (of some types) *can* be slow, depending on
what resources need to be loaded. After that, they're pretty fast. I
seem to remember my box is able to throw about a hundred thousand
exceptions a second. Are you really expecting to fail to write to
*that* many files?

If you're worried about the first exception being slow, you could
always start a new thread at the start of program execution so that it
gets that over and done with in the background - then you won't face
the performance hit later on if you get an exception for some other
reason.
 
Ok, I think I misunderstood your purpose.


Did you check - FileIOPermission.GetPathList Method and the param
FileIOPermissionAccess Enumeration ?

BTW, the exception catch thing seems to me to be the easiest way to go with
your requirements



8-)
 
Back
Top