Skip to content

Commit 335113a

Browse files
committed
fix cleanup of untracked backup files
1 parent eb3f9d0 commit 335113a

File tree

1 file changed

+53
-31
lines changed

1 file changed

+53
-31
lines changed

server/lib/classes/backup.inc.php

Lines changed: 53 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ protected static function clearBackups($server_id, $web_id, $max_backup_copies,
721721
}
722722

723723
/**
724-
* Garbage collection: deletes records from database about files that do not exist and deletes untracked files.
724+
* Garbage collection: deletes records from database about files that do not exist and deletes untracked files and cleans up backup download directories.
725725
* The backup directory must be mounted before calling this method.
726726
* @param int $server_id
727727
* @param string|null $backup_type if defined then process only backups of this type
@@ -740,25 +740,32 @@ protected static function backups_garbage_collection($server_id, $backup_type =
740740
$backup_dir = trim($server_config['backup_dir']);
741741
$sql = "SELECT * FROM web_backup WHERE server_id = ?";
742742
$sql_domains = "SELECT domain_id,document_root,system_user,system_group,backup_interval FROM web_domain WHERE server_id = ? AND (type = 'vhost' OR type = 'vhostsubdomain' OR type = 'vhostalias')";
743+
$sql_domains_with_backups = "SELECT domain_id,document_root,system_user,system_group,backup_interval FROM web_domain WHERE domain_id in (SELECT * FROM web_backup WHERE server_id = ?" . ((!empty($backup_type)) ? " AND backup_type = ?" : "") . ") AND (type = 'vhost' OR type = 'vhostsubdomain' OR type = 'vhostalias')";
743744
array_push($args, $server_id);
744745
array_push($args_domains, $server_id);
746+
array_push($args_domains_with_backups, $server_id);
745747
if (!empty($backup_type)) {
746748
$sql .= " AND backup_type = ?";
747749
array_push($args, $backup_type);
750+
array_push($args_domains_with_backups, $backup_type);
748751
}
749752
if (!empty($domain_id)) {
750753
$sql .= " AND parent_domain_id = ?";
751754
$sql_domains .= " AND domain_id = ?";
755+
$sql_domains_with_backups .= " AND domain_id = ?";
752756
array_push($args, $domain_id);
753757
array_push($args_domains, $domain_id);
758+
array_push($args_domains_with_backups, $domain_id);
754759
}
755760
array_unshift($args, $sql);
756761
array_unshift($args_domains, $sql_domains);
762+
array_unshift($args_domains_with_backups, $sql_domains);
757763

758764
$db_list = array($app->db);
759765
if ($app->db->dbHost != $app->dbmaster->dbHost)
760766
array_push($db_list, $app->dbmaster);
761767

768+
// Cleanup web_backup entries for non-existent backup files
762769
foreach ($db_list as $db) {
763770
$backups = call_user_func_array(array($db, "queryAllRecords"), $args);
764771
foreach ($backups as $backup) {
@@ -771,27 +778,42 @@ protected static function backups_garbage_collection($server_id, $backup_type =
771778
}
772779
}
773780

774-
foreach ($db_list as $db) {
775-
$domains = call_user_func_array(array($db, "queryAllRecords"), $args_domains);
776-
foreach ($domains as $rec) {
777-
$domain_id = $rec['domain_id'];
778-
$domain_backup_dir = $backup_dir . '/web' . $domain_id;
779-
$files = self::get_files($domain_backup_dir);
780-
781-
//Delete files that are in backup directory, but do not exist in database
782-
if (!empty($files)) {
783-
$sql = "SELECT backup_id,filename FROM web_backup WHERE server_id = ? AND parent_domain_id = ?";
781+
// Cleanup backup files with missing web_backup entries (runs on all servers)
782+
$domains = $app->dbmaster->queryAllRecords($args_domains_with_backups);
783+
foreach ($domains as $rec) {
784+
$domain_id = $rec['domain_id'];
785+
$domain_backup_dir = $backup_dir . '/web' . $domain_id;
786+
$files = self::get_files($domain_backup_dir);
787+
788+
if (!empty($files)) {
789+
// leave out server_id here, in case backup storage is shared between servers
790+
$sql = "SELECT backup_id, filename FROM web_backup WHERE parent_domain_id = ?";
791+
foreach ($db_list as $db) {
792+
$backup_record_exists = false;
784793
$backups = $db->queryAllRecords($sql, $server_id, $domain_id);
785794
foreach ($backups as $backup) {
786-
if (!in_array($backup['filename'],$files)) {
787-
$backup_file = $backup_dir . '/web' . $domain_id . '/' . $backup['filename'];
788-
$app->log('Backup file ' . $backup_file . ' is not contained in database, deleting this file from disk', LOGLEVEL_DEBUG);
789-
@unlink($backup_file);
795+
if (in_array($backup['filename'],$files)) {
796+
$backup_record_exists = true;
790797
}
791798
}
799+
if (!$backup_record_exists) {
800+
$backup_file = $backup_dir . '/web' . $domain_id . '/' . $backup['filename'];
801+
$app->log('Backup file ' . $backup_file . ' is not contained in database, deleting this file from disk', LOGLEVEL_DEBUG);
802+
@unlink($backup_file);
803+
}
792804
}
805+
}
806+
}
793807

794-
//Remove backupdir symlink and create as directory instead
808+
// This cleanup only runs on web servers
809+
$domains = $app->db->queryAllRecords($args_domains);
810+
foreach ($domains as $rec) {
811+
$domain_id = $rec['domain_id'];
812+
$domain_backup_dir = $backup_dir . '/web' . $domain_id;
813+
$files = self::get_files($domain_backup_dir);
814+
815+
// Remove backupdir symlink and create as directory instead
816+
if (is_link($backup_download_dir) || !is_dir($backup_download_dir)) {
795817
$web_path = $rec['document_root'];
796818
$app->system->web_folder_protection($web_path, false);
797819

@@ -806,23 +828,23 @@ protected static function backups_garbage_collection($server_id, $backup_type =
806828
}
807829

808830
$app->system->web_folder_protection($web_path, true);
831+
}
809832

810-
// delete old files from backup download dir (/var/www/example.com/backup)
811-
if (is_dir($backup_download_dir)) {
812-
$dir_handle = dir($backup_download_dir);
813-
$now = time();
814-
while (false !== ($entry = $dir_handle->read())) {
815-
$full_filename = $backup_download_dir . '/' . $entry;
816-
if ($entry != '.' && $entry != '..' && is_file($full_filename)) {
817-
// delete files older than 3 days
818-
if ($now - filemtime($full_filename) >= 60 * 60 * 24 * 3) {
819-
$app->log('Backup file ' . $full_filename . ' is too old, deleting this file from disk', LOGLEVEL_DEBUG);
820-
@unlink($full_filename);
821-
}
833+
// delete old files from backup download dir (/var/www/example.com/backup)
834+
if (is_dir($backup_download_dir)) {
835+
$dir_handle = dir($backup_download_dir);
836+
$now = time();
837+
while (false !== ($entry = $dir_handle->read())) {
838+
$full_filename = $backup_download_dir . '/' . $entry;
839+
if ($entry != '.' && $entry != '..' && is_file($full_filename)) {
840+
// delete files older than 3 days
841+
if ($now - filemtime($full_filename) >= 60 * 60 * 24 * 3) {
842+
$app->log('Backup file ' . $full_filename . ' is too old, deleting this file from disk', LOGLEVEL_DEBUG);
843+
@unlink($full_filename);
822844
}
823845
}
824-
$dir_handle->close();
825846
}
847+
$dir_handle->close();
826848
}
827849
}
828850
}
@@ -1419,7 +1441,7 @@ public static function run_backup($domain_id, $type, $backup_job, $mount = true)
14191441
$ok = self::make_web_backup($rec, $backup_job);
14201442
break;
14211443
case 'mysql':
1422-
$rec['server_id'] = $server_id;
1444+
$rec['server_id'] = $server_id;
14231445
$ok = self::make_database_backup($rec, $backup_job);
14241446
break;
14251447
default:
@@ -1460,7 +1482,7 @@ public static function run_all_backups($server_id, $backup_job = "auto")
14601482
}
14611483
}
14621484

1463-
$sql = "SELECT DISTINCT d.*, db.server_id as `server_id` FROM web_database as db INNER JOIN web_domain as d ON (d.domain_id = db.parent_domain_id) WHERE db.server_id = ? AND db.active = 'y' AND d.backup_interval != 'none' AND d.backup_interval != ''";
1485+
$sql = "SELECT DISTINCT d.*, db.server_id as `server_id` FROM web_database as db INNER JOIN web_domain as d ON (d.domain_id = db.parent_domain_id) WHERE db.server_id = ? AND db.active = 'y' AND d.backup_interval != 'none' AND d.backup_interval != ''";
14641486
$databases = $app->dbmaster->queryAllRecords($sql, $server_id);
14651487

14661488
foreach ($databases as $database) {

0 commit comments

Comments
 (0)