0)i}|dZddlZddlZddlZddlZddlmZmZddlmZddl Z ddl m Z ddl Z GddZ dS)z Backup Engine - TimeMachineBackup Incremental backup system with hard-link deduplication Extracted from BackupSystem for integration with Scheduler N)datetime timedelta)Path)loggerceZdZdZdZdefdZdefdZdefdZdefd Z d Z d Z d Z d e defdZdZdZddZdZdZddZddZddZdZddZdZdS) TimeMachineBackupct|trt|}tjtjt|}t||_|jdd|jdz |_ |jdz |_ |jdz |_ |j dd| dS)NTparentsexist_okzbackup_metadata.jsonz .backup.lockstore) isinstancerstrospath expandvars expanduser backup_rootmkdir metadata_file lock_file store_dir load_metadata)selfrs 3c:\PYTHON\Scheduler\modules\backup\backup_engine.py__init__zTimeMachineBackup.__init__s k4 ( ( +k**Kg((););C |jrd t|jdd5}tj||_dddn #1swxYwYn$#t $r iid|_YnwxYwiid|_d|jvr i|jd<dSdS)z&Carica i metadati dei backup esistentirutf-8encodingN)backups file_hashes path_cache)rexistsopenjsonloadmetadata Exception)rfs rrzTimeMachineBackup.load_metadata s   $ $ & & ? C$,cGDDD1$(IaLLDM111111111111111 C C C,.r B B  C)+2>>DM t} , ,*,DM, ' ' ' - ,s4A$A A$AA$A A$$A;:A;rc  tjdkrdSt|jpt |}ddl}ddlm}|jjj }|j |j |j | |j | |j | |j |j |j g|_| }| }| }|d} ||dd||||||| d} | r | jpdSn#t$$rYdSwxYwdS)zKRitorna il tipo di filesystem (es. 'NTFS', 'exFAT') su Windows oppure None.ntNr)wintypes)rnameranchorrctypesr/windllkernel32GetVolumeInformationWLPCWSTRLPWSTRDWORDPOINTERargtypescreate_unicode_bufferbyrefvaluer+) rrrootr3r/r6sermclflags fsname_bufoks r_windows_fs_typez"TimeMachineBackup._windows_fs_type.s w$t::$1D D MMM ' ' ' ' ' '$*M$:$P ! x~..x~..x~.. . ! *..""C..""CNN$$E55c::J&&tT1fll36G6GVYIZIZ\b\h\hin\o\oq{~ABBB 0!'/4/ 0   44 tsE*EE** E87E8c tjdkr3||}|r|dk|fSdSdS#t$rYdSwxYw)znProva a capire se il FS supporta hard links. Ritorna (supports: bool|None, fsname: str|None). r.NTFSNN)rr1rEupperr+)rrfsnames rfs_supports_hardlinksz'TimeMachineBackup.fs_supports_hardlinksMsq w$..t44>"LLNNf4f==#|<   << sAA AA source_pathc#ZK ddl}hd}tjt|ddD]\}}}g}t |D]} t || z } | r|| >| |vr|| X|rtt || z | dd} | } |D]E} || | s|| | r|| nF|D](} | | #t$rY%wxYw|D]}t ||z } |r*|rwt|| dd}|j } d}|D]2} ||| s|| | rd}n3|r|V#t$rYwxYwdS#t$rYdSwxYw) 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>.git.venv.cachetmpTempvenv __pycache__ node_modulesTF)topdown followlinks\/)fnmatchrwalkrlistr is_symlinkappend relative_toreplaceremove ValueErrorr1r+)rrLexclude_fnmprune_dirnamesr?dirsfilesprunedddpathrel_dirr1patr,fpathrel_fileskips riter_source_filesz#TimeMachineBackup.iter_source_files\s4  " " " "nnnN%'WS-=-=tY^%_%_%_. !. !!dEd&&A JJNE''))! a((( N** a((( &"%tDzzA~&B&B;&O&O"P"P"X"XY]_b"c"c #*&&C#||GS99&T\\$PS=T=T& & a 0 0 0 %& A A%!!A JJNE! ++--%$" )'*5+<+<[+I+I'J'J'R'RSWY\']']H#(:D#(D'.**#'<<##>#>!*$,,tUXBYBY!*+/D$)E!* $) (# $!!! !#!7. !. !^    FF ssD3H8E H EHEH5H H A8HHHH HHHH H*)H* relative_pathc tjdkrdShd}|jD]J}|ddd}||vr|cSKdS#t $rYdSwxYw)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>AUXCONNULPRNCOM1COM2COM3COM4COM5COM6COM7COM8COM9LPT1LPT2LPT3LPT4LPT5LPT6LPT7LPT8LPT9.r)rr1partssplitstriprIr+)rrqreservedpartbases r_windows_reserved_segmentz+TimeMachineBackup._windows_reserved_segments  w$tH &+  zz#q))!,2244::<<8##KKK$4   44 sA,AA,)A,, A:9A:c`d} tjddt|j\}}t j|dd5}t j|j|dd dddn #1swxYwYt j ||j |rGtj |r* t j |dS#t$rYdSwxYwdSdS#|rEtj |r' t j |w#t$rYwwxYwwwxYw) zSalva i metadatiN metadata_z.json)prefixsuffixdirwr r!F)indent ensure_ascii)tempfilemkstemprrrfdopenr(dumpr*r`rrr&raOSError)rrQfdr,s r save_metadatazTimeMachineBackup.save_metadatas &k'sSWScOdOdeeeGB2sW555 J $-15IIII J J J J J J J J J J J J J J J JsD. / / / rw~~c** IcNNNNNDD    s rw~~c** IcNNNND  sfAC#A1% C#1A55C#8A59C#8C CC#"D-DD- D(%D-'D((D-c` |dz } |dd|ddz}|dd}|dd}|dd}|dd}|dd} |d g} d} d} |d D][} | rE| jdkr: | | jz } | d z } D#t tf$rYWwxYw\d }||krd }n|dkrd}|dkr|dz|znd}t|dd5}| d| d| d| d| d| d|jd| d|d| d| d| d| d|dd| d|dd |d!| d"|dd d|z d!| d#|dd| d| d$| d| d%| d&d'| d(z d(z d)d*| d+|d&d'|d(z d(z d)d*| dkr1|dkr+|| kr%d|dz| z z }| d,|d-d.| d| d| d/| dt| d D] \}}| |d0|d!| d||kr| d| d1| d| d2| d3| d4| d5| d6| d7| d8| dd9d9d9d9S#1swxYwYd9S#t$r"}tj d:|Yd9}~d9Sd9}~wwxYw);z5Crea un file di log leggibile per il backup corrente.zBACKUP_INFO.txt files_linkedr files_copiedfiles_in_store total_size timestamp source_dirs*ru✓ OK - Hard links funzionantiuJ⚠ ATTENZIONE - Troppi file copiati (filesystem non supporta hard links?)u3⚠ PARZIALE - Alcuni file copiati invece di linkeddrr r!zG====================================================================== 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_filer1statst_sizerPermissionErrorr'write enumerater+rwarning)r backup_path backup_infolog_file total_filesrrrrrsources apparent_size file_countitemstatushardlink_percentager,savingsisrces rcreate_backup_log_filez(TimeMachineBackup.create_backup_log_files"!22P C%//.!<<{~_`?a?aaK(__-=qAAN&??>1==L&??>1==L$q99J# R88I!oomR88GMJ#))#..  <<>>di3D&D&D%)<< "a #_57Fl**e!!NJUWX<##5#D#D^_ hg666/ -!(((7888)***4y444555< (8<<<===.&...///(((,---(((N;NNNNOOOk\kkkRekkklllo\oooRUViRiooopppS>SSSSTTT(((.///(((}=}}}XefjXjkoXo}}}}~~~w:wwwU_`dUdeiUiwwwwxxx 1$$a!M11"%c)9M)I"J V7 V V V VWWW (((.///((('33--FAsGGqOOCOOO,,,, ,..GGO,,,GG6777GGO,,,GGQRRRGGLMMMGG0111GGYZZZGGN+++GGZ[[[GGRSSSGGO,,,_/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -/ -b C C C NAaAA B B B B B B B B B CsaC+T3!DTD)&T(D))9T"NS4' T4S88T;S8<T T- T((T-c,tj} t|d5tfddD]}|| dddn #1swxYwY|S#t tf$rYdSwxYw)z!Calcola l'hash SHA-256 di un filerbc.dS)Ni)read)r,srz7TimeMachineBackup.calculate_file_hash..s!&&*=*=rrN)hashlibsha256r'iterupdate hexdigestrr)r file_pathhasherchunkr,s @rcalculate_file_hashz%TimeMachineBackup.calculate_file_hashs!! i&& )!!"="="="=sCC))EMM%(((() ) ) ) ) ) ) ) ) ) ) ) ) ) ) )##%% %)   44 s4A>,A A>A##A>&A#'A>>BB file_hashreturncP|dd|dd}}|jdz |z |z |z S)z,Percorso nel content store per un dato hash.Nrr )r)rrabs rhash_to_store_pathz$TimeMachineBackup.hash_to_store_path!s8!}i!n1')A-1I==rc tj|jtjtjztjz}tj|dd5}|tj tj tj ddddn #1swxYwYdS#t$rtdYdSwxYw) z2Evita sovrapposizione backup (lock file semplice).rr r!)pidtimeNTz5Un altro backup sembra in esecuzione (lock presente).F)rr'rO_CREATO_EXCLO_WRONLYrrr(dumpsgetpidrnow isoformatFileExistsErrorprint)rrr,s r acquire_lockzTimeMachineBackup.acquire_lock&s bi)?"+)MNNB2sW555 ^ 29;; @X@X@Z@Z#[#[\\]]] ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^4    I J J J55 s7ACAC6 CCC C CC,+C,c |jr|jdSdS#t$rYdSwxYwN)rr&unlinkr)rs r release_lockzTimeMachineBackup.release_lock1sc ~$$&& (%%''''' ( (    DD s28 AANTc Z|)dtjd}|j|z }|dt jd|tjgddddd}d}|r|r|D]\} t| } | st j d | d >| | |D]} |d z }] |d |d n#t$rYnwxYwd} d} |D]} t| } | st j d | d ?t jd| || j z }|d| d z } d}|r. |dt| | dn#t$rYnwxYw| | |D]f}||||| || d z } |d z }|r? |d| |t|t| | |dV#t$rYbwxYwg|dt| |r1 |dt| | |dt#t$rYwxYw||jd|<|||||d|dz}t jd|d|dd|dd|dd |d|dkrzt j d t j d!t j d"t j d#t j d$t j d dSdS)%uCrea un nuovo backup. - Se `progress` è None (CLI senza barra), niente pre‑scansione. - Se `progress` è fornito: * con `progress_precount=True` (default, usato dalla GUI): conta rapida per inizializzare il totale. * con `progress_precount=False` (CLI): evita la pre‑scansione, mostrando comunque avanzamento incrementale. Nzbackup-z%Y-%m-%d-%H%M%STr zCreando backup: r)rrrrrrzAvviso: z non esisterstart)phasetotalz Backup di: source_start)rsource source_indexprogress)r processedrfilerrsource_processedr source_end)rrrrr#rrzBackup completato: z file totali ( hard links, z copie), rz nuovi file nel content storezF======================================================================u.⚠️ ATTENZIONE: Più copie che hard links!u:⚠️ Il backup sta OCCUPANDO PIÙ SPAZIO del necessariouI⚠️ Causa probabile: Filesystem non supporta hard links (FAT32/exFAT)uG⚠️ Soluzione: Formatta il disco come NTFS (Windows) o ext4 (Linux))rrstrftimerrrinforrr&rrpr+r1rprocess_file_v2r^r*rr)rr backup_namercrprogress_precountrrr source_dirrL_r source_idxdest_dirrrrs r create_backupzTimeMachineBackup.create_backup8s  PHLNN$;$; > > H!-"%k"2"2(2,<  !D 1< i -  ##K===".1K4OO   L+ L LN+ L L:En:U L L+, L L L    ~ &^)D D D N8 $ $ $ NK L L L NW X X X Nf g g g Nd e e e N8 $ $ $ $ $ E DsH5D DDF77 GG-H33 I?I/J JJcP ||}||z }|jdd||}|dS|j}||jdvrt|jd|} | rz|r|  tj t| t||dxxdz cc<dS#t$rYnwxYwtj|||dxxdz cc<|dxx|z cc<t||jd|<dS#t $r } t#d |d | Yd} ~ dSd} ~ wwxYw) z1Processa un singolo file (copia o crea hard link)Tr Nr$rrrrErrore processando : )r_parentrrrrr*rr&rrlinkrrshutilcopy2r+r) rrrr source_rootrq dest_filer file_size existing_filers r process_filezTimeMachineBackup.process_files$ :%11+>>M =0I   " "4$ " ? ? ?00;;I !((0IDM-888 $T]=%A)%L M M  ''))  ''))+!((***M 2 2C NNCCC#N333q8333" LI . . .  ' ' '1 , ' ' '  % % % 2 % % %7:)nnDM- ( 3 3 3 : : : 8 88Q88 9 9 9 9 9 9 9 9 9 :s>A E;BE;?D D$!E;#D$$AE;; F%F  F%c |jdstddStd|jdD]\}}tj|dd}td|d|d |vr|d d |d d z}td |d|d d d|d d dtd|d d td|d d d|d d d!dS)z Lista tutti i backup disponibilir#zNessun backup trovatoNzBackup disponibili:rz%Y-%m-%d %H:%M:%Sz z - rrrrz File totali: z (rz copie)z Nuovi nel content store: z File: z copiati, z collegamenti)r*ritemsr fromisoformatrr)rr1rdaters r list_backupszTimeMachineBackup.list_backupss}Y'  ) * * * F #$$$- 288:: E EJD$)${*;<<EEFYZZD &t&&&& ' ' ' 4''33dhh~q6Q6QQ>%>>488NA3N3N>>.!44>>>???Udhh?OQR6S6SUUVVVVD488NA#>#>DD.!44DDDEEEE E Ercx||jdvrtd|ddS|j|z }|std|ddS|t jdz }t |}|d td |d ||D]i}|r"||j z }tj ||d 8| rtj |||j z jtd |dS)zRipristina un backupr#zBackup z non trovatoNzCartella backup z non trovatarestored_backupTrzRipristinando backup z in  dirs_exist_okzRipristino completato in )r*rrr&rhomeriterdiris_dirr1r copytreerr )rr restore_pathrrrs rrestore_backupz TimeMachineBackup.restore_backupsg dmI6 6 6 5K555 6 6 6 F&4 !!##  >[>>> ? ? ? F  9;;)::LL)) D))) EkEE|EEFFF '')) = =D{{}} ='$)3hdCCCCC = T<$)#;<<< 8,8899999rct|j|z }||z }|std|d|dS|)tjt|jz }nt|}|jddtj ||td|dS)zRipristina un file specificozFile z non trovato nel backup NTr zFile ripristinato: ) rr&rrcwdr1rrr r )rrr restore_tor source_files r restore_filezTimeMachineBackup.restore_file s&4 !I- !!##  J)JJ[JJ K K K F  d9oo&::JJj))Jt<<< [*--- 0J0011111rc|j|z }||z }|r|std|d|dS|t j|jz }nt |}|jddtj ||dtd|dS)uRipristina una directory specifica dal backup. directory_path è relativo alla radice del backup (es: 'Documents' o 'Documents/Progetti').z Directory z non trovata nel backup NTr rzDirectory ripristinata in: ) rr&rrrr r1rrr r)rrdirectory_pathr!rrs rrestore_directoryz#TimeMachineBackup.restore_directorys&4  >1   "" **;*;*=*=  T~TT{TT U U U F  jo5JJj))Jt<<< JdCCCC 8J8899999rc  |rtjd|dS||}||z }|jdd||}|rtjd|d|ddStjd|d ||r\t| d d } |j } |D]/} tj | | stj | | rdS0| } t|} |j d i| }|rR|d | jkr4|d| jkr|d}nE||}|dS| j| j|d|j d i| <| j}||}d}|sN|jddt+jdt|j\}}t/j| t3j||t/j ||t.j|r& t/j|n[#t:$rYnOwxYwnJ#t.j|r& t/j|w#t:$rYwwxYwwxYwd}|dd|dxxdz cc<|dxx|z cc<|r| t/jt|t||dxxdz cc<n&#t:$r}tA|dd}|dkr|dsd|d<tjd||!|j"\}}|durn ttG|j"j$p|j"}n$#tJ$rt|j"}YnwxYwtjd|d|dt3j|||d xxdz cc<|s|dxx|z cc<Yd}~nd}~wwxYwt||j d!i|<|d"g||d"vr|d"&|dSdS#tJ$rZ} tj'd#|d |d$|n*#tJ$rtj'd#|d$|Yn wxYwYd}~dSYd}~dSd}~wwxYw)%z/Nuova versione: usa content store e cache path.zSalto symlink: NTr z,Salto elemento con nome riservato Windows: 'z' nel percorso ''z Analizzo: z -> rXrYr%mtime_nssizehash)r)r*r+Fobj_)rrrrrrrwinerrorivhardlink_failedz"Hard link fallito (usando copia): zIl volume di backup u è 'z7' e non supporta hard links: verranno effettuate copie.rr$hashesrr)(r]rrr_rrrdebugrr`r1rZrr*r st_mtime_nsrr setdefaultrr&rrrcloser r rrarrrgetattrrKrrr2r+r^error)rrrrr rcrqr badrel_strr1rlstabs_pathcachedrr  store_path newly_storedtmp_fdtmp_pathrwinerrsupportsrJvols rrz!TimeMachineBackup.process_file_v21s m E##%% <<<===%11+>>M =0I   " "4$ " ? ? ?00??C h3hhXehhh L@i@@Y@@ A A A m,,44T3?? ~"Cw44c8R8R!!B9~~H]&&|R88<>+//:K*L*L>59K 12N#K#K#KLLL'+'A'A$BR'S'S$Hf5((8"%d4+;&<&<&C&WtGW"X"XCC(888"%d&6"7"7CCC8(Hc(H(H(H(H(HIII Y 222N+++q0+++#; ---:---) ;.FI__DM $ $]B 7 7 B  " "8R 0 0 0 H 555H%,,Y7777765 E E E E R9RR)RRqRRSSSS E E E C9CCCCDDDDD ETSSSSSDDDDDD  Es+U6A(U6A5U6B=U6B:U6 *K:4U6K)(U6) K63U65K66U6: ML0/M0 L=:M<L==MA#U6%?O&$U6& T 1A)T-R T R*'T)R**AT?U6T  A)U66 WVW$WWWWWc |jdi}|stddSd t| fd}g}|4t jt|z fd|D}nD|1|d krd }t||krd |d| D}ntd dS|D]}|j |z } | rtj || |dtd |^#t$r}td |d|Yd}~d}~wwxYw|dS)u+Rimuove vecchi backup per conteggio o età.r#zNessun backup da rimuovereNc tj|dS#t$rtjcYSwxYw)Nr)rrrr+min)rs rparse_dtz1TimeMachineBackup.prune_backups..parse_dtsM $-dhh{.C.CDDD $ $ $|### $s&)AAc&|dS)Nr)kvrEs rrz1TimeMachineBackup.prune_backups..s"Q%r)key)daysc8g|]\}}|k|SrGrG).0r1rcutoffrEs r z3TimeMachineBackup.prune_backups..s/SSS*$88D>>F;R;R;R;R;Rrrcg|]\}}|SrGrG)rLr1rs rrNz3TimeMachineBackup.prune_backups..sAAAgdATAAArzSpecifica --keep o --older-thanzRimosso backup zImpossibile rimuovere r)r*rrsortedrrrrlenrr&r rmtreepopr+r) rkeepolder_than_daysr#ordered to_deleter1rrrMrEs @@r prune_backupszTimeMachineBackup.prune_backupss-##Ir22  . / / / F $ $ $ .H.H.H.HIII  &\^^i_&E&E&EEFSSSSSSSSII  axx7||d""AA4%AAA 3 4 4 4 F < " " !%'WT^%<%< ! !!dE"!!ED(( $T U 2!!LLNNN#qLGG(!!! D! )! E7EEEFFFFFs?D D&%D&)NNNTrrH)__name__ __module__ __qualname__rrrrErKrprrrrrrrrrrrrr#r&rrXr_rGrrrrs    - - -T>  $     <T<<<<|t.   TCTCTCl   >C>D>>>>   w%w%w%w%r&:&:&:PEEE,:::::2222$::::&oEoEoEb!!!!FGGGGGrr)__doc__rr rr(rrpathlibrrlogururrZrrGrrrfs   ((((((((K GK GK GK GK GK GK GK GK GK Gr