o
    +kes                     @   s.  d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZ d dl	m
Z
 ddlmZ e ZedZddlmZmZ dd	lmZmZmZ dd
lmZ ddlmZ ddlmZ ddlmZmZ ddlmZmZmZmZ ddlmZ ddlm Z  ddlm!Z! ddlm"Z" ddlm#Z#m$Z$ ddlm%Z%m&Z& ddlm'Z' ddlm(Z( ddl)m*Z*m+Z+ ddl,m-Z- ddl.m/Z/m0Z0m1Z1 ddl2m3Z3 ddl4m5Z5 ddl6m7Z7 ddl8m9Z9 eddZ:G d d! d!Z;d"d# Z<d$d% Z=d6d&d'Z>d(d) Z?d*d+ Z@G d,d- d-ZAG d.d/ d/ZBG d0d1 d1ZCG d2d3 d3eCZDG d4d5 d5eCZEdS )7    N)	unhexlify)
namedtuple)perf_counter   )create_loggerzborg.debug.files_cache)CACHE_READMEFILES_CACHE_MODE_DISABLED)
ChunkIndexChunkIndexEntryCacheSynchronizer)Location)Error)Manifest)get_cache_dirget_security_dir)int_to_bigintbigint_to_int
bin_to_hexparse_stringified_list)format_file_size)safe_ns)yes)remove_surrogates)ProgressIndicatorPercentProgressIndicatorMessage)set_ecEXIT_WARNING)safe_unlink)msgpack)ArchiveItemChunkListEntry)PlaintextKey)IntegrityCheckedFileDetachedIntegrityCheckedFileFileIntegrityError)Lock)SaveFile)cache_if_remote)LIST_SCAN_LIMITFileCacheEntryzage inode size cmtime chunk_idsc                   @   s   e Zd ZdZdd ZedddZdd Zd	d
 Zdd Z	dddZ
dddZdddZddddddZdddZdd ZdS )SecurityManageras  
    Tracks repositories. Ensures that nothing bad happens (repository swaps,
    replay attacks, unknown repositories etc.).

    This is complicated by the Cache being initially used for this, while
    only some commands actually use the Cache, which meant that other commands
    did not perform these checks.

    Further complications were created by the Cache being a cache, so it
    could be legitimately deleted, which is annoying because Borg didn't
    recognize repositories after that.

    Therefore a second location, the security database (see get_security_dir),
    was introduced which stores this information. However, this means that
    the code has to deal with a cache existing but no security DB entry,
    or inconsistencies between the security DB and the cache which have to
    be reconciled, and also with no cache existing but a security DB entry.
    c                 C   sV   || _ t|j| _t|| _tj| jd| _tj| jd| _	tj| jd| _
d S )Nzkey-typelocationzmanifest-timestamp)
repositoryr   id_strdir	cache_dirospathjoinkey_type_filelocation_filemanifest_ts_file)selfr,    r7   .usr/lib/python3.10/site-packages/borg/cache.py__init__?   s   
zSecurityManager.__init__Nc                 C   s,   |pt | j}tj|rt| dS dS )z:destroy the security dir for ``repository`` or at ``path``N)r   r-   r0   r1   existsshutilrmtreer,   r1   r7   r7   r8   destroyG   s   zSecurityManager.destroyc                 C   s   t dd | j| j| jfD S )Nc                 s   s    | ]	}t j|V  qd S N)r0   r1   r:   ).0fr7   r7   r8   	<genexpr>O   s    z(SecurityManager.known.<locals>.<genexpr>)allr3   r4   r5   r6   r7   r7   r8   knownN   s   zSecurityManager.knownc              
   C   s   |   sdS z$t| j}| }|t|jkW  d    W S 1 s#w   Y  W d S  tyC } ztd| W Y d }~d S d }~ww )NFz&Could not read/parse key type file: %s)	rE   openr3   readstrTYPEOSErrorloggerwarning)r6   keyfdtypeexcr7   r7   r8   key_matchesR   s   (zSecurityManager.key_matchesc                 C   s   t d| jj| j | jj }t d| t dt|j t d|j	 t
| j}|| W d    n1 s;w   Y  t
| j}|t|j W d    n1 sXw   Y  t
| j}||j	 W d    d S 1 stw   Y  d S )Nz#security: saving state for %s to %szsecurity: current location   %szsecurity: key type           %szsecurity: manifest timestamp %s)rK   debugr,   r-   r.   	_locationcanonical_pathrH   rI   	timestampr&   r4   writer3   r5   )r6   manifestrM   Zcurrent_locationrN   r7   r7   r8   save\   s   "zSecurityManager.savec              
   C   s\  z!t | j}| }W d    n1 sw   Y  td| W n+ ty3   td| j d }Y n tyL } ztd| d }W Y d }~nd }~ww |r`|jr`||jkr`|j}td| | j	j
 }|r||krd||d }t|ddd	d
dst td t| j}|| W d    n1 sw   Y  |r|  d S d S d S d S )Nz#security: read previous location %rz-security: previous location file %s not found)Could not read previous location file: %sz.security: using previous_location of cache: %rzDWarning: The repository at location {} was previously located at {}
zDo you want to continue? [yN] 	Aborting.Invalid answer, aborting.FZ BORG_RELOCATED_REPO_ACCESS_IS_OKZ	false_msgZinvalid_msgretryZenv_var_overridez<security: updating location stored in cache and security dir)rF   r4   rG   rK   rR   FileNotFoundErrorrJ   rL   previous_locationr,   rS   rT   formatr   CacheRepositoryAccessAbortedr&   rV   rX   )r6   cache_configrN   r_   rP   Zrepository_locationmsgr7   r7   r8   assert_location_matchesi   sF   

z'SecurityManager.assert_location_matchesc              
   C   s   z!t | j}| }W d    n1 sw   Y  td| W n+ ty3   td| j d}Y n tyL } ztd| d}W Y d }~nd }~ww |rWt||j	pUd}td| |rq||j	krst
|trmt t d S d S )Nz$security: read manifest timestamp %rz.security: manifest timestamp file %s not found rY   z4security: determined newest manifest timestamp as %s)rF   r5   rG   rK   rR   r^   rJ   rL   maxrU   
isinstancer!   ra   RepositoryIDNotUniqueRepositoryReplay)r6   rW   rM   rc   rN   rU   rP   r7   r7   r8   assert_no_manifest_replay   s*   

z)SecurityManager.assert_no_manifest_replayc                 C   sH   |r|j d ur|j t|jkrt |  r | |s"t d S d S r?   )key_typerH   rI   ra   EncryptionMethodMismatchrE   rQ   )r6   rM   rc   r7   r7   r8   assert_key_type   s
   zSecurityManager.assert_key_typeT)rc   warn_if_unencrypted	lock_waitc                C   s   |  ||| |r| ||| n+t| j|d}| r6| | ||| W d    n1 s0w   Y  n| || td d S )Nrp   z/security: repository checks ok, allowing access)assert_access_unknown_assert_secureCacheConfigr,   r:   rK   rR   )r6   rW   rM   rc   ro   rp   r7   r7   r8   assert_secure   s   zSecurityManager.assert_securec                 C   sJ   |  | | || | ||| |  s#td | || d S d S )Nz3security: remembering previously unknown repository)re   rn   rk   rE   rK   rR   rX   )r6   rW   rM   rc   r7   r7   r8   rs      s   

zSecurityManager._assert_securec                 C   sh   |j s0|  s2d}| pt|ddddd}|r,|rtd ntd | || d S t d S d S )	NziWarning: Attempting to access a previously unknown unencrypted repository!
Do you want to continue? [yN] rZ   r[   FZ*BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OKr\   zIsecurity: remembering unknown unencrypted repository (explicitly allowed)z-security: initializing unencrypted repository)Zlogically_encryptedrE   r   rK   rR   rX   ra   CacheInitAbortedError)r6   ro   rW   rM   rd   Zallow_accessr7   r7   r8   rr      s   
z%SecurityManager.assert_access_unknownr?   )__name__
__module____qualname____doc__r9   staticmethodr>   rE   rQ   rX   re   rk   rn   ru   rs   rr   r7   r7   r7   r8   r*   +   s    


 

r*   c                 C   s   t | }|j||j|d d S )Nrq   )r*   ru   rM   )r,   rW   rp   Zsmr7   r7   r8   ru      s   ru   c                 C   s   |j  }t|}t| }|j|jkrG|j|jkrG|j|jkrG|j|jkrG|jrG|jrG|jdrG|jdrG|jdd  |jdd  krG|S | S )Nz/~/z/./   )	rS   rT   r   protouserhostportr1   
startswith)Zcache_locationr,   Zrepo_locationZrlZclr7   r7   r8    recanonicalize_relative_location   s   
0

r   c                 C   s   |p
t jt | jS r?   )r0   r1   r2   r   r-   r=   r7   r7   r8   r/         r/   c                  C   s   t jdd} | rd|  S dS )NZBORG_FILES_CACHE_SUFFIXrf   files.files)r0   environgetsuffixr7   r7   r8   files_cache_name   s   r   c                 C   s   dd t | D d S )Nc                 S   s"   g | ]}|d ks| dr|qS )r   r   )r   r@   fnr7   r7   r8   
<listcomp>   s   " z-discover_files_cache_name.<locals>.<listcomp>r   )r0   listdir)r1   r7   r7   r8   discover_files_cache_name   s   r   c                   @   s`   e Zd ZdddZdd Zdd Zdd	 Zd
d Zdd Zdd Z	dddZ
dd Zdd ZdS )rt   Nc                 C   s4   || _ t||| _tj| jd| _d | _|| _d S )Nconfig)r,   r/   r1   r0   r2   config_pathlockrp   )r6   r,   r1   rp   r7   r7   r8   r9      s
   
zCacheConfig.__init__c                 C   s   |    | S r?   )rF   rD   r7   r7   r8   	__enter__   s   zCacheConfig.__enter__c                 C      |    d S r?   closer6   exc_typeZexc_valZexc_tbr7   r7   r8   __exit__      zCacheConfig.__exit__c                 C   s   t j| jS r?   )r0   r1   r:   r   rD   r7   r7   r8   r:      s   zCacheConfig.existsc                 C   s   |   rJ tjd d}|d |ddd |dd| jj |ddd |d |ddd t| j}|	| W d    d S 1 sJw   Y  d S )	Ninterpolationcacheversion1r,   rW   rf   	integrity)
r:   configparserConfigParseradd_sectionsetr,   r-   r&   r   rV   )r6   r   rN   r7   r7   r8   create  s   

"zCacheConfig.createc                 C   s.   t tj| jdd| jd | _|   d S )Nr   T)	exclusivetimeout)r%   r0   r1   r2   rp   acquirer   loadrD   r7   r7   r8   rF     s   "zCacheConfig.openc                 C   s  t jd d| _t| j}| j| W d    n1 sw   Y  | | j | jdd| _t	| jdd| _
| jjddd d| _| jjddd d| _tt| jjddd	d| _tt| jjdd
d	d| _z t| jd| _| jdd| jdkri | _td W n t jy   td i | _Y nw | jjddd d}|rt|| j| _nd | _| jdd| jj  d S )Nr   r   r,   rW   rU   )fallbackrl   ignored_featuresrf   mandatory_featuresr   zHCache integrity data not available: old Borg version modified the cache.zTCache integrity: No integrity data found (files, chunks). Cache is from old version.r_   )r   r   _configrF   r   	read_file_check_upgrader   idr   manifest_idrU   rl   r   r   r   r   dictitemsr   poprK   rL   NoSectionErrorrR   r   r,   r_   rS   rT   )r6   rN   r_   r7   r7   r8   r     s4   


zCacheConfig.loadc                 C   s   |rS| j dd|j | j dd|j | j ddd| j | j ddd| j | j ds8| j d | j	
 D ]\}}| j d|| q=| j dd|j |r`| j ddt|j t| j}| j | W d    d S 1 sww   Y  d S )	Nr   rW   rU   r   ,r   r   rl   )r   r   r-   rU   r2   r   r   has_sectionr   r   r   rH   rI   r&   r   rV   )r6   rW   rM   fileintegrity_datarN   r7   r7   r8   rX   3  s   "zCacheConfig.savec                 C   s"   | j d ur| j   d | _ d S d S r?   )r   releaserD   r7   r7   r8   r   C  s   


zCacheConfig.closec                 C   sb   z| j dd}d}||kr|   td|||f W d S  tjy0   |   td| d w )Nr   r   r   z0%s has unexpected cache version %d (wanted: %d).z#%s does not look like a Borg cache.)r   getintr   	Exceptionr   r   )r6   r   Zcache_versionZwanted_versionr7   r7   r8   r   H  s   zCacheConfig._check_upgrade)NN)rw   rx   ry   r9   r   r   r:   r   rF   r   rX   r   r   r7   r7   r7   r8   rt      s    

 rt   c                	   @   s   e Zd ZdZG dd deZG dd deZG dd deZG dd	 d	eZG d
d deZ	e
dddZe
dddZddddddeddf	ddZdS )ra   zClient Side cache
    c                   @      e Zd ZdZdS )zCache.RepositoryIDNotUniquez`Cache is newer than repository - do you have multiple, independently updated repos with same ID?Nrw   rx   ry   rz   r7   r7   r7   r8   ri   X      ri   c                   @   r   )zCache.RepositoryReplayzCache, or information obtained from the security directory is newer than repository - this is either an attack or unsafe (multiple repos with same ID)Nr   r7   r7   r7   r8   rj   [  r   rj   c                   @   r   )zCache.CacheInitAbortedErrorzCache initialization abortedNr   r7   r7   r7   r8   rv   ^  r   rv   c                   @   r   )zCache.RepositoryAccessAbortedzRepository access abortedNr   r7   r7   r7   r8   rb   a  r   rb   c                   @   r   )zCache.EncryptionMethodMismatchzLRepository encryption method changed since last access, refusing to continueNr   r7   r7   r7   r8   rm   d  r   rm   Nc                 C   s(   t | |}ttj|ddd  d S )Nr   T)r   )r/   r%   r0   r1   r2   
break_lockr=   r7   r7   r8   r   g  s   
zCache.break_lockc                 C   sL   |p
t jt | j}t j|d}t j|r$t | t| dS dS )z3destroy the cache for ``repository`` or at ``path``r   N)	r0   r1   r2   r   r-   r:   remover;   r<   )r,   r1   r   r7   r7   r8   r>   l  s   
zCache.destroyTFc                    s    	
fdd}fdd}|	s | S t }| rL| |jjk}W d    n1 s=w   Y  |rLtd | S td | S )Nc                      s   t 	
 dS )N)r,   rM   rW   r1   syncro   progressiecrp   
cache_modeconsider_part_files)
LocalCacher7   r   r   r   rM   rp   rW   r1   r   r,   r   ro   r7   r8   localy  s   zCache.__new__.<locals>.localc                      s   t  dS )N)r,   rM   rW   rp   r   r   )
AdHocCacher7   )r   r   rM   rp   rW   r,   r7   r8   adhoc~  s   zCache.__new__.<locals>.adhocz%Cache: choosing local cache (in sync)zKCache: choosing ad-hoc cache (local cache does not exist or is not in sync))rt   r:   r   r   rK   rR   )clsr,   rM   rW   r1   r   ro   r   rp   Zpermit_adhoc_cacher   r   r   r   r   rc   Zcache_in_syncr7   r   r8   __new__u  s    

zCache.__new__r?   )rw   rx   ry   rz   r   ri   rj   rv   rb   rm   r{   r   r>   r   r   r7   r7   r7   r8   ra   U  s    ra   c                   @   sH   e Zd ZdZdddZdd Zedg dZd	d
 Zdd Z	dd Z
dS )CacheStatsMixinzAll archives:   {0.total_size:>20s} {0.total_csize:>20s} {0.unique_csize:>20s}

                       Unique chunks         Total chunks
Chunk index:    {0.total_unique_chunks:20d} {0.total_chunks:20d}Fc                 C   s   || _ i | _d S r?   )r   
pre12_meta)r6   r   r7   r7   r8   r9     s   
zCacheStatsMixin.__init__c                 C   s   | j |  S r?   )
str_formatr`   format_tuplerD   r7   r7   r8   __str__  s   zCacheStatsMixin.__str__Summary)
total_sizetotal_csizeunique_sizeunique_csizetotal_unique_chunkstotal_chunksc                 C   s   ddl m} | j \}}}}}}d\}}| jjD ] }|| j| j| j|| jd}	|	j	| dd}
||
j
7 }||
j7 }q| |||||| }
|
S )Nr   )Archive)r   r   )r   F)Zwant_unique)archiver   chunksZ	summarizerW   archivesr,   rM   r   Z
calc_statsZosizecsizer   _asdict)r6   r   r   r   r   r   r   r   archive_namer   statsr7   r7   r8   r     s    
zCacheStatsMixin.statsc                 C   s8   |   }dD ]}t|| | jd||< q| jdi |S )N)r   r   r   r   r7   )r   r   r   r   )r6   r   fieldr7   r7   r8   r     s   zCacheStatsMixin.format_tuplec                 C   s   |   d S )Nr   )r   rD   r7   r7   r8   chunks_stored_size  r   z"CacheStatsMixin.chunks_stored_sizeN)F)rw   rx   ry   r   r9   r   r   r   r   r   r   r7   r7   r7   r8   r     s    
r   c                   @   s   e Zd ZdZdddddeddfddZdd Zd	d
 Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zdd  Zd!d" Zd/d#d$Zd0d%d&Zd1d'd(Zd2d)d*Zd+d, Zd-d. ZdS )3r   z0
    Persistent, local (client-side) cache.
    NTFc                 C   s  t j| |d || _|| _|| _|| _|	| _|
| _d| _d| _	t
||| _t|| _t| j| j|| _tj| jsF| j||| |   |   z1| jj||| jd |  s]|   |   |rv| jj| jjkry|   |   W dS W dS W dS    |    )aO  
        :param warn_if_unencrypted: print warning if accessing unknown unencrypted repository
        :param lock_wait: timeout for lock acquisition (int [s] or None [wait forever])
        :param sync: do :meth:`.sync`
        :param cache_mode: what shall be compared in the file stat infos vs. cached stat infos comparison
        r   NF)rc   )r   r9   r,   rM   rW   r   r   r   rU   
txn_activer/   r1   r*   security_managerrt   rc   r0   r:   rr   r   rF   ru   check_cache_compatibility
wipe_cacheupdate_compatibilityr   r   r   commitr   )r6   r,   rM   rW   r1   r   ro   r   rp   r   r   r   r7   r7   r8   r9     s8   	
zLocalCache.__init__c                 C      | S r?   r7   rD   r7   r7   r8   r        zLocalCache.__enter__c                 C   r   r?   r   r   r7   r7   r8   r     r   zLocalCache.__exit__c                 C   s  t | j tt j| jdd}|t W d   n1 s!w   Y  | j  t	 t j| jd t t j| jd t
t j| jt dd W d   n1 sZw   Y  t
t j| jdd	d}tj| j|d
d W d   dS 1 sw   Y  dS )z0Create a new empty cache at `self.path`
        READMEwNr   chunks.archive.dTbinary
pre12-metaF   indent)r0   makedirsr1   rF   r2   rV   r   rc   r   r	   r&   r   jsondumpr   r6   rN   r7   r7   r8   r     s   
"zLocalCache.createc              	   C   s   | j   ttj| jdd| j jdd}t	|| _
W d    n1 s(w   Y  d| jv r6d | _n|   z%ttj| jd}t|| _W d    W d S 1 sXw   Y  W d S  ttjfyl   Y d S w )Nr   Fr1   rV   r   dr   )rc   r   r"   r0   r1   r2   r   r   r	   rG   r   r   r   _read_filesrF   r   r   r^   JSONDecodeErrorr   r7   r7   r8   _do_open  s"   

&zLocalCache._do_openc                 C   s2   t j| jstd| j | j  |   d S )Nz"%s Does not look like a Borg cache)r0   r1   isdirr   rc   rF   rollbackrD   r7   r7   r8   rF     s   
zLocalCache.openc                 C   sj   t tj| jdd}tj| j|dd W d    n1 sw   Y  | jd ur3| j  d | _d S d S )Nr   r   r   r   )	rF   r0   r1   r2   r   r   r   rc   r   r   r7   r7   r8   r      s   


zLocalCache.closec           
      C   s  i | _ d | _td td d }zrttj| jt	 d| j
jt	 dT}tjdd}	 |d}|s7n=|| z|D ]\}}t| }t|j|jd d	| j |< q?W n ttfyr } zd
t| }W Y d }~nd }~ww q/W d    n1 s~w   Y  W n/ ty } zdt| }W Y d }~nd }~w ty }	 zdt|	 }W Y d }	~	nd }	~	ww |d urt| td i | _ tdt| j  d S )NzReading files cache ...zFILES-CACHE-LOAD: starting...Fr   T)Zuse_listi   r   )agez#The files cache seems invalid. [%s]z#The files cache can't be read. [%s]z"The files cache is corrupted. [%s]z:Continuing without files cache - expect lower performance.z.FILES-CACHE-LOAD: finished, %d entries loaded.)r   _newest_cmtimerK   rR   files_cache_loggerr"   r0   r1   r2   r   rc   r   r   r   ZUnpackerrG   feedr)   packb_replacer  	TypeError
ValueErrorrH   rJ   r$   rL   len)
r6   rd   rN   udata	path_hashitementryrP   fier7   r7   r8   r   (  sT   



 

zLocalCache._read_filesc                 C   s  t dd}tj| jd}t| |d ttj| jd| |d ttj| jd| |d zttj| jt | W n% t	ym   t
tj|t d	d
 W d    n1 sfw   Y  Y nw ttj| jdtj| jd d	| _|  d S )Nzcache.begin_transactionmsgidtxn.tmpz.Initializing cache transaction: Reading configr   z.Initializing cache transaction: Reading chunksr   z-Initializing cache transaction: Reading filesTr   
txn.active)r   r0   r1   r2   mkdiroutputr;   copyr   r^   r&   renamer   finish)r6   pitxn_dirr7   r7   r8   	begin_txnI  s*   




zLocalCache.begin_txnc                 C   s  | j sdS | j| j| j tdd}| jdur| jdu r d| _tt	j
dd}|d td tt	j| jt d	d
>}d}| j D ].\}}tt| }|jdkr`t|j| jk sj|jdkrv|j|k rvt||f| |d7 }qHW d   n1 sw   Y  td| td| j td| |j| jjt < |d tt	j| jdd	d
}| j| W d   n1 sw   Y  |j| jjd< |d | j| j| j t	 t	j| jdt	j| jd t!"t	j| jd d| _ |#  dS )zCommit transaction
        Nzcache.commitr  l    ZBORG_FILES_CACHE_TTL   zSaving files cachezFILES-CACHE-SAVE: starting...Tr1   rV   r   r   z>FILES-CACHE-KILL: removed all old entries with age >= TTL [%d]zCFILES-CACHE-KILL: removed all current entries with newest cmtime %dz7FILES-CACHE-SAVE: finished, %d remaining entries saved.zSaving chunks cacher   zSaving cache configr  r  F)$r   r   rX   rW   rM   r   r   r  intr0   r   r   r  r  rR   r"   r1   r2   r   r   r)   r   unpackbr  r   cmtimepackr   rc   r   r   rV   r  r;   r<   r  )r6   r  ZttlrN   Zentry_countr  r  r  r7   r7   r8   r   ]  sL   






zLocalCache.commitc                 C   s   t jt j| jdrtt j| jd t j| jd}t j|rntt j|d| j tt j|d| j tt j|t|| j t |t j| jd t jt j| jdrntt j| jd d| _	| 
  dS )z3Roll back partial and aborted transactions
        r  r  r   r   FN)r0   r1   r:   r2   r;   r<   r  r   r  r   r   )r6   r  r7   r7   r8   r     s   zLocalCache.rollbackc                    s`  t jjd t dddd
d	d fdd	 fddfdd	fd
dd fdd		
fddfddfddfddfdd	
fdd} fdd}jtjjf 	  t
jjd|  t j _|j_W d   dS 1 sw   Y  dS )!a  Re-synchronize chunks cache with repository.

        Maintains a directory with known backup archive indexes, so it only
        needs to fetch infos from repo and build a chunk index once per backup
        archive.
        If out of sync, missing archive indexes get added, outdated indexes
        get removed and a new master chunks index is built by merging all
        archive indexes.
        r   r   rf   c                    s   t | }tj || }|S r?   )r   r0   r1   r2   )r   r   Zid_hexr1   )archive_pathr7   r8   mkpath  s   zLocalCache.sync.<locals>.mkpathc                     s2   j rt } dd | D dd | D B S t S )Nc                 S   s    h | ]}t |d krt|qS )@   )r	  r   r   r7   r7   r8   	<setcomp>  s     z;LocalCache.sync.<locals>.cached_archives.<locals>.<setcomp>c                 S   s2   h | ]}t |d kr|drt|dd qS )H   .compactNr$  )r	  endswithr   r   r7   r7   r8   r%    s   2 )do_cacher0   r   r   )fnsr"  r6   r7   r8   cached_archives  s   
z(LocalCache.sync.<locals>.cached_archivesc                      s   dd  j j D S )Nc                 S   s   h | ]}|j qS r7   )r   )r@   infor7   r7   r8   r%    s    z9LocalCache.sync.<locals>.repo_archives.<locals>.<setcomp>)rW   r   listr7   rD   r7   r8   repo_archives  r   z&LocalCache.sync.<locals>.repo_archivesc                    s   | D ]} | qd S r?   r7   )idsr   )cleanup_cached_archiver7   r8   cleanup_outdated  s   
z)LocalCache.sync.<locals>.cleanup_outdatedTc                    s   zt  |  t  | d  W n	 ty   Y nw |s d S zt  | dd t  | ddd  W d S  tyA   Y d S w )Nz
.integrityr'  r   )r0   unlinkr^   )r   cleanup_compact)r#  r7   r8   r1    s   z/LocalCache.sync.<locals>.cleanup_cached_archivec                    s   |   }g }t r4|D ]&} |}|r-| | j|jd}|j|jks(J d|| |< q|| qn|}t|j	|D ] \}}| | jt|d}|| |< | |< d7 t|7 q?dS )z
            Archives created with AdHocCache will have csize=0 in all chunk list entries whose
            chunks were already in the repository.

            Scan *chunk_idx* for entries where csize=0 and fill in the correct information.
            )r   zChunk size mismatchr   N)
Zzero_csize_idsr	  r   r  r   sizeappendzipr,   get_many)	chunk_idxZall_missing_idsZ	fetch_idsid_Zalready_fetched_entryr  r  )chunks_fetched_size_indexdecrypted_repositoryfetched_bytes_for_csizefetched_chunks_for_csizer7   r8   fetch_missing_csize  s&   


	z,LocalCache.sync.<locals>.fetch_missing_csizec           
         s   | | \}}|| dt|| jj|dd\}}}t|d}|jdkr*tdt|}t	|j
||j
D ]\}	\}}||	dt|| t|7 d7 || q8jrf | | | d S d S )Nr   T)Zforce_tam_not_required)Zinternal_dictz Unknown archive metadata version)r   addr	  rM   Zunpack_and_verify_archiver   r   r   r   r7  r   r8  r  r)  )

archive_idr<  r9  r   r  r   Zverified_r   Zitem_id)r?  processed_item_metadata_bytesprocessed_item_metadata_chunksr6   write_archive_indexr7   r8   fetch_and_build_idx  s    

 z,LocalCache.sync.<locals>.fetch_and_build_idxc                    s    |  7  | dd}| dd}z"t|dt| d d}|| W d    n1 s.w   Y  W n tyB   t| Y d S w t|| d S )Nr'  r   z.tmpT)r1   rV   filename)compactr#   r   rV   r   r   r0   r  )rA  r9  r   Zfn_tmprN   )"compact_chunks_archive_saved_spacer#  r7   r8   rE    s   
z,LocalCache.sync.<locals>.write_archive_indexc                    s  | }t d| zPz)t|d dd}tj|dd}W d    n1 s&w   Y   | dd |W W S  tyX   t|dd}t|}W d    n1 sQw   Y  Y nw W n" ty| } zt d||  |  tt	 W Y d }~d S d }~ww t 
d	|  |  | | |S )
Nz-Reading cached archive chunk index for %s ...r'  Fr  T)Zpermit_compact)r4  z1Cached archive chunk index of %s is corrupted: %sz6Found non-compact index for %s, converting to compact.)rK   r-  r#   r	   rG   r^   r$   errorr   r   rR   )rA  r   Zarchive_chunk_idx_pathrN   archive_chunk_idxr  )r1  r#  rE  r7   r8   read_archive_index  s6   
z+LocalCache.sync.<locals>.read_archive_indexc                    sD   i } j j D ]}|j| v r|j||j< qt|t| ks J |S r?   )rW   r   r.  r   namer	  )archive_idsZarchive_namesr-  rD   r7   r8   get_archive_ids_to_names/  s   
z1LocalCache.sync.<locals>.get_archive_ids_to_namesc           	   	      s  t d   } }t dt|t|t|| t||  |   ||  tj}|rƈjs6d nt|d} tt|dddd}|}| D ]S\}}|j	t
|gd jr||v ro||}|d u ro|| ||vrt d	| t }|| t d
 | | qM| pt|d} t d| ||  qMjs|  |  t dt t dt	
 t dt t d | S )NzSynchronizing chunks cache...zIArchives: %d, w/ cached Idx: %d, w/ outdated Idx: %d, w/o cached Idx: %d.Zusableg?z3%3.0f%% Syncing chunks cache. Processing archive %sz
cache.sync)totalsteprd   r  )r-  z.Fetching and building archive index for %s ...z$Merging into master chunks index ...z!Fetching archive index for %s ...zlCache sync: had to fetch %s (%d chunks) because no archive had a csize set for them (due to --no-cache-sync)z0Cache sync: processed %s (%d chunks) of metadataz;Cache sync: compact chunks.archive.d storage saved %s byteszDone.)rK   r-  r	  clearr,   r)  r	   r   r   showr   r   merger  rR   r   )	r9  Z
cached_idsrN  Zmaster_index_capacityr  Zarchive_ids_to_namesrA  r   rK  )r,  r2  rI  r<  rF  r?  r=  r>  rO  rC  rD  rL  r/  r6   r7   r8   create_master_idx;  s^   






z*LocalCache.sync.<locals>.create_master_idxc                      sf   zt t jjd W n   Y zt t jjd W n   Y zt   W dS    Y dS )z?bring old cache dirs into the desired state (cleanup and adapt)zchunks.archivezchunks.archive.tmpN)r0   r3  r1   r2   r  r7   r+  r7   r8   legacy_cleanupn  s   z'LocalCache.sync.<locals>.legacy_cleanup)Zdecrypted_cacheN)rf   )T)r0   r1   r2   r	   rW   Zcheck_repository_compatibilityr   Z	OperationZREADr  r'   r,   rM   r   r)  r   )r6   rV  rW  r7   )r"  r,  r;  r1  r2  rI  r<  rF  r?  r=  r>  rO  r#  rC  rD  rL  r/  r6   rE  r8   r     s4   

!&3"zLocalCache.syncc                 C   s*   t j}| jj|@ rdS | jj|ksdS dS )NFT)r   SUPPORTED_REPO_FEATURESrc   r   r   )r6   my_featuresr7   r7   r8   r     s   z$LocalCache.check_cache_compatibilityc                 C   s   t d tj| jd}tj|r)ttj| jd ttj| jd t	 | _
ttj| jt dd W d    n1 sEw   Y  d| j_| jjddd t | j_t | j_d S )Nz9Discarding incompatible cache and forcing a cache rebuildr   Tr   rf   r   rW   )rK   rL   r0   r1   r2   r   r;   r<   r   r	   r   r&   r   rc   r   r   r   r   r   )r6   r"  r7   r7   r8   r     s   

zLocalCache.wipe_cachec                 C   sZ   | j  }tj}t }| D ]	\}}|| q| jj||  | jj	||@  d S r?   )
rW   Zget_all_mandatory_featuresr   rX  r   r   updaterc   r   r   )r6   Zoperation_to_features_maprY  Zrepo_featuresZ	operationfeaturesr7   r7   r8   r     s   
zLocalCache.update_compatibilityc           
      C   s   | j s|   t|}| ||}|r|s| ||S | j|}t|}	| jj|||d | j	
|d||	 |||	|  t|||	S )Nwaitr   )r   r  r	  
seen_chunkchunk_increfrM   encryptr,   putr   r@  rZ  r    
r6   r   chunkr   Z	overwriter]  r5  refcountr  r   r7   r7   r8   	add_chunk  s   zLocalCache.add_chunkc                 C   sJ   | j |tdd d \}}}|d ur#|d ur#||kr#td|||f |S )Nr   z@chunk has same id [%r], but different size (stored: %d new: %d)!)r   r   r
   r   )r6   r   r5  rd  Zstored_sizerB  r7   r7   r8   r^    s   zLocalCache.seen_chunkc                 C   s>   | j s|   | j|\}}}|j||d|d t|||S NFpart)r   r  r   increfrZ  r    r6   r   r   r5  rh  count_sizer   r7   r7   r8   r_    s
   zLocalCache.chunk_increfc                 C   t   | j s|   | j|\}}}|dkr-| j|= | jj||d |j| | d|d d S |j| | d|d d S Nr   r\  Trg  F)r   r  r   decrefr,   deleterZ  r6   r   r   r]  rh  rk  r5  r   r7   r7   r8   chunk_decref     zLocalCache.chunk_decrefc                 C   s.  t |jsdS | j}d|v rtd dS d|v r!td dS | j|}|s1td| dS tt	
| }d|v rJ|j|jkrJtd| d	S d
|v r\|j|jkr\td| d	S d|v rpt|j|jkrptd| d	S d|v rt|j|jkrtd| d	S t	|j|jdd| j|< d|jfS )aj  
        Check if we know the file that has this path_hash (know == it is in our files cache) and
        whether it is unchanged (the size/inode number/cmtime is same for stuff we check in this cache_mode).

        :param hashed_path: the file's path as we gave it to hash(hashed_path)
        :param path_hash: hash(hashed_path), to save some memory in the files cache
        :param st: the file's stat() result
        :return: known, ids (known is True if we have infos about this file in the cache,
                             ids is the list of chunk ids IF the file has not changed, otherwise None).
        FNr   zUNKNOWN: files cache disabledrzUNKNOWN: rechunking enforcedz*UNKNOWN: no file metadata in cache for: %rsz(KNOWN-CHANGED: file size has changed: %r)TNiz0KNOWN-CHANGED: file inode number has changed: %rcz)KNOWN-CHANGED: file ctime has changed: %rmz)KNOWN-CHANGED: file mtime has changed: %rr   )inoder  T)statS_ISREGst_moder   r  rR   r   r   r)   r   r  r5  st_sizerz  st_inor   r   st_ctime_nsst_mtime_nsr  r  	chunk_ids)r6   hashed_pathr  str   r  r7   r7   r8   file_known_and_unchanged  s8   

	
z#LocalCache.file_known_and_unchangedc           	      C   s   t |jsd S | j}d|v rtd d S d|v r"d}t|j}nd|v r.d}t|j}nd}t|j}t	d|j
|jt||d}t|| j|< t| jpOd|| _td	|jd
t|j d|| d S )Nr   z*FILES-CACHE-NOUPDATE: files cache disabledrx  ctimery  mtimer   )r  rz  r5  r   r  z)FILES-CACHE-UPDATE: put %r [has %s] <- %rz[%d entries])r  )r{  r|  r}  r   r  rR   r   r  r  r)   r  r~  r   r   r  r   rg   r  r  r	  r  )	r6   r  r  r  r0  r   Zcmtime_typeZ	cmtime_nsr  r7   r7   r8   memorize_file  s*   

zLocalCache.memorize_fileFTr?   NFTF)rw   rx   ry   rz   r   r9   r   r   r   r   rF   r   r   r  r   r   r   r   r   r   re  r^  r_  rr  r  r  r7   r7   r7   r8   r     s6    
,!) s



	
4r   c                   @   s   e Zd ZdZdZ		dddZdd	 Zd
d ZdZdZ	dd Z
dd Zd ddZd!ddZd"ddZd#ddZdd Zdd Zdd ZdS )$r   a  
    Ad-hoc, non-persistent cache.

    Compared to the standard LocalCache the AdHocCache does not maintain accurate reference count,
    nor does it provide a files cache (which would require persistence). Chunks that were not added
    during the current AdHocCache lifetime won't have correct size/csize set (0 bytes) and will
    have an infinite reference count (MAX_VALUE).
    zAll archives:                unknown              unknown              unknown

                       Unique chunks         Total chunks
Chunk index:    {0.total_unique_chunks:20d}             unknownTNFc                 C   s\   t j| |d || _|| _|| _|| _d| _t|| _| jj	|||d i | _
td d S )Nr   Frq   z1Note: --no-cache-sync is an experimental feature.)r   r9   r,   rM   rW   r   _txn_activer*   r   ru   r   rK   rL   )r6   r,   rM   rW   ro   rp   r   r   r7   r7   r8   r9   5  s   
zAdHocCache.__init__c                 C   r   r?   r7   rD   r7   r7   r8   r   F  r   zAdHocCache.__enter__c                 C      d S r?   r7   r   r7   r7   r8   r   I  r   zAdHocCache.__exit__r   c                 C   s   t d dS )Nz$UNKNOWN: files cache not implementedrt  )r  rR   )r6   r  r  r  r7   r7   r8   r  O  s   
z#AdHocCache.file_known_and_unchangedc                 C   r  r?   r7   )r6   r  r  r  r0  r7   r7   r8   r  S  r   zAdHocCache.memorize_filec           
      C   s   |rJ d| j s|   t|}| ||}|r!| j|||dS | j|}t|}	| jj|||d | j	
|d||	 |||	|  t|||	S )NuH   AdHocCache does not permit overwrites — trying to use it for recreate?r5  r\  r   )r  r  r	  r^  r_  rM   r`  r,   ra  r   r@  rZ  r    rb  r7   r7   r8   re  V  s   zAdHocCache.add_chunkc                 C   sL   | j s|   | j|tdd d }|jr#|r#|js#|j|d| j|< |jS )Nr   r  )r  r  r   r   r
   rd  r5  r  )r6   r   r5  r  r7   r7   r8   r^  e  s   zAdHocCache.seen_chunkc                 C   sN   | j s|   | j|\}}}|p|}|sJ |j||d|d t|||S rf  )r  r  r   ri  rZ  r    rj  r7   r7   r8   r_  p  s   zAdHocCache.chunk_increfc                 C   rm  rn  )r  r  r   ro  r,   rp  rZ  rq  r7   r7   r8   rr  {  rs  zAdHocCache.chunk_decrefc                 C   s&   | j sd S | j| j| j d| _ d S r  )r  r   rX   rW   rM   rD   r7   r7   r8   r     s   
zAdHocCache.commitc                 C   s   d| _ | `d S r  )r  r   rD   r7   r7   r8   r     s   zAdHocCache.rollbackc           
   	   C   s   d| _ t| j}t|d d| _t|ddd}t }d}d }	 | jjt|d}|d	7 }|s.n|j	t|d
 |d }t
tjddd}|D ]}|| j|< qDqt| j|ksVJ | j| jj= t | pbd}	|  td||	|t|d |	  d S )NTg?rP  z!Downloading chunk list... %3.0f%%zcache.download_chunks)rQ  rd   r  r   )limitmarkerr   )Zincrease)rd  r5  r   g{Gz?zBAdHocCache: downloaded %d chunk IDs in %.2f s (%d requests), ~%s/s"   )r  r	  r,   r	   r   r   r   r.  r(   rT  r
   Z	MAX_VALUErW   ZMANIFEST_IDr  rK   rR   r   )
r6   Z
num_chunksr  t0Znum_requestsr  resultZ
init_entryr:  Zdurationr7   r7   r8   r    s6   
zAdHocCache.begin_txn)TNFFr  r?   r  r  )rw   rx   ry   rz   r   r9   r   r   r   r   r  r  re  r^  r_  rr  r   r   r  r7   r7   r7   r8   r   %  s&    	




r   r?   )Fr   r   r0   r;   r{  binasciir   collectionsr   timer   rK   r   r  	constantsr   r   Z	hashindexr	   r
   r   helpersr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r   r    Z
crypto.keyr!   Zcrypto.file_integrityr"   r#   r$   Zlockingr%   platformr&   Zremoter'   r,   r(   r)   r*   ru   r   r/   r   r   rt   ra   r   r   r   r7   r7   r7   r8   <module>   sd    
 %
dA0    c