views:

338

answers:

3

I'm making a software that tries to restore a database to sql server, but for that, i need Full Control over the folder that will host .mdf and .ldf files, I'm using the System.Security.AccessControl classes to give Full control for everyone but its not working! I just don't know why its happening... The app aplies the rules ok, but when it reaches the restore database part, it throws an exception telling me "The Operating system Returned a error (error 5, Access Denied)". My code is as follows:

public static void GiveDirFullPermissionEveryoneDotNet(String dir)
{
    GiveDirFullPermissionDotNet(dir, new String[] { @"TODOS", @"EVERYONE", @"BUILTIN/Users", @"Users", @"NT AUTHORITY\NETWORK SERVICE", @"NETWORK", @"Administrators", @"Administrator", @"Administradores", @"Administrador", @"SYSTEM" });
}

public static void GiveDirFullPermissionDotNet(String dir, String[] users)
{
    DirectorySecurity dirSec = Directory.GetAccessControl(dir);
    FileSystemAccessRule fsar;

    foreach (String userAtual in users)
    {
        try
        {
            fsar = new FileSystemAccessRule(userAtual
                                          , FileSystemRights.FullControl
                                          , InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit
                                          , PropagationFlags.InheritOnly
                                          , AccessControlType.Allow);
            dirSec.AddAccessRule(fsar);
        }
        catch (Exception)
        {
            continue;
        }
    }

    Directory.SetAccessControl(dir, dirSec);
}

I tried it the shell way, using "CACLS.EXE", but some windows versions its "ICACLS.EXE" (thanks great brains at Microsoft taking care of portability for us developers!). So I really want to do it the .NET way, please help.

EDIT:

I'll post here my RestoreDatabase method, the exception is thrown at "sqlRestore.SqlRestore(sqlServer);" line

public void RestoreDatabase(string databaseName,
                            string filePath,
                            string serverName,
                            string userName,
                            string password,
                            string dataFilePath,
                            string logFilePath)
{
    //! Classe de restauração do SQL server
    Restore sqlRestore = new Restore();

    //! adicionando o arquivo indicado ao Restore
    BackupDeviceItem deviceItem = new BackupDeviceItem(filePath, DeviceType.File);
    sqlRestore.Devices.Add(deviceItem);
    sqlRestore.Database = databaseName;

    ServerConnection connection;

    //! Se passou string vazia no usuário, tenta Windows Authentication
    if (userName == "")
    {
        SqlConnection sqlCon = new SqlConnection(@"Data Source=" + serverName + @"; Integrated Security=True;");
        connection = new ServerConnection(sqlCon);
    }
    //! Se passou login de usuário, tenta Server Autentication
    else
        connection = new ServerConnection(serverName, userName, password);


    Server sqlServer = new Server(connection);

    Database db = sqlServer.Databases[databaseName];
    sqlRestore.Action = RestoreActionType.Database;
    string dataFileLocation = dataFilePath + databaseName + ".mdf";
    string logFileLocation = logFilePath + databaseName + "_Log.ldf";
    db = sqlServer.Databases[databaseName];
    RelocateFile rf = new RelocateFile(databaseName, dataFileLocation);

    sqlRestore.RelocateFiles.Add(new RelocateFile(databaseName, dataFileLocation));
    sqlRestore.RelocateFiles.Add(new RelocateFile(databaseName + "_log", logFileLocation));
    sqlRestore.ReplaceDatabase = true;
    sqlRestore.Complete += new ServerMessageEventHandler(sqlRestoreComplete);
    sqlRestore.PercentCompleteNotification = 10;
    sqlRestore.PercentComplete += new PercentCompleteEventHandler(sqlRestorePercentComplete);

    sqlRestore.SqlRestore(sqlServer);

    db = sqlServer.Databases[databaseName];

    db.SetOnline();

    sqlServer.Refresh();
}
A: 

My money is on this not being a .Net issue but a problem with the SQL Server...

Here's my guess (from here)

From http://www.fmsinc.com/freE/NewTips/SQL/SQLtip9.asp

While only local devices are shown in Enterprise Manager’s Backup/Restore dialogs, there is a way to create or restore a SQL Server database backup on a network file share. Creating or restoring a database backup on a network file share requires the following prerequisites:

1) The SQL Server services, on the Server containing the instance of SQL Server, must be running under a domain-level account (e.g. A Domain Administrator account). This is accomplished by changing the "Log On" properties for the services named "MSSQLSERVER" and "SQLSERVERAGENT" on the server running SQL Server (not your local instance). When you have completed changing the log on information for these 2 services, you will need to restart the "MSSQLSERVER" service on that server. Note that this will ask if you want to restart the "SQLSERVERAGENT" as well - Answer: Yes.

2) The SQL Server service account must have FULL CONTROL rights to the file system folder and to the share. That means you need to have a shared location in which the logon account you specified in 1 (above) has full control rights.

3) The file share should only be accessed via UNC name. Mapped drives may not be consistently visible to the SQL Service.

4) You cannot specify the path by using the browse ellipses (...). You must type the fully qualified path

Austin Salonen
sqlservr.exe is running under NETWORK SERVICE user and sqlwriter.exe is running under SYSTEM user, as you can see in my code, both are getting full control permissions
Tufo
1) answered above2) answered above3) its not a mapped drive, its simply c:\program files\myApp folder4) I'm giving the full/absolute path to the folder, "C:\program files\myapp", not %programfiles% neiter any relational path...
Tufo
A: 

I've tested your code "RestoreDatabase" without any modification of permissions on my computer and it works for me. Visual Studio 2008 SP1, SQL 2008 Express SP1, Windows 7 x64.

I hope this helps you.

ikhaldeev
mine is VS2008 SP1, SQL 2005 Express, Win7 x64...but the same problem happens when i test it on other virtual machines...
Tufo
maybe it depends on database structure? try to create, backup and restore some test db.
ikhaldeev
A: 

I just found the answer by myself...

in my method "RestoreDatabase", when I set "RelocateFile" instances inside the "Restore" class, I'm saying to sql server move the .mdf and .ldf files to a new folder pointed by me, until now it sounds fine, as I give full permissions to everyone in the folder that will receive the files.

BUT thats where the problem begins: SQL Server create both .mdf and .ldf files inside its default data folder usign its own user (this one having full control over the SQL's default data folder) BUT (again) when he moves the files after the restore is complete, he uses another user, and this user must have permissions over the SQL's default data folder. What happen if this user hasn't the needed permissions? The answer is: "Exactly whats happening to me: 'The Operating system Returned a error (error 5, Access Denied)'".

To solve this problem I login to SQL server, query for the SQL default data folder location and set full control to it. After that, everything goes fine =]

Wish this help someone with the same problem! Thanks for everyone who tried to help!

Tufo