File.Exists returning false from a network share
Asked Answered
F

4

6

I've been working on a ASP.NET project that is going to save uploaded files to a network share. I figured I could just use a virtual directory and be fine, but I have been struggling with permissions for Directory.CreateDirectory.

I was able to upload files so I decided to change my code to place everything in a single directory, however this requires me to make use of File.Exists to avoid overwriting duplicates.

Now that I have all my code updated, I have discovered that no matter what I do, File.Exists always returns false (The file definitely exists) when I test against the network share.

Any ideas? I'm coming to the very end of my rope with network shares.

Ferrigno answered 30/12, 2008 at 19:51 Comment(1)
You might consider retagging this with 'ASP.NET'. I would do it, but my rep isn't high enough yet :(Eugenioeugenius
E
7

I just recently worked on a very similar project where I am saving files to a network share. The two computers are on the same subnet, but are not controlled by domain controller, so each computer has it's own users.

I created a user with the same username and password on both computers. Then I created a network share and set the folder/share permissions to allow read-write for the user.

I then created the following class to manage the impersonation:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.Text;

namespace MyProject.Business.Web
{
    public class SecurityManager
    {
        #region DLL Imports
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
        #endregion

        public string Domain { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }

        private WindowsImpersonationContext m_CurrentImpersonationContext;

        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public void StartImpersonation()
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;

            IntPtr tokenHandle = IntPtr.Zero;
            IntPtr dupeTokenHandle = IntPtr.Zero;

            // obtain a handle to an access token
            bool wasLogonSuccessful = LogonUser(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);

            if (!wasLogonSuccessful)
                throw new Exception(String.Format("Logon failed with error number {0}", Marshal.GetLastWin32Error()));

            // use the token handle to impersonate the user
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            m_CurrentImpersonationContext = newId.Impersonate();

            // free the tokens
            if (tokenHandle != IntPtr.Zero)
                CloseHandle(tokenHandle);
        }
        public void EndImpersonation()
        {
            m_CurrentImpersonationContext.Undo();
        }
    }
}

Then in the ASP.NET page I did the following:

SecurityManager sm = new SecurityManager();
sm.UserName = ConfigurationManager.AppSettings["UserFileShareUsername"];
sm.Password = ConfigurationManager.AppSettings["UserFileSharePassword"];
sm.StartImpersonation();

if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);

File.Move(sourcePath, destinationPath);

sm.EndImpersonation();
Eugenioeugenius answered 30/12, 2008 at 20:3 Comment(1)
AJ , Can you take a look at my situation and suggest a solution ?#12684148Soapbox
T
8

File.Exist doesn't actually check for existance of a file. It instead checks for Existance of files that you have some measure of access to. If you know the file exists, the likely problem is that you don't have access to it.

Teahouse answered 30/12, 2008 at 19:58 Comment(1)
You started with a kind of half way true/false statement. It would be better have "It checks for Existance of files that you have some measure of access to".Crosscheck
E
7

I just recently worked on a very similar project where I am saving files to a network share. The two computers are on the same subnet, but are not controlled by domain controller, so each computer has it's own users.

I created a user with the same username and password on both computers. Then I created a network share and set the folder/share permissions to allow read-write for the user.

I then created the following class to manage the impersonation:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;
using System.Text;

namespace MyProject.Business.Web
{
    public class SecurityManager
    {
        #region DLL Imports
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
        #endregion

        public string Domain { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }

        private WindowsImpersonationContext m_CurrentImpersonationContext;

        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public void StartImpersonation()
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;

            IntPtr tokenHandle = IntPtr.Zero;
            IntPtr dupeTokenHandle = IntPtr.Zero;

            // obtain a handle to an access token
            bool wasLogonSuccessful = LogonUser(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref tokenHandle);

            if (!wasLogonSuccessful)
                throw new Exception(String.Format("Logon failed with error number {0}", Marshal.GetLastWin32Error()));

            // use the token handle to impersonate the user
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            m_CurrentImpersonationContext = newId.Impersonate();

            // free the tokens
            if (tokenHandle != IntPtr.Zero)
                CloseHandle(tokenHandle);
        }
        public void EndImpersonation()
        {
            m_CurrentImpersonationContext.Undo();
        }
    }
}

Then in the ASP.NET page I did the following:

SecurityManager sm = new SecurityManager();
sm.UserName = ConfigurationManager.AppSettings["UserFileShareUsername"];
sm.Password = ConfigurationManager.AppSettings["UserFileSharePassword"];
sm.StartImpersonation();

if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);

File.Move(sourcePath, destinationPath);

sm.EndImpersonation();
Eugenioeugenius answered 30/12, 2008 at 20:3 Comment(1)
AJ , Can you take a look at my situation and suggest a solution ?#12684148Soapbox
P
2

Perhaps your code which is running (i.e. ASP.NET server code) is running as a user (e.g. the IIS user) which doesn't have permission to access that network share.

I think that IIS isn't supposed to be run as a highly-priviledged user, which by default has permission to view shares on other machines.

Pedantry answered 30/12, 2008 at 19:54 Comment(1)
I've got down the permissions path a million times. I was hoping this could be a hacky workaround because I can't solve the permissions issue, but alas I'm back to square 1.Ferrigno
O
2

I used much the same code, but had my class implement the IDisposable interface, and added the Undo() to the Dispose() method. This code works fine, if you're the only developer using it and you'll always do things the right way, right?

Obala answered 28/6, 2010 at 15:1 Comment(2)
What is the purpose of this answer? I do not see it adds any value to problem.Crosscheck
Right, because what's the value in the IDisposable interface anyway. Stupid Jon Skeet letting us clean up after ourselves by giving instructions to the garbage collector in a consistent manner.Obala

© 2022 - 2024 — McMap. All rights reserved.