@@ -736,11 +736,12 @@ protected static function backups_garbage_collection($server_id, $backup_type =
736736 //First check that all records in database have related files and delete records without files on disk
737737 $ args = array ();
738738 $ args_domains = array ();
739+ $ args_domains_with_backups = array ();
739740 $ server_config = $ app ->getconf ->get_server_config ($ server_id , 'server ' );
740741 $ backup_dir = trim ($ server_config ['backup_dir ' ]);
741742 $ sql = "SELECT * FROM web_backup WHERE server_id = ? " ;
742743 $ 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') " ;
744+ $ sql_domains_with_backups = "SELECT domain_id,document_root,system_user,system_group,backup_interval FROM web_domain WHERE domain_id in (SELECT parent_domain_id FROM web_backup WHERE server_id = ? " . ((!empty ($ backup_type )) ? " AND backup_type = ? " : "" ) . ") AND (type = 'vhost' OR type = 'vhostsubdomain' OR type = 'vhostalias') " ;
744745 array_push ($ args , $ server_id );
745746 array_push ($ args_domains , $ server_id );
746747 array_push ($ args_domains_with_backups , $ server_id );
@@ -759,7 +760,7 @@ protected static function backups_garbage_collection($server_id, $backup_type =
759760 }
760761 array_unshift ($ args , $ sql );
761762 array_unshift ($ args_domains , $ sql_domains );
762- array_unshift ($ args_domains_with_backups , $ sql_domains );
763+ array_unshift ($ args_domains_with_backups , $ sql_domains_with_backups );
763764
764765 $ db_list = array ($ app ->db );
765766 if ($ app ->db ->dbHost != $ app ->dbmaster ->dbHost )
@@ -779,7 +780,7 @@ protected static function backups_garbage_collection($server_id, $backup_type =
779780 }
780781
781782 // Cleanup backup files with missing web_backup entries (runs on all servers)
782- $ domains = $ app ->dbmaster -> queryAllRecords ( $ args_domains_with_backups );
783+ $ domains = call_user_func_array ( array ( $ app ->dbmaster , " queryAllRecords " ), $ args_domains_with_backups );
783784 foreach ($ domains as $ rec ) {
784785 $ domain_id = $ rec ['domain_id ' ];
785786 $ domain_backup_dir = $ backup_dir . '/web ' . $ domain_id ;
@@ -788,29 +789,29 @@ protected static function backups_garbage_collection($server_id, $backup_type =
788789 if (!empty ($ files )) {
789790 // leave out server_id here, in case backup storage is shared between servers
790791 $ sql = "SELECT backup_id, filename FROM web_backup WHERE parent_domain_id = ? " ;
792+ $ untracked_backup_files = array ();
791793 foreach ($ db_list as $ db ) {
792- $ backup_record_exists = false ;
793- $ backups = $ db ->queryAllRecords ($ sql , $ server_id , $ domain_id );
794+ $ backups = $ db ->queryAllRecords ($ sql , $ domain_id );
794795 foreach ($ backups as $ backup ) {
795- if (in_array ($ backup ['filename ' ],$ files )) {
796- $ backup_record_exists = true ;
796+ if (! in_array ($ backup ['filename ' ],$ files )) {
797+ $ untracked_backup_files [] = $ backup [ ' filename ' ] ;
797798 }
798799 }
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- }
800+ }
801+ array_unique ( $ untracked_backup_files );
802+ foreach ($ untracked_backup_files as $ f ) {
803+ $ backup_file = $ backup_dir . '/web ' . $ domain_id . '/ ' . $ f ;
804+ $ app ->log ('Backup file ' . $ backup_file . ' is not contained in database, deleting this file from disk ' , LOGLEVEL_DEBUG );
805+ @unlink ($ backup_file );
804806 }
805807 }
806808 }
807809
808810 // This cleanup only runs on web servers
809- $ domains = $ app ->db -> queryAllRecords ( $ args_domains );
811+ $ domains = call_user_func_array ( array ( $ app ->db , " queryAllRecords " ), $ args_domains );
810812 foreach ($ domains as $ rec ) {
811813 $ domain_id = $ rec ['domain_id ' ];
812814 $ domain_backup_dir = $ backup_dir . '/web ' . $ domain_id ;
813- $ files = self ::get_files ($ domain_backup_dir );
814815
815816 // Remove backupdir symlink and create as directory instead
816817 if (is_link ($ backup_download_dir ) || !is_dir ($ backup_download_dir )) {
@@ -836,7 +837,7 @@ protected static function backups_garbage_collection($server_id, $backup_type =
836837 $ now = time ();
837838 while (false !== ($ entry = $ dir_handle ->read ())) {
838839 $ full_filename = $ backup_download_dir . '/ ' . $ entry ;
839- if ($ entry != '. ' && $ entry != '.. ' && is_file ($ full_filename )) {
840+ if ($ entry != '. ' && $ entry != '.. ' && is_file ($ full_filename ) && ! is_link ( $ full_filename ) ) {
840841 // delete files older than 3 days
841842 if ($ now - filemtime ($ full_filename ) >= 60 * 60 * 24 * 3 ) {
842843 $ app ->log ('Backup file ' . $ full_filename . ' is too old, deleting this file from disk ' , LOGLEVEL_DEBUG );
@@ -919,16 +920,24 @@ protected static function get_files($directory, $prefix_list = null, $endings_li
919920 * Generates excludes list for compressors
920921 * @param string[] $backup_excludes
921922 * @param string $arg
923+ * @param string $pre
924+ * @param string $post
922925 * @return string
923926 * @author Ramil Valitov <ramilvalitov@gmail.com>
924927 */
925- protected static function generateExcludeList ($ backup_excludes , $ arg )
928+ protected static function generateExcludeList ($ backup_excludes , $ arg, $ pre = '' , $ post = '' )
926929 {
927- $ excludes = implode (" " . $ arg , $ backup_excludes );
928- if (!empty ($ excludes )) {
929- $ excludes = $ arg . $ excludes ;
930+ $ excludes = "" ;
931+ foreach ($ backup_excludes as $ ex ) {
932+ # pass through escapeshellarg if not already done
933+ if ( preg_match ( "/^'.+'$/ " , $ ex ) ) {
934+ $ excludes .= "$ {arg}$ {pre}$ {ex}$ {post} " ;
935+ } else {
936+ $ excludes .= "$ {arg}" . escapeshellarg ("$ {pre}$ {ex}$ {post}" ) . " " ;
937+ }
930938 }
931- return $ excludes ;
939+
940+ return trim ( $ excludes );
932941 }
933942
934943 /**
@@ -987,12 +996,14 @@ protected static function runWebCompression($format, $backup_excludes, $backup_m
987996 if (!empty ($ password )) {
988997 $ zip_options .= ' --password ' . escapeshellarg ($ password );
989998 }
999+ $ excludes = self ::generateExcludeList ($ backup_excludes , '-x ' );
1000+ $ excludes .= " " . self ::generateExcludeList ($ backup_excludes , '-x ' , '' , '/* ' );
9901001 if ($ backup_mode == 'user_zip ' ) {
9911002 //Standard casual behaviour of ISPConfig
992- $ app ->system ->exec_safe ($ find_user_files . ' | zip ' . $ zip_options . ' -b ? ' . $ excludes . ' --symlinks ? -@' , $ web_path , $ web_user , $ web_group , $ http_server_user , $ backup_tmp , $ web_backup_dir . '/ ' . $ web_backup_file );
1003+ $ app ->system ->exec_safe ($ find_user_files . ' | zip ' . $ zip_options . ' -b ? --symlinks ? -@ ' . $ excludes , $ web_path , $ web_user , $ web_group , $ http_server_user , $ backup_tmp , $ web_backup_dir . '/ ' . $ web_backup_file );
9931004 } else {
994- //Use cd to have a correct directory structure inside the archive, extra options to zip hidden (dot) files
995- $ app ->system ->exec_safe ('cd ? && zip ' . $ zip_options . ' -b ? ' . $ excludes . ' --symlinks -r ? * .* -x "../*" ' , $ web_path , $ backup_tmp , $ web_backup_dir . '/ ' . $ web_backup_file );
1005+ //Use cd to have a correct directory structure inside the archive, zip current directory "." to include hidden (dot) files
1006+ $ app ->system ->exec_safe ('cd ? && zip ' . $ zip_options . ' -b ? --symlinks -r ? . ' . $ excludes , $ web_path , $ backup_tmp , $ web_backup_dir . '/ ' . $ web_backup_file );
9961007 }
9971008 $ exit_code = $ app ->system ->last_exec_retcode ();
9981009 // zip can return 12(due to harmless warnings) and still create valid backups
@@ -1237,8 +1248,8 @@ protected static function make_database_backup($web_domain, $backup_job)
12371248 //* Remove old backups
12381249 self ::backups_garbage_collection ($ server_id , 'mysql ' , $ domain_id );
12391250 $ prefix_list = array (
1240- ' db_ ' . escapeshellarg ( $ db_name). ' _ ' ,
1241- ' manual-db_ ' . escapeshellarg ( $ db_name). ' _ ' ,
1251+ " db_ $ { db_name} _ " ,
1252+ " manual-db_ $ { db_name} _ " ,
12421253 );
12431254 self ::clearBackups ($ server_id , $ domain_id , intval ($ rec ['backup_copies ' ]), $ db_backup_dir , $ prefix_list );
12441255 } else {
@@ -1313,7 +1324,7 @@ protected static function make_web_backup($web_domain, $backup_job)
13131324
13141325 # default exclusions
13151326 $ backup_excludes = array (
1316- escapeshellarg ( './backup\* ' ) ,
1327+ './backup* ' ,
13171328 './bin ' , './dev ' , './etc ' , './lib ' , './lib32 ' , './lib64 ' , './opt ' , './sys ' , './usr ' , './var ' , './proc ' , './run ' , './tmp ' ,
13181329 );
13191330
0 commit comments