o 0)i}@sndZddlZddlZddlZddlZddlmZmZddlmZddl Z ddl m Z ddl Z GdddZ dS)z Backup Engine - TimeMachineBackup Incremental backup system with hard-link deduplication Extracted from BackupSystem for integration with Scheduler N)datetime timedelta)Path)loggerc@seZdZddZddZdefddZdefdd Zd efd d Zd efddZ ddZ ddZ ddZ de defddZddZddZd2d d!Zd"d#Zd$d%Zd3d&d'Zd3d(d)Zd3d*d+Zd,d-Zd4d.d/Zd0d1ZdS)5TimeMachineBackupcCst|tr t|}tjtjt|}t||_|jjddd|jd|_ |jd|_ |jd|_ |j jddd| dS)NTparentsexist_okzbackup_metadata.jsonz .backup.lockstore) isinstancerstrospath expandvars expanduser backup_rootmkdir metadata_file lock_file store_dir load_metadata)selfrr3c:\PYTHON\Scheduler\modules\backup\backup_engine.py__init__s      zTimeMachineBackup.__init__cCs|jr5z t|jddd}t||_Wdn1swYWnty4iid|_Ynwiid|_d|jvrGi|jd<dSdS)z&Carica i metadati dei backup esistentirutf-8encodingN)backups file_hashes path_cache)rexistsopenjsonloadmetadata Exception)rfrrrr s    zTimeMachineBackup.load_metadatarc Cszltjdkr WdSt|jpt|}ddl}ddlm}|jjj }|j |j |j | |j | |j | |j |j |j g|_| }| }| }|d} ||dd||||||| d} | rj| jphdWSWdStyvYdSw)zKRitorna il tipo di filesystem (es. 'NTFS', 'exFAT') su Windows oppure None.ntNr)wintypes)r nameranchorr ctypesr*windllkernel32GetVolumeInformationWLPCWSTRLPWSTRDWORDPOINTERargtypescreate_unicode_bufferbyrefvaluer') rrrootr.r*r1ZserZmclflagsZ fsname_bufokrrr_windows_fs_type.s8        (  z"TimeMachineBackup._windows_fs_typecCsLztjdkr||}|r|dk|fWSWdSWdSty%YdSw)znProva a capire se il FS supporta hard links. Ritorna (supports: bool|None, fsname: str|None). r)ZNTFSNN)r r,r=upperr')rrfsnamerrrfs_supports_hardlinksMs   z'TimeMachineBackup.fs_supports_hardlinks source_pathc cszddl}hd}tjt|dddD]\}}}g}t|D]G} t|| } | r1|| q| |vr;|| q|rftt|| | dd} | } |D]} || | s^|| | re|| nqPq|D]} z| | Wqit y{Yqiw|D]G}t||}z6|rWq~|rt|| dd}|j } d}|D]} ||| s|| | rd}nq|rWq~|VWq~t yYq~wqWdSt yYdSw) afItera i file di una sorgente con pruning veloce delle directory inutili. - Usa os.walk(topdown=True) per poter modificare `dirs` in-place ed evitare di scendere in alberi enormi come .git, node_modules, venv, ecc. - Applica i pattern di exclude sia alle directory (per il pruning) sia ai file. - Non segue symlink. rN> __pycache__tmpZTempz.venvz.gitZ node_modulesz.cachevenvTF)topdown followlinks\/)fnmatchr walkr listr is_symlinkappend relative_toreplaceremove ValueErrorr,r')rrBexcludeZ_fnmZprune_dirnamesr:dirsfilesZpruneddZdpathZrel_dirr,patr(fpathZrel_fileskiprrriter_source_files\sl          /z#TimeMachineBackup.iter_source_files relative_pathcCsjz*tjdkr WdShd}|jD]}|ddd}||vr'|WSqWdSty4YdSw)aRitorna il segmento riservato di Windows trovato nel percorso relativo, altrimenti None. Su Windows non sono permessi nomi di dispositivo come: CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9. Sono vietati anche con estensione (es. NUL.txt). r)N>PRNNULZLPT5ZCOM4ZCOM3ZCOM5ZCOM7ZLPT1ZLPT7ZLPT6ZLPT3ZCOM6ZCOM2ZCOM9ZCOM8ZLPT4ZLPT2ZLPT9AUXZCOM1ZLPT8CON.r)r r,partssplitstripr?r')rr[reservedpartbaserrr_windows_reserved_segments    z+TimeMachineBackup._windows_reserved_segmentcCsd}zVtjddt|jd\}}tj|ddd}tj|j|dd d Wdn1s-wYt ||j W|rUtj |rWzt |WdStyTYdSwdSdS|rrtj |rszt |WwtyqYwwww) zSalva i metadatiNZ metadata_z.json)prefixsuffixdirwrrF)indent ensure_ascii)tempfilemkstempr rr fdopenr$dumpr&rPrrr"rQOSError)rrDfdr(rrr save_metadatas(  zTimeMachineBackup.save_metadatac Cs|d}z|dd|dd}|dd}|dd}|dd}|dd}|dd} |d g} d} d} |d D]$} | rc| jdkrcz | | j7} | d 7} Wq?ttfybYq?wq?d }||krmd }n|dkrsd}|dkr}|d|nd}t|ddd<}| d| d| d| d| d| d|jd| d|d| d| d| d| d|dd| d|dd |d!| d"|dd d|d!| d#|dd| d| d$| d| d%| d&d'| d(d(d)d*| d+|d&d'|d(d(d)d*| dkrF|dkrF|| krFd|d| }| d,|d-d.| d| d| d/| dt | d D]\}}| |d0|dq_| d||kr| d| d1| d| d2| d3| d4| d5| d6| d7| d8| dWd9Wd9SWd9Wd9S1swYWd9St y}zt d:|WYd9}~d9Sd9}~ww);z5Crea un file di log leggibile per il backup corrente.zBACKUP_INFO.txt files_linkedr files_copiedfiles_in_store total_size timestamp source_dirs*rau✓ OK - Hard links funzionantiuJ⚠ ATTENZIONE - Troppi file copiati (filesystem non supporta hard links?)u3⚠ PARZIALE - Alcuni file copiati invece di linkeddrlrrzG====================================================================== z RIEPILOGO BACKUP zH====================================================================== z Data e ora:  z Nome backup: zStato: z zG---------------------------------------------------------------------- zSTATISTICHE FILE z File totali nella snapshot: z>10u" • Hard links (risparmiati): z (z%) u" • Copie fisiche: z Nuovi file nel content store: zOCCUPAZIONE SPAZIO z Dimensione apparente snapshot: z>15,z bytes (iz>8.1fz MB) z Nuovi dati nel content store: z Risparmio spazio (hard links): z>14.1fz% zDIRECTORY SORGENTI z. u⚠ AVVISO IMPORTANTE u2Questo backup contiene più COPIE che HARD LINKS! u,Stai occupando PIÙ SPAZIO del necessario. zCausa probabile: u9 • Filesystem non supporta hard links (FAT32, exFAT) z Soluzione: u; • Formatta il disco come NTFS (Windows) o ext4 (Linux) u3 • Esegui un nuovo backup dopo la formattazione Nz Impossibile creare file di log: )getrglobis_filer,statst_sizertPermissionErrorr#write enumerater'rwarning)r backup_path backup_infolog_file total_filesryrwrxrzr{sourcesZ apparent_sizeZ file_countitemstatusZhardlink_percentager(Zsavingsisrcerrrcreate_backup_log_files                $$                  $(1z(TimeMachineBackup.create_backup_log_filec sxt}z+t|dtfdddD]}||qWdn1s&wY|WSttfy;YdSw)z!Calcola l'hash SHA-256 di un filerbcs dS)Ni)readrr(rrs z7TimeMachineBackup.calculate_file_hash..N)hashlibZsha256r#iterupdate hexdigestrtr)r file_pathZhasherchunkrrrcalculate_file_hashs   z%TimeMachineBackup.calculate_file_hash file_hashreturncCs0|dd|dd}}|jd|||S)z,Percorso nel content store per un dato hash.Nrmr )r)rrabrrrhash_to_store_path!sz$TimeMachineBackup.hash_to_store_pathcCsz rHrIr!mtime_nssizehash)rrrFZobj_)rirkryrrarzrwwinerrorivZhardlink_failedz"Hard link fallito (usando copia): zIl volume di backup u è 'z7' e non supporta hard links: verranno effettuate copie.rxr hashesrr)(rMrrrOrrrhdebugr rPr,rJrr&r st_mtime_nsrr setdefaultrr"rprqr closerrrrQrtrrgetattrrArrr-r'rNerror)rrrrrrSr[rZbadZrel_strr,rWstZabs_pathcachedrrZ store_pathZ newly_storedZtmp_fdZtmp_pathrZwinerrZsupportsr@Zvolrrrr1s  $               z!TimeMachineBackup.process_file_v2c s@|jdi}|stddSddt|fddd}g}|dur9tt|d fd d |D}n#|durV|d krCd }t||krUd d |d| D}ntddS|D];}|j |}z| rot || |dtd|Wq^ty}ztd|d|WYd}~q^d}~ww|dS)u+Rimuove vecchi backup per conteggio o età.rzNessun backup da rimuovereNcSs,z t|dWStytjYSw)Nr{)rrrr'min)rrrrparse_dts   z1TimeMachineBackup.prune_backups..parse_dtcs |dS)Nrar)Zkv)rrrrs z1TimeMachineBackup.prune_backups..)key)dayscs g|] \}}|kr|qSrr).0r,rcutoffrrr s z3TimeMachineBackup.prune_backups..rcSsg|]\}}|qSrr)rr,rrrrrszSpecifica --keep o --older-thanzRimosso backup zImpossibile rimuovere r)r&rrsortedrrrrlenrr"rrmtreepopr'rv) rkeepolder_than_daysrorderedZ to_deleter,rrrrr prune_backupss<      zTimeMachineBackup.prune_backupsc Cs|jdi}|stddS|D] }d|vr tddSqt}|D] }||dgq(d}|jrjt |jD](\}}}|D] }||vrht ||} z | |d7}WqHt ygYqHwqHqAtd|d dS) zGRimuove oggetti nello store non referenziati dai backup (se possibile).rz&Nessun backup presente; niente da fareNrzIGC saltata: alcuni backup non hanno elenco 'hashes' (versione precedente)rrazGC store: rimossi z oggetti non referenziati) r&rrvaluessetrrr"r rKrrr') rrrusedremovedr:rTrUfnamerXrrrgc_stores6      zTimeMachineBackup.gc_store)NNNTrr>)__name__ __module__ __qualname__rrrr=rArZrhrvrrr rrrrrrrrrrrrrrrrrs, >V  y(    q #r)__doc__r rrr$rrpathlibrrplogururrJrrrrrs