Pull to refresh

Backup Time Machine своими руками

Reading time3 min
Views5.2K
Как не крути, а в новогодние праздники, риск порчи файлов значительно возрастает. Не миновала сия беда и меня. Как не трудно догадаться, я перепутал диск при форматировании и… да-да, все что было нажито неправедным путем непосильным трудом, в один момент было уничтожено.

Помянув сборник софта и архив отсканированных справочников, я задумался над вопросом бекапов. И… Пришел к выводу, что того что мне в самом деле требуется, нет. Точнее конечно же есть, но либо стоит дорого, либо работает не так, как мне бы того хотелось.

Закончив с пытками гугла на тему: «сделай мне хорошо», решил поступить как истинный Unix`оид, хоть и работающий в форточках. А именно: не выпендривайся, чем проще — лучше.

Тут я вспомнил презентацию MacOS на которой демонстрировали их Time Machine. Ведь если подумать, очень удобно иметь возможность получить доступ к любому файлу за любой день. Но… Если делать полные копии в виде архивов, то никаких объемов не хватит, чтобы это все хранить. Дальше мысль зацепилась за инкрементные бекапы. То есть в первый раз вы делаете полный архив, а затем архивируете только то, что изменилось.

И… Этот вариант я тоже отбросил, как не удобный для моего случая. Во-первых, существует необходимость в возможности удалять произвольные «дни». А во-вторых, я очень часто переименовываю свои файлы и время от времени перекладываю из директорию в директорию. А это, в свою очередь, к моему удивлению, срезало почти всех кандидатов в «бекаперы». То есть, софт тупо смотрел на имя файла, дату его изменения и… и все. В итоге бекап разбухал.

Итак, спасли меня две идеи:
Во-первых, не важно как называется файл, важно содержание. Таким образом с каждого файла должно сниматься несколько хешей и на основе этой сигнатуры можно достаточно точно судить, что это за файл. В моем случае я ограничился снятием md5 суммы и размером файла. Выбор конечно спорный, но для сканов этого вполне достаточно.
Во-вторых, если файл не изменился или изменилость только его имя, надо копировать не весь файл, а делать на него жесткую ссылку, благо в NTFS такие есть.

Если кто не знает, то благодаря команде:
fsutil hardlink create <ссылка> <файл>
В windows можно получить настоящую жесткую ссылку.

В итоге, получился простой алгоритм, который я, не заморачиваясь, оформил на консольном PHP. Теперь бекап происходит при подключении переносного диска к компьютеру, либо (если диск уже подключен) раз в сутки.

И вот собственно сам «бекапер».
<?php
//
$dir = array();
$hah = array();
$hah_new = array();
$file = array();
$copy = 0;
$link = 0;
include 'conf.php';
$date = date('Y-m-d');

// выход если бекап на сегодня уже существует
if(is_dir($date)){
	exit("Backup already exists\n");
}

// составляем список существующих файлов
foreach(glob('*', GLOB_ONLYDIR) as $v){
	if(is_file($v.'/hah.db')){
		$hah = array_merge($hah, unserialize(file_get_contents($v.'/hah.db')));
	}
}

// создаем дерево директорий
foreach($dir as $v){
	$x = explode('/', $v);
	array_unshift($x, $date);
	$x[1] = substr($x[1], 0, 1);
	foreach($x as $k=>$v){
		$y = implode('/', array_slice($x, 0, $k+1));
		if(!is_dir($y)){
			mkdir($y);
		}
	}
}

// получаем список файлов
while($n = array_pop($dir)){
	if(!is_dir($date.'/'.substr($n, 0, 1).'/'.substr($n, 3))){
		mkdir($date.'/'.substr($n, 0, 1).'/'.substr($n, 3));
	}
	$dir = array_merge($dir, glob($n.'/*', GLOB_ONLYDIR));
	$file = array_merge($file, array_diff(glob($n.'/*'), glob($n.'/*', GLOB_ONLYDIR)));
}

// копируем новые и линкуем старые файлы
foreach($file as $k=>$v){
	$x = md5_file($v).filesize($v);
	if(!$x){
		continue;
	}
	$f = $date.'/'.substr($v, 0, 1).'/'.substr($v, 3);
	if($hah[$x]){
		exec('fsutil hardlink create "'.$f.'" "'.$hah[$x].'"');
		$hah_new[$x] = $f;
		$link++;
	}else{
		copy($v, $f);
		$hah_new[$x] = $f;
		$copy++;
	}
	print ceil($k*100/count($file))."%\r";
}
print "\nLink: ".$link."\n";
print "Copy: ".$copy."\n";

// сохраняем информацию о файлах
file_put_contents($date.'/hah.db', serialize($hah_new));
exit;


Его конфигурация:
<?php
date_default_timezone_set('Asia/Novosibirsk');

$dir[] = 'c:/scan'; // сканы
$dir[] = 'c:/web'; // корень локального сервера
$dir[] = 'c:/gohsrf'; // приказы
$dir[] = 'q:'; // флешка


Ну и BAT`ник его запускающий:
@echo off
cls
php backup.php
pause
Tags:
Hubs:
+11
Comments47

Articles