예제 #1
0
// cleanFilesystem runs fsck to make sure the filesystem under config.dest_dev is
// intact, and sets the number of times to check to 0 and the last time
// checked to now. This option should only be used in EXTn filesystems or
// filesystems that support tunefs.
func (b *Backup) cleanFilesystem() error {
	// fsck (read-only check)
	cmd := []string{fsckCmd, "-n", b.config.DestDev}
	if err := execute.Run("FS_CLEANUP", cmd, b.log); err != nil {
		return fmt.Errorf("error running %q: %v", cmd, err)
	}
	// Tunefs
	cmd = []string{tunefsCmd, "-C", "0", "-T", "now", b.config.DestDev}
	return execute.Run("FS_CLEANUP", cmd, b.log)
}
예제 #2
0
// openLuks opens the luks destination device into a temporary /dev/mapper
// device file and retuns the /dev/mapper device filename.
func (b *Backup) openLuks() (string, error) {
	// Our temporary dev/mapper device is based on the config name
	devname := "netbackup_" + b.config.Name
	devfile := filepath.Join(devMapperDir, devname)

	// Make sure it doesn't already exist
	if _, err := os.Stat(devfile); err == nil {
		return "", fmt.Errorf("device mapper file %q already exists", devfile)
	}

	// cryptsetup LuksOpen
	cmd := []string{cryptSetupCmd}
	if b.config.LuksKeyFile != "" {
		cmd = append(cmd, "--key-file="+b.config.LuksKeyFile)
	}
	cmd = append(cmd, "luksOpen")
	cmd = append(cmd, b.config.LuksDestDev)
	cmd = append(cmd, devname)

	if err := execute.Run("LUKS_OPEN", cmd, b.log); err != nil {
		return "", err
	}

	return devfile, nil
}
예제 #3
0
// mountDev mounts the destination device into a temporary mount point and
// returns the mount point name.
func (b *Backup) mountDev() (string, error) {
	tmpdir, err := ioutil.TempDir("", "netbackup_mount")
	if err != nil {
		return "", fmt.Errorf("unable to create temp directory: %v", err)
	}

	// We use the mount command instead of the mount syscall as it makes
	// simpler to specify defaults in /etc/fstab.
	cmd := []string{mountCmd, b.config.DestDev, tmpdir}
	if err := execute.Run("MOUNT", cmd, b.log); err != nil {
		return "", err
	}

	return tmpdir, nil
}
예제 #4
0
// umountDev dismounts the destination device specified in config.DestDev.
func (b *Backup) umountDev() error {
	cmd := []string{umountCmd, b.config.DestDev}
	return execute.Run("UMOUNT", cmd, b.log)
}
예제 #5
0
// Run executes the backup according to the config file and options.
func (b *Backup) Run() error {
	var transp interface {
		Run() error
	}

	// If we're running in dry-run mode, we set dummy values for DestDev if
	// LuksDestDev is present, and for DestDir if DestDev is present. This hack
	// is necessary because these values won't be set to the appropriate values
	// in dry-run mode (since we don't want to open the luks destination in
	// that case) and the transports won't be able to show a full command line
	// in that case.

	if b.dryRun {
		if b.config.LuksDestDev != "" {
			b.config.DestDev = "dummy_dest_dev"
		}
		if b.config.DestDev != "" {
			b.config.DestDir = "dummy_dest_dir"
		}
	}

	if !b.dryRun {
		// Make sure sourcedir is a mountpoint, if requested. This should
		// reduce the risk of backing up an empty (unmounted) source on top of
		// a full destination.
		if b.config.SourceIsMountPoint {
			mounted, err := isMounted(b.config.SourceDir)
			if err != nil {
				return fmt.Errorf("Unable to verify if source_dir is mounted: %v", err)
			}
			if !mounted {
				return fmt.Errorf("SourceDir (%s) should be a mountpoint, but is not mounted", b.config.SourceDir)
			}
		}

		// Open LUKS device, if needed
		if b.config.LuksDestDev != "" {
			devfile, err := b.openLuks()
			if err != nil {
				return fmt.Errorf("Error opening LUKS device %q: %v", b.config.LuksDestDev, err)
			}
			// Set the destination device to the /dev/mapper device opened by
			// LUKS. This should allow the natural processing to mount and
			// dismount this device.
			b.config.DestDev = devfile

			// close luks device at the end
			defer b.closeLuks()
			defer time.Sleep(2 * time.Second)
		}

		// Run cleanup on fs prior to backup, if requested.
		if b.config.FSCleanup {
			if err := b.cleanFilesystem(); err != nil {
				return fmt.Errorf("Error performing pre-backup cleanup on %q: %v", b.config.DestDev, err)
			}
		}

		// Mount destination device, if needed.
		if b.config.DestDev != "" {
			tmpdir, err := b.mountDev()
			if err != nil {
				return fmt.Errorf("Error opening destination device %q: %v", b.config.DestDev, err)
			}
			// After we mount the destination device, we set Destdir to that location
			// so the backup will proceed seamlessly.
			b.config.DestDir = tmpdir

			// umount destination filesystem and remove temp mount point.
			defer os.Remove(b.config.DestDir)
			defer b.umountDev()
			// For some reason, not having a pause before attempting to unmount
			// can generate a race condition where umount complains that the fs
			// is busy (even though the transport is already down.)
			defer time.Sleep(2 * time.Second)
		}
	}

	var err error

	// Create new transport based on config.Transport
	switch b.config.Transport {
	case "rclone":
		transp, err = transports.NewRcloneTransport(b.config, nil, b.log, b.dryRun)
	case "rdiff-backup":
		transp, err = transports.NewRdiffBackupTransport(b.config, nil, b.log, b.dryRun)
	case "rsync":
		transp, err = transports.NewRsyncTransport(b.config, nil, b.log, b.dryRun)
	default:
		return fmt.Errorf("Unknown transport %q", b.config.Transport)
	}
	if err != nil {
		return fmt.Errorf("Error creating %s transport: %v", b.config.Transport, err)
	}

	// Execute pre-commands, if any.
	if b.config.PreCommand != "" && !b.dryRun {
		if err := execute.Run("PRE", execute.WithShell(b.config.PreCommand), b.log); err != nil {
			return fmt.Errorf("Error running pre-command: %v", err)
		}
	}

	// Make it so...
	if err := transp.Run(); err != nil {
		return fmt.Errorf("Error running backup: %v", err)
	}

	// Execute post-commands, if any.
	if b.config.PostCommand != "" && !b.dryRun {
		if err := execute.Run("POST", execute.WithShell(b.config.PostCommand), b.log); err != nil {
			return fmt.Errorf("Error running post-command (possible backup failure): %v", err)
		}
	}

	return nil
}
예제 #6
0
// closeLuks closes the current destination device.
func (b *Backup) closeLuks() error {
	// cryptsetup luksClose needs the /dev/mapper device name.
	cmd := []string{cryptSetupCmd, "luksClose", b.config.DestDev}
	return execute.Run("LUKS_CLOSE", cmd, b.log)
}