close Warning: Error with navigation contributor "AccountModule"

Opened 11 years ago

Last modified 3 years ago

#13 new defect

Fix file locking on Windows

Reported by: chris Owned by: chris
Priority: normal Milestone: 0.12
Component: bbackupd Version: 0.10
Keywords: windows file locking locked volume shadow copy service vss Cc:

Description

There are two locking issues on Windows: Box cannot back up open files, and while Box is backing up a file, other applications cannot open it for writing.

Changing our access method to Volume Shadow Copy Service is probably the right fix for both of these, but it only works on XP and above, and I cannot install the SDK it on my development machine. I would also like to make it work with MinGW without requiring a restrictively-licensed SDK download from Microsoft.

Please could someone use a Windows machine (XP or above, Windows Activation compliant) to download the SDK and send it to me?

Another option (workaround) might be to use the Windows command-line tools to create a shadow copy and back that up instead of the original. However, I think that would be a kluge, difficult to install, and difficult to detect and handle errors with.

Attachments (1)

ShadowBox.bat (2.9 KB) - added by Pete Jalajas 10 years ago.
ShadowBox?.bat, an example of how to use Shadow Copy, vshadow.exe

Download all attachments as: .zip

Change History (10)

comment:1 Changed 11 years ago by chris

The SDK can be found here.

comment:2 Changed 11 years ago by Lee Sonko

comment:3 Changed 11 years ago by chris

Got it, thanks.

Changed 10 years ago by Pete Jalajas

Attachment: ShadowBox.bat added

ShadowBox?.bat, an example of how to use Shadow Copy, vshadow.exe

comment:4 Changed 10 years ago by chris

Milestone: 0.110.12

Not in 0.11, sorry.

comment:5 Changed 8 years ago by Achim J Latz

A good example of how simple VSS-support was added to Cywgin Rsync 3.0.x (check the patch files, they seem straightforward).

I have already checked via e-mail with both Leen Besselink (the author of the page) and Elias Penttilä (the original author) and they are absolutely fine with us using their source either verbatim or as inspiration.

http://www.consolejunky.net/cwrsync-vss/

comment:6 Changed 8 years ago by Achim J Latz

Elias Penttilä's patchset for rsync-2.6.6 http://users.tkk.fi/~epenttil/rsync-vss/

comment:7 Changed 8 years ago by Achim J Latz

First ideas for VSS implementation

First off, the GPL'ed Bacula backup project introduced an abstraction layer "VSSClient" that is used to create the actual VSS depending on the underlying Windows OS, as the APIs seem slightly different for XP, 2K3 and Vista/Windows? 7. All VSS code is contained in one vss_generic.cpp which uses #ifdef to include the headers and libs for each OS, such as:

#ifdef B_VSS_XP
// #pragma message("compile VSS for Windows XP")   
   #define VSSClientGeneric VSSClientXP
   
   #include "inc/WinXP/vss.h"
   #include "inc/WinXP/vswriter.h"
   #include "inc/WinXP/vsbackup.h"

#endif

#ifdef B_VSS_W2K3
// #pragma message("compile VSS for Windows 2003")
   #define VSSClientGeneric VSSClient2003
   
   #include "inc/Win2003/vss.h"
   #include "inc/Win2003/vswriter.h"
   #include "inc/Win2003/vsbackup.h"
#endif

This requires that all SDKs to be available in their respective subdirectory, i.e. the XP VSS SDK should be available in boxbackup/win32/vss/inc/WinXP/ and the one for Server 2K3 in boxbackup/win32/vss/inc/Win2003/ (see next step)

For the specific header and code files, please refer to: http://bacula.svn.sourceforge.net/viewvc/bacula/trunk/bacula/src/win32/filed/?pathrev=8346

Using Bacula's approach for VSS in Box Backup

  1. Install the required VSS SDKs (see "SDK Download links" below for where to get the various versions):
  1. Make sure that MingW can see the VSS files and folders (in this case for Server 2003):
    mkdir -p /usr/local/src/boxbackup/win32/vss/
    ln -s /cygdrive/c/Program\ Files/Microsoft/VSSSDK72/inc/win2003 /usr/local/src/boxbackup/win32/vss/inc/Win2003
    ln -s /cygdrive/c/Program\ Files/Microsoft/VSSSDK72/lib/win2003/obj/i386 /usr/local/src/boxbackup/win32/vss/lib/Win2003
    ln -s /cygdrive/c/Program\ Files/MSXML\ 4.0/inc /usr/local/src/boxbackup/win32/inc_xml
    
  1. add this to bin/bbackupd/Makefile to enable linking (in this case for Server 2003)
    -lole32 -luuid win32/vss/lib/Win2003/vssapi.lib win32/vss/lib/Win2003/vss_uuid.lib
    
  1. Add VSS code to BB, based on the following functions implemented by the Bacula "API"
    BOOL InitializeForBackup();
    virtual BOOL CreateSnapshots(char* szDriveLetters) = 0;
    virtual BOOL CloseBackup() = 0;
    virtual const char* GetDriverName() = 0;
    BOOL GetShadowPath  (const char* szFilePath, char* szShadowPath, int nBuflen);
    BOOL GetShadowPathW (const wchar_t* szFilePath, wchar_t* szShadowPath, int nBuflen); /* nBuflen in characters */
    BOOL CloseBackup()
    

On a high level, we need to

  1. Create the shadow copy and receive a (global) handle to a VSSClient object
  2. Substitute all paths that are used for backup with the corresponding VSS path as generated by our VSSClient object
  3. Deinit the shadow copy once the backup is done.

Note: There is also the AlphaVSS, "a .NET class library written in C++/CLI aiming to provide a managed interface to this API. The goal is to provide an interface that is simple to use from a C# or VB.NET application, yet providing the full functionality of VSS." http://alphavss.codeplex.com/ Not sure if we can use that somehow in stock C++?

Anyway, I detected the following places in the code where Box Backup interacts with the local file system paths:

lib/common/BoxPlatform.h

// Add VSS for Win32 clients
#ifdef WIN32
#ifndef USE_VSS
#define USE_VSS
#endif
#endif

bin/bbackupd/BackupDaemon.cpp

RunSyncNow()
#ifdef USE_VSS
	InitializeForBackup();
#endif
               // Sync the directory
               (*i)->mpDirectoryRecord->SyncDirectory(
                       params,
                       BackupProtocolClientListDirectory::RootDirectory,
                       (*i)->mPath, std::string("/") + (*i)->mName);

Local path is stored in (*i)->mPath

for(std::vector<Location *>::const_iterator
        i(mLocations.begin());
        i != mLocations.end(); ++i)
// add at the end of RunSyncNow()
#ifdef USE_VSS
	CloseBackup();
#endif

Now we need to make sure that the local path used by SyncDirectory? above is actually the VSS path:

bin/bbackupd/BackupClientDirectoryRecord.cpp

SyncDirectory
Purpose: Recursively synchronise a local directory with the server.

        BackupClientDirectoryRecord::SyncParams &rParams,
        int64_t ContainingDirectoryID,
        const std::string &rLocalPath,
        const std::string &rRemotePath,
        bool ThisDirHasJustBeenCreated)

Makes use of these other funcions:

UpdateItems
Purpose: Update the items stored on the server. The rFiles vector will be erased after it's used to save space.
Returns true if all items were updated successfully. (If not, the failures will have been logged).

UploadFile
Purpose: Update the items stored on the server. The rFiles vector will be erased after it's used to save space.
Returns true if all items were updated successfully. (If not, the failures will have been logged).
struct dirent *en = 0;
EMU_STRUCT_STAT file_st;
std::string filename;
while((en = ::readdir(dirHandle)) != 0)

uses MakeFullPath?

filename = MakeFullPath(rLocalPath, en->d_name);

lib/common/PathUtils.cpp

Name:    PathUtils.cpp
Purpose: Platform-independent path manipulation

Name:    MakeFullPath(const std::string& rDir, const std::string& rFile)
Purpose: Combine directory and file name

is where we need to add calls to and use the result to return the correct VSS-based FullPath?:

BOOL GetShadowPath  (const char* szFilePath, char* szShadowPath, int nBuflen);
BOOL GetShadowPathW (const wchar_t* szFilePath, wchar_t* szShadowPath, int nBuflen); /* nBuflen in characters */

SDK Download links

For Windows XP and 2003 users, you can download Microsoft’s Volume Shadow Copy Service SDK.

http://www.microsoft.com/downloads/details.aspx?FamilyID=0B4F56E4-0CCC-4626-826A-ED2C4C95C871&displaylang=en

http://www.microsoft.com/downloads/details.aspx?FamilyID=0b4f56e4-0ccc-4626-826a-ed2c4c95c871&displaylang=en&Hash=XbJaz6J077EdJjQS1kvwkBx2hOhvwBFCTsP8QUwEqEoyk5xpijF%2ftKvdCP7mZPNC2HnBGRcuuT5luN2TuMLTqA%3d%3d

Vista user’s need to download the Windows Vista SDK Update V6.0 (Microsoft Windows SDK Update for Windows Vista and .NET Framework 3.0)

http://msdn.microsoft.com/en-us/windows/bb980924.aspx

http://www.microsoft.com/downloads/details.aspx?FamilyID=4377F86D-C913-4B5C-B87E-EF72E5B4E065&displaylang=en

2008 Server

http://www.microsoft.com/downloads/details.aspx?FamilyID=F26B1AA4-741A-433A-9BE5-FA919850BDBF&displaylang=en

Windows 7 needs SDK 7.0

http://www.microsoft.com/downloads/details.aspx?FamilyID=c17ba869-9671-4330-a63e-1fd44e0e2505&displaylang=en

comment:8 Changed 8 years ago by Achim J Latz

How is it done on Bacula

in win32/filed/main.cpp

  /* Start up Volume Shadow Copy (only on FD) */
  VSSInit();

is this really the start of the routine, or simply a helper main.cpp?

which calls win32/filed/vss.cpp

void VSSInit()
{
   /* decide which vss class to initialize */
   if (g_MajorVersion == 5) {
      switch (g_MinorVersion) {
      case 1:
         g_pVSSClient = new VSSClientXP();
         atexit(VSSCleanup);
         return;
      case 2:
         g_pVSSClient = new VSSClient2003();
         atexit(VSSCleanup);
         return;
      }
   /* Vista or Longhorn or later */
//       } else if (g_MajorVersion == 6 && g_MinorVersion == 0) {
   } else if (g_MajorVersion >= 6) {
      g_pVSSClient = new VSSClientVista();
      atexit(VSSCleanup);
      return;
   }
}

so a global variable g_pVSSClient that contains the object for VSS control

now in filed/job.c and

#if defined(WIN32_VSS)
#include "vss.h"

static pthread_mutex_t vss_mutex = PTHREAD_MUTEX_INITIALIZER;
static int enable_vss;
#endif


#if defined(WIN32_VSS)
   int vss = 0;

   sscanf(dir->msg, "fileset vss=%d", &vss);
   enable_vss = vss;
#endif



#if defined(WIN32_VSS)
   // capture state here, if client is backed up by multiple directors
   // and one enables vss and the other does not then enable_vss can change
   // between here and where its evaluated after the job completes.
   jcr->VSS = g_pVSSClient && enable_vss;
   if (jcr->VSS) {
      /* Run only one at a time */
      P(vss_mutex);
   }
#endif


static int backup_cmd(JCR *jcr)

#if defined(WIN32_VSS)
   /* START VSS ON WIN32 */
   if (jcr->VSS) {
      if (g_pVSSClient->InitializeForBackup()) {
        /* tell vss which drives to snapshot */
        char szWinDriveLetters[27];
        if (get_win32_driveletters(jcr->ff, szWinDriveLetters)) {
            Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=\"%s\", Drive(s)=\"%s\"\n"), g_pVSSClient->GetDriverName(), szWinDriveLetters);
            if (!g_pVSSClient->CreateSnapshots(szWinDriveLetters)) {
               Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshots failed.\n"));
               jcr->JobErrors++;
            } else {
               /* tell user if snapshot creation of a specific drive failed */
               int i;
               for (i=0; i < (int)strlen(szWinDriveLetters); i++) {
                  if (islower(szWinDriveLetters[i])) {
                     Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshot of drive \"%c:\\\" failed. VSS support is disabled on this drive.\n"), szWinDriveLetters[i]);
                     jcr->JobErrors++;
                  }
               }
               /* inform user about writer states */
               for (i=0; i < (int)g_pVSSClient->GetWriterCount(); i++)
                  if (g_pVSSClient->GetWriterState(i) < 1) {
                     Jmsg(jcr, M_WARNING, 0, _("VSS Writer (PrepareForBackup): %s\n"), g_pVSSClient->GetWriterInfo(i));
                     jcr->JobErrors++;
                  }
            }
        } else {
            Jmsg(jcr, M_INFO, 0, _("No drive letters found for generating VSS snapshots.\n"));
        }
      } else {
         berrno be;
         Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled. ERR=%s\n"), be.bstrerror());
      }
      run_scripts(jcr, jcr->RunScripts, "ClientAfterVSS");
   }
#endif


#if defined(WIN32_VSS)
   /* STOP VSS ON WIN32 */
   /* tell vss to close the backup session */
   if (jcr->VSS) {
      if (g_pVSSClient->CloseBackup()) {
         /* inform user about writer states */
         for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) {
            int msg_type = M_INFO;
            if (g_pVSSClient->GetWriterState(i) < 1) {
               msg_type = M_WARNING;
               jcr->JobErrors++;
            }
            Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i));
         }
      }
      V(vss_mutex);
   }
#endif

and in filed/status.c

#ifdef WIN32_VSS
#include "vss.h"
#define VSS " VSS"
extern VSSClient *g_pVSSClient;
#else
#define VSS ""
#endif

len = Mmsg(msg, "VSS %s, Priv 0x%x\n", g_pVSSClient?"enabled":"disabled", privs);


#ifdef WIN32_VSS
   if (g_pVSSClient && g_pVSSClient->IsInitialized()) {
      vss = "VSS ";
   }
#endif

comment:9 Changed 3 years ago by chris

We have basic VSS support in Box Backup trunk, soon to be released as 0.13. We need official MSVC 64-bit builds to go along with it, and that requires merging Charles' fixes from his branch.

Note: See TracTickets for help on using tickets.