views:

9578

answers:

8

Basically, I would like to check if I have rights to open the file before I actually try to open it; I do not want to use a try/catch for this check unless I have to. Is there a file access property I can check before hand?

+38  A: 
Joel Coehoorn
Exactly. This is a classic example of a race condition.
R. Bemrose
It would be great if there were a "File System Mutex". But without the existence of that, you are dead right.
Jason Jackson
Or use linux/unix, ofcourse.
chrissie1
Isnt the question about file opening rights/permissions?So first you check if you have permission on this file, and then only if you have permission, you try if it's available?
korro
korro: you have to be able to handle bad permissions on failure anyway, and that makes the initial check redundant and wasteful.
Joel Coehoorn
@chrissie: linux has the same issue. It's a file system thing, and ext2/3/reiser/etc are not immune.
Joel Coehoorn
+1, this is the only correct way to do this.
Paul Betts
Looks like a good explanation.Althoug i will check once before affirming
Ravisha
An initial check can help to handle common specific errors gracefully - looking ahead is often easier than matching particular exception attributes to specific causes. The try/catch still remains obligatory.
peterchen
+2  A: 

Eric Lippert probably explains why Joel is right here

PolyglotProgrammer
Hehe, I read that a while ago and was thinking about for this exact post as well, because this is exactly a vexing exception. But we're still waiting for a built-in File.TryOpen() method, and even then you might still need to be careful if you allow sufficient sharing access.
Joel Coehoorn
+10  A: 

While I agree that this does create a race condition, I believe FileIOPermission has the answer if you really wanted to do it:

new FileIOPermission(FileIOPermissionAccess.Read, path).Demand();

Edit: As pointed out, this is for the FX permissions - not the underlying file system permissions - so it only answers half (the easy half) of your question.

If you're interested in getting NTFS permissions, you'll have to P/Invoke AccessCheck, AuthzAccessCheck, or GetEffectiveRightsFromACL. You'd also need to retrieve the actual ACL from the file, and be wary of enumerating permissions on remote servers.

Mark Brackett
Sorry. That doesn't verify if you have physically access to the file but only if your privilege under the framework includes "reading files" which doesn't meet the requirement of the question.
Maxim
Why not? The question is "I would like to check if I have rights to open the file". You might want to check if you have rights first, and only then try if you can access it.
korro
+3  A: 

First, what Joel Coehoorn said.

Also: you should examine the assumptions that underly your desire to avoid using try/catch unless you have to. The typical reason for avoiding logic that depends on exceptions (creating Exception objects performs poorly) probably isn't relevant to code that's opening a file.

I suppose that if you're writing a method that populates a List<FileStream> by opening every file in a directory subtree and you expected large numbers of them to be inaccessible you might want to check file permissions before trying to open a file so that you didn't get too many exceptions. But you'd still handle the exception. Also, there's probably something terribly wrong with your program's design if you're writing a method that does this.

Robert Rossney
+1  A: 

I'll go with PolyglotProgrammer.

Thanks for all the answers.

Horas
+3  A: 

Quick tip for anyone else coming here with a similar problem:

Watch out for web synchronization apps such as DropBox. I just spent 2 hours thinking the "using" statement (Dispose pattern) is broken in .NET.

I eventually realised that Dropbox is continually reading and writing files in the background, in order to sync them.

Guess where my Visual Studio Projects folder is located? Inside the "My Dropbox" folder of course.

Therefore as I ran my application in Debug mode, the files it was reading and writing were also continually being accessed by DropBox to be synched with the DropBox server. This caused the locking/access conflicts.

So at least I now know that I need to a more robust File Open function (ie TryOpen() that will make multiple attempts). I am surprised it's not already a built-in part of the framework.

[Update]

Here's my helper function:

/// <summary>
/// Tries to open a file, with a user defined number of attempt and Sleep delay between attempts.
/// </summary>
/// <param name="filePath">The full file path to be opened</param>
/// <param name="fileMode">Required file mode enum value(see MSDN documentation)</param>
/// <param name="fileAccess">Required file access enum value(see MSDN documentation)</param>
/// <param name="fileShare">Required file share enum value(see MSDN documentation)</param>
/// <param name="maximumAttempts">The total number of attempts to make (multiply by attemptWaitMS for the maximum time the function with Try opening the file)</param>
/// <param name="attemptWaitMS">The delay in Milliseconds between each attempt.</param>
/// <returns>A valid FileStream object for the opened file, or null if the File could not be opened after the required attempts</returns>
public FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess,FileShare fileShare,int maximumAttempts,int attemptWaitMS)
{
    FileStream fs = null;
    int attempts = 0;

    // Loop allow multiple attempts
    while (true)
    {
        try
        {
            fs = File.Open(filePath, fileMode, fileAccess, fileShare);

            //If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
            break;
        }
        catch (IOException ioEx)
        {
            // IOExcception is thrown if the file is in use by another process.

            // Check the numbere of attempts to ensure no infinite loop
            attempts++;
            if (attempts > maximumAttempts)
            {
                // Too many attempts,cannot Open File, break and return null 
                fs = null;
                break;
            }
            else
            {
                // Sleep before making another attempt
                Thread.Sleep(attemptWaitMS);

            }

        }

    }
    // Reutn the filestream, may be valid or null
    return fs;
}
Ash
@Ash I think u didn read question properlyHE wants to avoid try catch.
Ravisha
@Ravisha, did you even read Joel's top voted answer? As Joel says, **"What you do instead is just try to open the file and handle the exception if it fails"**. Please don't downvote just because you don't like the fact that something cannot be avoided.
Ash
A: 
public static FileStream GetFileStream(String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, ref int attempts, int attemptWaitInMilliseconds)
{            
    try
    {
         return File.Open(filePath, fileMode, fileAccess, fileShare);
    }
    catch (UnauthorizedAccessException unauthorizedAccessException)
    {
        if (attempts <= 0)
        {
            throw unauthorizedAccessException;
        }
        else
        {
            Thread.Sleep(attemptWaitInMilliseconds);
            attempts--;
            return GetFileStream(filePath, fileMode, fileAccess, fileShare, ref attempts, attemptWaitInMilliseconds);
        }
    }
}
Rudzitis
-1: use "throw;" not "throw unauthorizedAccessException;". You're losing your stack trace.
John Saunders
Why is `attempts` passed by ref? That makes no sense. Neither does testing for `<=` instead of just `==`.
Konrad Rudolph
@John: well, in this case it’s *desirable* to lose the (deeply nested) stack trace of the recursive call so I think in this instance `throw ex` is actually *the right thing* to do.
Konrad Rudolph
@Konrad: @Rudzitis: I'm changing my reason for the -1. It's worse than screwing the stack by "throw ex". You're screwing the stack by artificially inducing extra stack levels through recursion at a time when stack depth actually matters. This is an iterative problem, not a recursive one.
John Saunders