View Single Post
09 Mar 2012  
TanaT

Windows 7 Ultimate x64
 
 
Laptop backup strategy / SD card anywhere + "big" hard drive at home

Hello,

I've just solved a backup strategy problem and I want to share it in case it could be useful.

If you think it's not that good, consider closing or deleting this post ;-) !

I use my laptop in many circumstances : office, my home, other homes, etc. Sometimes with internet access, sometimes not.

I can't feel safe with only one hard drive running, but laptops with 2 bays are big and hard to find if you want to set a RAID mirroring solution.

I don't want to carry external hard drive every where even if they could be small.

But I have one or two hard drives available when I'm at my office or home.

When I'm not there, I could rely on a USB key or SD card inserted in my laptop anytime to faike a mirroring. But of course it's too small to backup all I want : it has to be differential.

So here is my solution build on powershell and robocopy.
Following script should be launched automatically or manually after correctly set.

It looks for any folder name $BckFold (for full backups) and $PartFold (for partial backups) on any hard drive found and launch appropriate backups.

If it's not clear, just ask.

Hope it could be useful to someone...

Bye.

TanaT


[ EDIT 2012-03-13 ] ! Please use version 0.1 in the following reply !

Code:
# -----------------------------------------------------------------
# ------------------------ MVBACKUP -------------------------------
# -----------------------------------------------------------------
#
# Laptop backups management, by TanaT.
# Created 2012-03-03.
#
# 1. Complete SETTINGS part with the folders you want to backup.
# 2. Launch the script manually anytime you want or automatically with
# control panel/Admin tools/Tasks scheduler.
#
# Any time the script is launched, it looks for available Destination
# directories in any hard drive available. If it finds full backup
# directories as set in BckFold it launches a mirroring to it and set
# the archive flags of sources files. If it finds partial backup
# directories in any hard drive available (like USB Key for instance)
# it launches a mirroring to it restricted to files without any archive
# flag set (those who hasn't been backed up yet).

# Example :
# BckFold = "_mvBackup_" and PrtFold = "_partBack_"
# Create a folder named _mvBackup_ in a big external hard drive (500Go ?)
# Create a folder named _partBack_ in a USB Key (8Go ?)
# Keep your big hard drive home and bring your USB Key with your laptop.
# Connect your hard drive and launch mvbackup.ps1 when your home.
# Connect your USB key and launch mvbackup.ps1 when your out and you've
# just worked hours on any kind of stuff you want to keep safe.
# When you're back home, launch it again on your external hard drive.
# You can have as many as you want drives (big or small). Just create the
# appropriate folder at the root of your drive.

# -----------------------------------------------------------------
# ------------------------ SETTINGS -------------------------------
# -----------------------------------------------------------------

# List of "folders to backup" = "Names of folders to give in archive" separated with ;
$SrcDirs = @{
"C:\Users\TanaT\Pictures" = "Images_TanaT";
"C:\Users\TanaT\Documents" = "MesDocs_TanaT"
}

# Name of full backup folder we should look for in "big" hard drives
$BckFold = "mvBackup_AT"

# Name of partial backup folder we should look for in "small" usb keys / SD cards
$PrtFold = "partBack_AT"

# Timestamps when a backup action (full) is complete
# are stored in $SrcDirs_items\$LastFTSFile (source side)
# Timestamps when a backup action (partial) is complete
# are stored in $PrtFold\$BckDirs_items\$LastPTSFile (destination side)
#   (Creation timestamps of files are used.)
$LastFTSFile = ".lastFullTimestamp.mvb"
$LastPTSFile = ".lastPartTimestamp.mvb"
$LogFile = "C:\users\anatole\mvbackup.log"
# -----------------------------------------------------------------
# ------------------------ ROUTINES -------------------------------
# -----------------------------------------------------------------

# Touch equivalent except that it works with Creation timestamp
function TouchFile ()
{ 
  param([string] $file)
  if (test-path $file)
  { $now = Get-Date
    (Get-Item $file).CreationTime = $now }
  else
  { echo $null > $file }
}

# MvMirror full|part srcDir destDir
# case full :
#   * Mirror srcDir to destDir
#   * Set Archive flag of every saved files and dirs
#   * Set creation time of $args[1] \ $LastFTSFile = now 
# case part :
#   * mirror srcDir to destDir ignoring files with Archive flag set
#   * Set creation time of $args[2] \ $LastPTSFile = now
function MvMirror ()
{
  If($args.Count -ne 3)
  {
    write-host "Function MvMirror must have 3 arguments."
    return
  }

  $action = $args[0]
  If($action -eq "full")
  {
    # Full backup : Mirror all and unset Archive flag of source files/folders
    #write-host robocopy $args[1] $args[2] /MIR /COPYALL /M /R:1 /W:2 /NP /log:$LogFile
    robocopy $args[1] $args[2] /MIR /M /XJ /R:1 /W:2 /NP /log:$LogFile /XF $LastFTSFile $LastPTSFile
    $firstErrLev = $?
    robocopy $args[1] $args[2] /MIR /XJ /R:1 /W:2 /NP /log:$LogFile /XF $LastFTSFile $LastPTSFile
    if ( ( $firstErrLev -le 1 ) -and ( $? -le 1 ) )
    { # Success set new timestamp of full backup
      write-host TouchFile ("$($args[1])\$LastFTSFile")
      TouchFile ("$($args[1])\$LastFTSFile")
    }
  }
  elseIf($action -eq "part")
  {
    # Partial backup : Mirror files with Archive flag set
    # (those that hasn't been backed up yet)
    robocopy $args[1] $args[2] /MIR /IA:A /S /XJ /R:1 /W:2 /NP /log:$LogFile /XF $LastFTSFile $LastPTSFile
    if ( $? -le 1 )
    { # Success set new timestamp of partial backup
      write-host TouchFile ("$($args[2])\$LastPTSFile")
      TouchFile ("$($args[2])\$LastPTSFile")
    }
  }
  else
  {
    write-host "Function MvMirror: 1st arg must be full or part."
  }
}

# -----------------------------------------------------------------
# ------------------------ MAIN -----------------------------------
# -----------------------------------------------------------------

# ------------------------ GLOBAL CONTROLS ------------------------

# Invalid filename chars pattern
$pattern = "[{0}]" -f ([Regex]::Escape([String] `
[System.IO.Path]::GetInvalidFilenameChars()))

foreach ($dir in ($SrcDirs.clone()).Keys)
{
  # If a source folder doesn't exist, remove it from the list
  if ( ! (Test-Path $dir -PathType container) )
  {    
    write-host "Removing $dir from the list (unreadable dir)"
    $SrcDirs.Remove($dir)
  }
  else
  {
    # If a destination dir name is illegal, remove it from the list
    if ($SrcDirs[$dir] -match $pattern)
    {
      write-host "Removing $dir from the list (illegal foldername $SrcDirs[$dir]"
      $SrcDirs.Remove($dir)
    }
  }
}

# ------------------------ DIRS SEARCH ----------------------------
$FullBDirs = @() # To list any full    backup drive + path found
$PartBDirs = @() # To list any partial backup drive + path found

# Look for any hard drive letter and search for mvbackup folders in them
foreach ($drive in (Get-WmiObject Win32_LogicalDisk -filter "DriveType = 2 or DriveType=3"))
{
  if (Test-Path "$($drive.DeviceID)\$BckFold")
  {
    $FullBDirs += "$($drive.DeviceID)\$BckFold" # Add a full backup dir
  }  
  if (Test-Path "$($drive.DeviceID)\$PrtFold")
  {
    $PartBDirs += "$($drive.DeviceID)\$PrtFold" # Add a partial backup dir
  }    
}

write-host "Full :"
$FullBDirs

write-host "Partial :"
$PartBDirs

# ------------------------ BEGIN ! --------------------------------

# -- If full backup dir available, do it
if ($FullBDirs.length -gt 0)
{
  write-host I have full back up to do
  foreach ($currFullDestPath in $FullBDirs)
  {
    # loop over any full destination dirs available
    foreach ( $currSrcDir in ($SrcDirs.Keys) )
    {
      write-host "Processing full $currSrcDir $currFullDestPath\$($SrcDirs[$currSrcDir])"
      MvMirror full $currSrcDir $currFullDestPath\$($SrcDirs[$currSrcDir])
    }
  }
}

# -- If partial backup dir available, do it
if ($PartBDirs.length -gt 0)
{
  write-host I have partial back up to do
  foreach ($currPartDestPath in $PartBDirs)
  {
    # loop over any partial destination dirs available
    foreach ( $currSrcDir in ($SrcDirs.Keys) )
    {
      # If a full backup occurred more recently than last partial backup, empty partial dir before.
      if ( (Test-Path "$currPartDestPath\$($SrcDirs[$currSrcDir])\$LastPTSFile") -and (Test-Path "$currSrcDir\$LastFTSFile") )
      {
        if ((Get-Item "$currPartDestPath\$($SrcDirs[$currSrcDir])\$LastPTSFile").CreationTime -lt (Get-Item "$currSrcDir\$LastFTSFile").CreationTime)
        {
          write-host "Delete partial folder first"
          Remove-Item $currPartDestPath\$($SrcDirs[$currSrcDir]) -recurse
        }
      }        
      write-host "Processing partial $currSrcDir $currPartDestPath\$($SrcDirs[$currSrcDir])"
      MvMirror part $currSrcDir $currPartDestPath\$($SrcDirs[$currSrcDir])
    }
  }
}


# ------------------------ EOF ------------------------------------
My System SpecsSystem Spec