o
    +keC                    @   s  d dl Z d dlZ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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T dd	lmZ dd
lmZ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mZ ddlm Z  ddlm!Z! ddl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,m-Z-m.Z.m/Z/ ddl0m1Z1 ddl2m3Z3m4Z4 e(e5Z'dZ6e7e6Z8dZ9e7e9e8ksJ d Z:dZ;dZ<dZ=eee>Z?G dd dZ@G dd dZAeAjBjCd ksJ dS )!    N)hexlify	unhexlify)defaultdict)ConfigParser)partial)islice   )*)NSIndex)ErrorErrorWithTracebackIntegrityErrorformat_file_sizeparse_file_size)Location)ProgressIndicatorPercent)
bin_to_hex)secure_erasesafe_unlink)Manifest)msgpack)utcnow)Lock	LockError
LockErrorT)create_logger)LRUCache)SaveFileSyncFilesync_dirsafe_fadvise)crc32)IntegrityCheckedFileFileIntegrityErrors   BORG_SEGs   ATTICSEG      c                   @   sj  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	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G dd deZ			d}ddZdd Zdd  Zd!d" Zd#d$ Zed%d& Zed'd( Zd)d* Zd+d, Zd-d. Zd/d0 Zd1d2 Zd3d4 Zd5d6 Z d7d8 Z!d9d: Z"d;d< Z#d=d> Z$d?d@ Z%dAdB Z&d~dCdDZ'dEdF Z(ddHdIZ)dJdK Z*ddLdMZ+ddNdOZ,dPdQ Z-dRdS Z.dTdU Z/dVdW Z0dXdY Z1ddZd[Z2d\d] Z3dd_d`Z4ddadbZ5dcdd Z6dedf Z7dgdh Z8didj Z9ddkdlZ:ddmdnZ;dodp Z<ddqdrZ=ddsdtZ>ddudvZ?dwdx Z@ddydzZAd{d| ZBdS )
Repositorya  
    Filesystem based transactional key value store

    Transactionality is achieved by using a log (aka journal) to record changes. The log is a series of numbered files
    called segments. Each segment is a series of log entries. The segment number together with the offset of each
    entry relative to its segment start establishes an ordering of the log entries. This is the "definition" of
    time for the purposes of the log.

    Log entries are either PUT, DELETE or COMMIT.

    A COMMIT is always the final log entry in a segment and marks all data from the beginning of the log until the
    segment ending with the COMMIT as committed and consistent. The segment number of a segment ending with a COMMIT
    is called the transaction ID of that commit, and a segment ending with a COMMIT is called committed.

    When reading from a repository it is first checked whether the last segment is committed. If it is not, then
    all segments after the last committed segment are deleted; they contain log entries whose consistency is not
    established by a COMMIT.

    Note that the COMMIT can't establish consistency by itself, but only manages to do so with proper support from
    the platform (including the hardware). See platform.base.SyncFile for details.

    A PUT inserts a key-value pair. The value is stored in the log entry, hence the repository implements
    full data logging, meaning that all data is consistent, not just metadata (which is common in file systems).

    A DELETE marks a key as deleted.

    For a given key only the last entry regarding the key, which is called current (all other entries are called
    superseded), is relevant: If there is no entry or the last entry is a DELETE then the key does not exist.
    Otherwise the last PUT defines the value of the key.

    By superseding a PUT (with either another PUT or a DELETE) the log entry becomes obsolete. A segment containing
    such obsolete entries is called sparse, while a segment containing no such entries is called compact.

    Sparse segments can be compacted and thereby disk space freed. This destroys the transaction for which the
    superseded entries where current.

    On disk layout:

    dir/README
    dir/config
    dir/data/<X // SEGMENTS_PER_DIR>/<X>
    dir/index.X
    dir/hints.X

    File system interaction
    -----------------------

    LoggedIO generally tries to rely on common behaviours across transactional file systems.

    Segments that are deleted are truncated first, which avoids problems if the FS needs to
    allocate space to delete the dirent of the segment. This mostly affects CoW file systems,
    traditional journaling file systems have a fairly good grip on this problem.

    Note that deletion, i.e. unlink(2), is atomic on every file system that uses inode reference
    counts, which includes pretty much all of them. To remove a dirent the inodes refcount has
    to be decreased, but you can't decrease the refcount before removing the dirent nor can you
    decrease the refcount after removing the dirent. File systems solve this with a lock,
    and by ensuring it all stays within the same FS transaction.

    Truncation is generally not atomic in itself, and combining truncate(2) and unlink(2) is of
    course never guaranteed to be atomic. Truncation in a classic extent-based FS is done in
    roughly two phases, first the extents are removed then the inode is updated. (In practice
    this is of course way more complex).

    LoggedIO gracefully handles truncate/unlink splits as long as the truncate resulted in
    a zero length file. Zero length segments are considered to not exist, while LoggedIO.cleanup()
    will still get rid of them.
    c                   @      e Zd ZdZdS )zRepository.DoesNotExistzRepository {} does not exist.N__name__
__module____qualname____doc__ r-   r-   3usr/lib/python3.10/site-packages/borg/repository.pyDoesNotExist{       r/   c                   @   r'   )zRepository.AlreadyExistsz"A repository already exists at {}.Nr(   r-   r-   r-   r.   AlreadyExists~   r0   r1   c                   @   r'   )zRepository.PathAlreadyExistsz!There is already something at {}.Nr(   r-   r-   r-   r.   PathAlreadyExists   r0   r2   c                   @   r'   )z!Repository.ParentPathDoesNotExistz:The parent path of the repo directory [{}] does not exist.Nr(   r-   r-   r-   r.   ParentPathDoesNotExist   r0   r3   c                   @   r'   )zRepository.InvalidRepositoryz0{} is not a valid repository. Check repo config.Nr(   r-   r-   r-   r.   InvalidRepository   r0   r4   c                   @   r'   )z"Repository.InvalidRepositoryConfigz?{} does not have a valid configuration. Check repo config [{}].Nr(   r-   r-   r-   r.   InvalidRepositoryConfig   r0   r5   c                   @   r'   )zRepository.AtticRepositoryz8Attic repository detected. Please run "borg upgrade {}".Nr(   r-   r-   r-   r.   AtticRepository   r0   r6   c                   @   r'   )zRepository.CheckNeededz3Inconsistency detected. Please run "borg check {}".Nr(   r-   r-   r-   r.   CheckNeeded   r0   r7   c                       s    e Zd ZdZ fddZ  ZS )zRepository.ObjectNotFoundz.Object with key {} not found in repository {}.c                    s$   t |tr	t|}t || d S N)
isinstancebytesr   super__init__)selfidZrepo	__class__r-   r.   r<      s   
z"Repository.ObjectNotFound.__init__)r)   r*   r+   r,   r<   __classcell__r-   r-   r?   r.   ObjectNotFound   s    rB   c                   @   r'   )z%Repository.InsufficientFreeSpaceErrorzNInsufficient free space to complete transaction (required: {}, available: {}).Nr(   r-   r-   r-   r.   InsufficientFreeSpaceError   r0   rC   c                   @   r'   )zRepository.StorageQuotaExceededzJThe storage quota ({}) has been exceeded ({}). Try deleting some archives.Nr(   r-   r-   r-   r.   StorageQuotaExceeded   r0   rD   FNTc
           
      C   s   t j|| _td| j | _d | _d | _d | _i | _d| _	|| _
|| _|| _d| _|| _|| _|| _d| _d | _|| _|	| _d S )Nz	file://%sFr   )ospathabspathr   Z	_locationiolockindexshadow_index_active_txn	lock_waitdo_lock	do_createcreated	exclusiveappend_onlystorage_quotastorage_quota_usetransaction_doomedcheck_segment_magicmake_parent_dirs)
r=   rF   createrQ   rM   rI   rR   rS   rV   rW   r-   r-   r.   r<      s$   
zRepository.__init__c                 C   s   | j r|   J dd S )NFz&cleanup happened in Repository.__del__)rI   closer=   r-   r-   r.   __del__   s   zRepository.__del__c                 C   s   d| j j d| j dS )N< >)r@   r)   rF   rZ   r-   r-   r.   __repr__   s   zRepository.__repr__c                 C   s@   | j rd| _ | | j d| _| j| jt| j| j| jd | S )NFT)rM   rI   )	rO   rX   rF   rP   openboolrQ   rM   rN   rZ   r-   r-   r.   	__enter__   s   zRepository.__enter__c                 C   sR   |d ur#|t u o|jtjk}| jr|rtd d}nd}| j|d |   d S )NzGNo space left on device, cleaning up partial transaction to free space.TFcleanup)OSErrorerrnoENOSPCrL   loggerwarning	_rollbackrY   )r=   exc_typeZexc_valZexc_tbZno_space_left_on_devicerd   r-   r-   r.   __exit__   s   

zRepository.__exit__c                 C   
   t | jS r8   )r   r>   rZ   r-   r-   r.   id_str   s   
zRepository.id_strc                 C   sl   z+t tj| dd}|d}d|v pd|v W  d   W S 1 s$w   Y  W dS  ty5   Y dS w )z;Check whether there is already a Borg repository at *path*.READMErbd   s   Borg Backup repositorys   Borg repositoryNF)r`   rE   rF   joinreadre   )rF   fdZreadme_headr-   r-   r.   is_repository   s   
(zRepository.is_repositoryc                 C   s   zt |}W n	 ty   Y nw | |r| |t|jr&t |r+| |	 |}t j	
t j	|t j}||kr@dS | |rJ| |q,)a  
        Raise an exception if a repository already exists at *path* or any parent directory.

        Checking parent directories is done for two reasons:
        (1) It's just a weird thing to do, and usually not intended. A Borg using the "parent" repository
            may be confused, or we may accidentally put stuff into the "data/" or "data/<n>/" directories.
        (2) When implementing repository quotas (which we currently don't), it's important to prohibit
            folks from creating quota-free repositories. Since no one can create a repository within another
            repository, user's can only use the quota'd repository, when their --restrict-to-path points
            at the user's repository.
        TN)rE   statFileNotFoundErrorru   r1   S_ISDIRst_modelistdirr2   rF   rG   rr   pardir)r=   rF   stZprevious_pathr-   r-   r.   check_can_create_repository   s"   




z&Repository.check_can_create_repositoryc              
   C   s~  |  | | jrtj|tj}tj|dd tj|s7zt| W n t	y6 } z| 
||d}~ww ttj|dd}|t W d   n1 sQw   Y  ttj|d tdd}|d |dd	d
 |ddtt |ddtt |ddtt| j | jr|ddt| j n|ddd |ddd |ddttd | || dS )z0Create a new empty repository at `path`
        T)exist_okNro   wdatainterpolation
repositoryversion1segments_per_dirmax_segment_sizerR   rS   0additional_free_spacer>       )r}   rW   rE   rF   rr   r{   makedirsexistsmkdirrw   r3   r`   writeZREPOSITORY_READMEr   add_sectionsetstrZDEFAULT_SEGMENTS_PER_DIRZDEFAULT_MAX_SEGMENT_SIZEintrR   rS   r   urandomsave_config)r=   rF   parent_patherrrt   configr-   r-   r.   rX     s6   


zRepository.createc              
   C   sh  t j|d}t j|d}t j|rtd t|dd t j|rgd}zt || W n7 tyY } z|j	t	j
t	jt	jt	jt	jt	jfv rNt| n W Y d }~nd }~w tyf   t| Y nw zt|}|| W d    n1 s|w   Y  W n! ty } z| jr td|j|jf  W Y d }~nd }~ww t j|rt|dd d S d S )Nr   z
config.oldz=Old config file not securely erased on previous config updateT)Zavoid_collateral_damagezFailed to securely erase old repository config file (hardlinks not supported). Old repokey data, if any, might persist on physical storage.T%s: Failed writing to '%s'. This is expected when working on read-only repositories.)rE   rF   rr   isfilerh   ri   r   linkre   rf   ZEMLINKZENOSYSEPERMEACCESENOTSUPZEIOAttributeErrorr   r   PermissionErrorrN   strerrorfilename)r=   rF   r   Zconfig_pathZold_config_pathZlink_error_msgert   r-   r-   r.   r   .  sD   
"

zRepository.save_configc                 C   s8   | j sJ |d}| j dd| | | j| j  d S )Nutf-8r   key)r   decoder   r   rF   r=   Zkeydatar-   r-   r.   save_keyQ  s   

zRepository.save_keyc                 C   s    | j jdddd }|dS )Nr   r    fallbackr   )r   getstripencoder   r-   r-   r.   load_keyW  s   
zRepository.load_keyc                 C   s   | j r| j stdtj| jd}z#t|}tj	t
| ddW  d    W S 1 s0w   Y  W d S  tyA   Y d S w )N-bug in code, exclusive lock should exist herenoncebig	byteorder)rN   rI   got_exclusive_lockAssertionErrorrE   rF   rr   r`   r   
from_bytesr   rs   rw   )r=   
nonce_pathrt   r-   r-   r.   get_free_nonce\  s   
(zRepository.get_free_noncec              
   C   s   | j r| j std|  |krtdtj| jd}z't	|dd}|
t|jddd W d    W d S 1 s>w   Y  W d S  tyg } z| j rQ td	|j|jf  W Y d }~d S d }~ww )
Nr   z6nonce space reservation with mismatched previous stater   Fbinary   r   r   r   )rN   rI   r   r   r   	ExceptionrE   rF   rr   r   r   r   to_bytesr   rh   ri   r   r   )r=   Znext_unreservedZstart_noncer   rt   r   r-   r-   r.   commit_nonce_reservationg  s"   &
z#Repository.commit_nonce_reservationc                 C   sB   | j r
t| jd |   ttj| jd t| j dS )z.Destroy the repository at `self.path`
         is in append-only moder   N)	rR   
ValueErrorrF   rY   rE   removerr   shutilrmtreerZ   r-   r-   r.   destroyx  s
   zRepository.destroyc                    s.   t  fddt jD }|r|d S d S )Nc                 3   sV    | ]&}| d r(|dd  r(ttj j|jdkrt|dd V  qdS )index.   Nr   )
startswithisdigitrE   rv   rF   rr   st_sizer   ).0fnrZ   r-   r.   	<genexpr>  s    z6Repository.get_index_transaction_id.<locals>.<genexpr>)sortedrE   rz   rF   )r=   indicesr-   rZ   r.   get_index_transaction_id  s   
z#Repository.get_index_transaction_idc                 C   sl   |   }| j }|d ur|d u rd| j }| |||kr4|d ur*||kr*d }n|}| || d S d S )Nz,%s" - although likely this is "beyond repair)r   rH   get_segments_transaction_idrF   r7   replay_segments)r=   index_transaction_idsegments_transaction_idmsgZreplay_fromr-   r-   r.   check_transaction  s   


zRepository.check_transactionc                 C   s   |    |  S r8   )r   r   rZ   r-   r-   r.   get_transaction_id  s   zRepository.get_transaction_idc                 C   s   t tj| jd  d S )NrI   )r   rE   rF   rr   
break_lockrZ   r-   r-   r.   r     s   zRepository.break_lockc                 C   s    | j d ur| j || d S d S r8   )rI   migrate_lock)r=   Zold_idZnew_idr-   r-   r.   r     s   
zRepository.migrate_lockc           	      C   sD  || _ zt|}W n ty   | |w t|js"| ||r4ttj 	|d||d
 | _nd | _td d| _z"ttj 	| j d}| j| W d    n1 sYw   Y  W n typ   |   | | j w d| j vr|   | |d| jdd}|dkr|   | |d	| t| jdd
| _| jtkr|   | |dt | jdd| _t| jjdddd| _| jp| jjdddd| _| jd u rt| jjdddd| _t| jdd | _t | j | j| j| _!| j"r| j!# }|d ur| j!$|t%kr |   | &|d S d S d S )NrI   )timeoutr   r   r   zno repository section foundr   r   z;repository version %d is not supported by this borg versionr   zmax_segment_size >= %dr   r   r   r   rR   FrS   r>   )'rF   rE   rv   rw   r/   rx   ry   r4   r   rr   acquirerI   r   r   r`   	read_filerY   sectionsr5   getintr   r   r   ZMAX_SEGMENT_SIZE_LIMITr   r   rR   
getbooleanrS   r   r   r>   LoggedIOrH   rV   get_latest_segmentget_segment_magicATTIC_MAGICr6   )	r=   rF   rQ   rM   rI   r|   rt   Zrepo_versionsegmentr-   r-   r.   r`     sb   

 



zRepository.openc                 C   s4   | j r| jr| j  d | _| j   d | _ d S d S r8   )rI   rH   rY   releaserZ   r-   r-   r.   rY     s   


zRepository.close皙?c                 C   s   | j r| j }|   ||   |   | j }| j|d | j|  t	j
j7  < |rU| jsU|rP| j D ]\}}tj|dkrOd| j|< t	j
j| j|< q7| | |   |   dS )zCommit transaction
        r      N)rU   rollbackcheck_free_spacelog_storage_quotarH   write_commitsegments
setdefaultcompactr   
header_fmtsizerR   segment_iteratorrE   rF   getsizecompact_segmentswrite_index)r=   
save_spacer   	thresholdZcleanup_commits	exceptionr   r   r-   r-   r.   commit  s&   



zRepository.commitc                 C   s   d| }t j| j|}zt|d}t|}W d    n1 s"w   Y  W n
 ty2   Y d S w |ddkrFt	d|d| d S || 
 S )Nintegrity.%drp      versionr$   z'Unknown integrity data version %r in %s)rE   rF   rr   r`   r   unpackrw   r   rh   ri   r   )r=   transaction_idr   integrity_fileZintegrity_pathrt   	integrityr-   r-   r.   _read_integrity  s   zRepository._read_integrityc              
   C   s   |d u rt  S tj| jd| }| |d}z t|d|d}t |W  d    W S 1 s0w   Y  W d S  ttt	fyn } z(t
d| t| |sP | |   | jdd | |  W  Y d }~S d }~ww )Nindex.%d   indexFr   integrity_datazARepository index missing or corrupted, trying to recover from: %s)r   )r
   rE   rF   rr   r   r"   rs   r   re   r#   rh   ri   unlinkprepare_txnr   r   
open_index)r=   r   auto_recover
index_pathr  rt   excr-   r-   r.   r  
  s$   (
zRepository.open_indexc              
   C   s  d| _ | jr)| j s)| jd urtdz| j  W n ttfy(   d| _  w | j	r0|d u rbz
| j
|dd| _	W n' tttfya } ztd| |   | j
|dd| _	W Y d }~nd }~ww |d u rwi | _t | _d| _| j  d S |r| j| tj| jd| }tj| jd| }| |d	}zt|d|d
}t|}W d    n1 sw   Y  W n5 tj t!tfy }	 z%td|	 t"|	t!st#| t#| |   | $| W Y d }	~	d S d }	~	ww |d dkr$t%d| |d | _t | _d| _i | _t&|d D ]}
t%d|
 | '|
 qt%d n)|d dkr3td|d  |d | _t|d | _|(dd| _|(di | _| )  | j* D ]\}}t+|D ]}
|
|krj|,|
 q^qVd S )NTr   F)r  z9Checking repository transaction due to previous error: %sr   hints.%dr      hintsr  zARepository hints file missing or corrupted, trying to recover: %sr   r   zUpgrading from v1 hints.%d   segments   compactz%Rebuilding sparse info for segment %dzUpgrade to v2 hints completer$   zUnknown hints file version: %d   storage_quota_use   shadow_index)-rL   rN   rI   r   rQ   r   upgrader   r   rJ   r  r   re   r#   rh   ri   r   r   	FreeSpacer   rT   rK   clearrH   rd   rE   rF   rr   r   r"   r   r   ZUnpackExceptionrw   r9   r  r  debugr   _rebuild_sparser   r   itemslistr   )r=   r   
do_cleanupr	  Z
hints_pathr  r  rt   hintsr   r   r   Zshadowed_segmentsr-   r-   r.   r    s   




	



zRepository.prepare_txnc                 C   sP  dd }dd }d| j | j| j| jd}ddi}| j }|d us#J | jrOttj	
| j	dd	}td
|t tf |d W d    n1 sJw   Y  d| }tj	
| j	|}t|d |dd}	t||	 ||	 W d    n1 syw   Y  |	j|d< d| }
tj	
| j	|
}t|d |
dd}	| j|	 ||	 W d    n1 sw   Y  |	j|d< d| }tj	
| j	|}t|d d}	t||	 ||	 W d    n1 sw   Y  || t| j	 || || t| j	 d| }t| j	D ]}|dsq||rqttj	
| j	| qd | _d S )Nc                 S   s   |    t|   d S r8   )flushrE   fsyncfilenort   r-   r-   r.   flush_and_syncf  s   z.Repository.write_index.<locals>.flush_and_syncc                 S   s   t | d |  d S )N.tmp)rE   replacefiler-   r-   r.   
rename_tmpj  s   z*Repository.write_index.<locals>.rename_tmpr$   )r   r  r  r  r  r   Ztransactionsaztransaction %d, UTC time %sr   r
  r  T)r   r   r  r   r  r   wbz.%d)r   zhints.z
integrity.)r   r   rT   rK   rH   r   rR   r`   rE   rF   rr   printr   strftimeZ
ISO_FORMATr"   r   packr  rJ   r   r   rz   r   endswithr  )r=   r  r"  r  r   r   logZ
hints_nameZ
hints_filert   Z
index_nameZ
index_fileZintegrity_namer   currentnamer-   r-   r.   r   e  sj   









zRepository.write_indexc              
   C   s  | j  d }t| jd t| jd  d }||7 }|| j7 }| jsi| jt }t| jdk red}| j	 D ]\}}z|| j
|| 7 }W q6 tyO   Y q6w td t||}td| ||7 }n||7 }z	t| jj}W n ty } ztdt|  W Y d}~dS d}~ww td	| d
|  ||k r| jrtd |   n| jdd t|}	t|}
| |	|
dS )zJPre-commit check for sufficient free space to actually perform the commit.   
   i   r   zAcheck_free_space: few segments, not requiring a full free segmentzBcheck_free_space: calculated working space for compact as %d bytesz.Failed to check free space before committing: Nz!check_free_space: required bytes z, free bytes z@Not enough free space to initialize repository at this location.Trc   )rJ   r   lenr   r   r   rR   r   MAX_OBJECT_SIZEr  rH   segment_sizerw   rh   r  minr   
disk_usagerF   freere   ri   r   rP   errorr   rj   r   rC   )r=   Zrequired_free_spaceZ
hints_sizeZfull_segment_sizeZcompact_working_spacer   r3  Z
free_spaceZos_errorZformatted_requiredZformatted_freer-   r-   r.   r     sH   
 






zRepository.check_free_spacec                 C   s(   | j rtdt| jt| j  d S d S )Nz!Storage quota: %s out of %s used.)rS   rh   infor   rT   rZ   r-   r-   r.   r     s
   zRepository.log_storage_quotac                    s  j s
td dS j} }j}g dfdd	}td|d  ttj dd	d
d}tj 	 D ]r\ }j
 sRtd  j  = |  q8j
 }d| | }	|	|ksqtd |	d | |  q8| d td |  |	d | j
j ddD ]	\}
}}}|
tkrqj|}| |fk}|
tkr|rzj
j||dd\}}W n tjy   |  j
||\}}Y nw ||fj|< ||d ||  d	7  < |   d	8  < q|
tkr|sz
j|   W n ttfy
   Y nw  jt|j
jj 8  _q|
tkr|s|jv}|p7t fddj| D }|du p@ |k}|sG|rzzj
j |dd\}}W n tjyi   |  j
 |\}}Y nw j |  |7  < ||d qtdt!| ||||j| j| sj|= q|  dksJ d"  |  q8|#  |dd j
$  j}t%dt&||  td dS )zBCompact sparse segments by copying data into new segments
        znothing to do: compact emptyNTc                    s    j j| d} j|d  j|  tjj7  < t	d| r dnd| D ] }t	d|  j
|}|dks<J d j |  j|= q&g d S )Nintermediater   z+complete_xfer: wrote %scommit at segment %dzintermediate r   z)complete_xfer: deleting unused segment %d<Corrupted segment reference count - corrupted index or hints)rH   r   r   r   r   r   r   r   rh   r  popdelete_segment)r7  r   count)r=   unusedr-   r.   complete_xfer  s   
z2Repository.compact_segments.<locals>.complete_xferz'Compaction started (threshold is %i%%).rq   zCompacting segments %3.0f%%r   zrepository.compact_segmentstotalr   stepmsgidz3segment %d not found, but listed in compaction datag      ?z>not compacting segment %d (maybe freeable: %2.2f%% [%d bytes])g      Y@r   zNcompacting segment %d with usage count %d (maybe freeable: %2.2f%% [%d bytes]))include_data)
raise_fullc                 3   s    | ]}| k V  qd S r8   r-   )r   Zshadowedr   r-   r.   r   :  s    
z.Repository.compact_segments.<locals>.<genexpr>zIdropping DEL for id %s - seg %d, iti %r, knisi %r, spe %r, dins %r, si %rr8  Fr6  z+compaction freed about %s repository space.zcompaction completed.T)'r   rh   r  rT   r   r   r   r.  r   r  rH   segment_existsri   showr0  r   iter_objects
TAG_COMMITrJ   r   TAG_PUT	write_putr   SegmentFullrK   r   KeyErrorr   put_header_fmtr   
TAG_DELETEanywrite_deleter   appendfinishclear_empty_dirsr5  r   )r=   r   Zquota_use_beforer   r   r=  piZfreeable_spacer0  Zfreeable_ratiotagr   offsetr   Zin_indexZis_index_objectZnew_segmentZkey_not_in_shadow_indexZshadowed_put_existsZdelete_is_not_stabler   Zquota_use_afterr-   )r   r=   r<  r.   r     s   


"





zRepository.compact_segmentsc           
      C   s   | j }d | _ | j|dd zStdd | j D }t|ddd}t| j D ]&\}\}}|| |d ur<||kr<q(||krB n| j|}	| 	||	 q(|
  |   W || _ |   d S || _ |   w )NF)r  c                 s       | ]}d V  qdS r   Nr-   r   _r-   r-   r.   r         z-Repository.replay_segments.<locals>.<genexpr>zReplaying segments %3.0f%%zrepository.replay_segments)r?  r   rA  )rQ   r  sumrH   r   r   	enumeraterG  rH  _update_indexrS  r   r   )
r=   r   r   Zremember_exclusivesegment_countrU  ir   r   objectsr-   r-   r.   r   z  s,   


zRepository.replay_segmentsc              	   C   s  d| j |< |D ]\}}}}|tkrXz%| j| \}}	| j|  |7  < | j |  d8  < | j|g | W n	 ty?   Y nw ||f| j|< | j |  d7  < |  j|7  _q|t	krz
| j
|\}}W n	 tyo   Y qw | j|r| j |  d8  < | jj|||dd}| j|  |7  < | j|g | q|tkrqd| d| }
|du r| |
||
 q| j | dkr| j|| j|< dS dS )z2some code shared between replay_segments and checkr   r   F	read_datazUnexpected tag z in segment N)r   rJ  rJ   r   rK   r   rR  rM  rT   rO  r9  rH   rF  rs   rI  r7   r0  )r=   r   rb  reportrV  r   rW  r   sr[  r   r-   r-   r.   r_    sF   


zRepository._update_indexc                 C   s   z| j |}W n ty   | j| Y dS w | j| dkr'|| j|< dS d| j|< | j j|ddD ],\}}}}|tkrS| j	|d||fkrR| j|  |7  < q4|t
kr`| j|  |7  < q4dS )zNRebuild sparse bytes count for a single segment relative to the current index.Nr   Frc  )r   r   )rH   r0  rw   r   r9  r   rH  rJ  rJ   r   rO  )r=   r   r0  rV  r   rW  r   r-   r-   r.   r    s&   

zRepository._rebuild_sparser   c                    s  | j r|rt| jd d  fdd}td | jrJ z|  }| |}td| W n t	yN } z| j
 }d}td| W Y d}~nd}~ww |du r\td	 |  }|du rjtd
 | j
 }|du rt|d dS |r|| j
| | j
 }td| td| | d tdd | j
 D }	td|	 t|}
|r|
rJ |
rdnd}|
r| jjdddd}td|d  nd}| jdd | | j| j t }t|	dddd}d}t| j
 D ]\}\}}|| ||krq||krqtd| z
t| j
|}W n- tyA } z |t| g }|r7| j
 || t| j
|}W Y d}~nd}~ww |
sL| !||| |
rrt || krrtd| | j"ddt| | | j| j  nqtd| | jdd | | j| j |#  |r|du r|d |  |d | j
_$| j
%  |
s,td! |r%|s%t&|t&| j'kr|d" t(d#t&| t(d$t&| j' ntd% d&}d'}| j') D ]\}}|*||}||krt+|t,||| q|) D ]#\}}|| j'v rq| j'*||}||kr#t+|t,||| q|r,| -  | .   rD|r=td(| nt(d)| ntd*|   pO|S )+zCheck repository consistency

        This method verifies all segment checksums and makes sure
        the index is consistent with the data stored in the segments.
        r   Fc                    s   d t |  d S NT)rh   r4  )r   Zerror_foundr-   r.   report_error  s   z&Repository.check.<locals>.report_errorzStarting repository checkz&Read committed index of transaction %dNz#Failed to read committed index (%s)zNo segments transaction foundz1No index transaction found, trying latest segmentz'This repository contains no valid data.zSegment transaction is    %szDetermined transaction is %sc                 s   rX  rY  r-   rZ  r-   r-   r.   r     r\  z#Repository.check.<locals>.<genexpr>zFound %d segmentsr   fullr   last_segment_checkedr   r   zskipping to segments >= %dr   zChecking segments %3.1f%%r   zrepository.checkr>  zchecking segment file %s...z:finished partial segment check, last segment checked is %dz$finished segment check at segment %dzAdding commit tag to segment zStarting repository index checkzIndex object count mismatch.zcommitted index: %d objectszrebuilt index:   %d objectszIndex object count match.z5ID: %-64s rebuilt index: %-16s committed index: %-16sz<not found>z8Finished %s repository check, errors found and repaired.z+Finished %s repository check, errors found.z0Finished %s repository check, no problems found.)/rR   r   rF   rh   r5  rL   r   r  r  r   rH   r   r   r   rd   r  r]  r   ra   r   r   remove_optionr   time	monotonicr   r^  rG  r  rH  r   r   recover_segmentr_  r   rS  r   r   r.  rJ   r4  	iteritemsr   ri   r   r   r   )r=   Zrepairr   Zmax_durationri  r   Zcurrent_indexr	  r   r`  r   moderk  Zt_startrU  r   ra  r   rb  r   Zline_formatZ	not_foundr   valuecurrent_valuer-   rh  r.   check  s   
















zRepository.checkc           
      c   s    | j j|dD ]S\}}|dur||kr dS z&| j j||pdddD ]\}}}}|dur3||kr3 n	|||||fV  q#W q ty[ }	 ztd||t|	f  W Y d}	~	qd}	~	ww dS )a	  Very low level scan over all segment file entries.

        It does NOT care about what's committed and what not.
        It does NOT care whether an object might be deleted or superseded later.
        It just yields anything it finds in the segment files.

        This is intended as a last-resort way to get access to all repo contents of damaged repos,
        when there is uncommitted, but valuable data in there...

        When segment or segment+offset is given, limit processing to this location only.
        rD  Nr   T)r   rW  rB  z6Segment %d (%s) has IntegrityError(s) [%s] - skipping.)rH   r   rH  r   rh   r4  r   )
r=   r   rW  Zcurrent_segmentr   rV  r   current_offsetr   r   r-   r-   r.   scan_low_levelR  s&   
zRepository.scan_low_levelc                C   s,   |r| j | j   d| _d| _d| _dS )z	
        NF)rH   rd   r   rJ   rL   rU   )r=   rd   r-   r-   r.   rj   k  s
   
zRepository._rollbackc                 C   s   | j dd d S )NFrc   )rj   rZ   r-   r-   r.   r   t  s   zRepository.rollbackc                 C   s    | j s| |  | _ t| j S r8   )rJ   r  r   r.  rZ   r-   r-   r.   __len__x     
zRepository.__len__c                 C   s    | j s| |  | _ || j v S r8   )rJ   r  r   )r=   r>   r-   r-   r.   __contains__}  rx  zRepository.__contains__c                 C   s4   | j s| |  | _ dd t| j j|d|D S )zd
        list <limit> IDs starting from after id <marker> - in index (pseudo-random) order.
        c                 S   s   g | ]\}}|qS r-   r-   )r   id_r[  r-   r-   r.   
<listcomp>  s    z#Repository.list.<locals>.<listcomp>)marker)rJ   r  r   r   rp  )r=   limitr|  r-   r-   r.   r    s   zRepository.listc              
   C   s   |dur|dk rt d| js|  }| || _|du }|r!dn| j| \}}g }| j|D ]J\}}	| jj||ddd}
	 z
t|
\}}}}W n tt	fyT   Y n&w |dkr\d}q>|t
kry||f| j|kry|| t||kry|  S q?q0|S )	a  
        list <limit> IDs starting from after id <marker> - in on-disk order, so that a client
        fetching data in this order does linear reads and reuses stuff from disk cache.

        We rely on repository.check() has run already (either now or some time before) and that:

        - if we are called from a borg check command, self.index is a valid, fresh, in-sync repo index.
        - if we are called from elsewhere, either self.index or the on-disk index is valid and in-sync.
        - the repository segments are valid (no CRC errors).
          if we encounter CRC errors in segment entry headers, rest of segment is skipped.
        Nr   z$please use limit > 0 or limit = None)r   r   F)rd  rB  Tr   )r   rJ   r   r  rH   r   rH  nextStopIterationr   rJ  r   rR  r.  )r=   r}  r|  r   Zat_startZstart_segmentZstart_offsetresultr   r   Zobj_iteratorrV  r>   rW  r   r-   r-   r.   scan  s4   
zRepository.scanc                 C   sV   | j s| |  | _ z| j | \}}| j|||W S  ty*   | || jd w r8   )rJ   r  r   rH   rs   rM  rB   rF   )r=   r>   r   rW  r-   r-   r.   r     s   zRepository.getc                 c   s    |D ]}|  |V  qd S r8   )r   )r=   idsZis_preloadedrz  r-   r-   r.   get_many  s   zRepository.get_manyc                 C   s   | j s
| |   z	| j| \}}W n	 ty   Y nw | ||| | j||\}}|  jt	|| jj
j 7  _| j|d | j|  d7  < ||f| j|< | jrk| j| jkrm| t| jt| j| _| jdS dS )zput a repo object

        Note: when doing calls with wait=False this gets async and caller must
              deal with async results / exceptions later.
        r   r   N)rL   r  r   rJ   rM  _deleterH   rK  rT   r.  rN  r   r   r   rS   rD   r   rU   )r=   r>   r   waitr   rW  r-   r-   r.   put  s&   zRepository.putc                 C   sZ   | j s
| |   z
| j|\}}W n ty#   | || jdw | ||| dS )zdelete a repo object

        Note: when doing calls with wait=False this gets async and caller must
              deal with async results / exceptions later.
        N)	rL   r  r   rJ   r9  rM  rB   rF   r  )r=   r>   r  r   rW  r-   r-   r.   delete  s   zRepository.deletec                 C   s   | j |g | | j|  d8  < | jj|||dd}| j|  |7  < | j|\}}| j|  |7  < | j|d d S )Nr   Frc  r   )rK   r   rR  r   rH   rs   r   rQ  )r=   r>   r   rW  r   r-   r-   r.   r    s   zRepository._deletec                 C      dS )aC  Get one async result (only applies to remote repositories).

        async commands (== calls with wait=False, e.g. delete and put) have no results,
        but may raise exceptions. These async exceptions must get collected later via
        async_response() calls. Repeat the call until it returns None.
        The previous calls might either return one (non-None) result or raise an exception.
        If wait=True is given and there are outstanding responses, it will wait for them
        to arrive. With wait=False, it will only return already received responses.
        Nr-   )r=   r  r-   r-   r.   async_response      zRepository.async_responsec                 C   r  )z>Preload objects (only applies to remote repositories)
        Nr-   )r=   r  r-   r-   r.   preload  r  zRepository.preload)FFNTFNTFrg  )FTr   FrE  r8   )FFr   )NNF)Cr)   r*   r+   r,   r   r/   r1   r2   r3   r4   r5   r6   r   r7   rB   rC   rD   r<   r[   r_   rb   rl   propertyrn   staticmethodru   r}   rX   r   r   r   r   r   r   r   r   r   r   r   r`   rY   r   r   r  r  r   r   r   r   r   r_  r  rt  rv  rj   r   rw  ry  r  r  r   r  r  r  r  r  r  r-   r-   r-   r.   r&   5   s    E


##		
2


IG9 
)
 
	

+
	


r&   c                   @   s  e Zd ZG dd deZedZejdksJ edZ	e	jdks$J edZ
e
jdks0J ed	Zejd
ks<J e
deZeeee ZdDddZdd Zdd ZeefddZeefddZdEddZdd Zdd Zdd Zd d! Zd"d# ZdFd$d%Z d&d' Z!d(d) Z"d*d+ Z#d,d- Z$d.d/ Z%d0d1 Z&d2d3 Z'dGd6d7Z(d8d9 Z)dHd:d;Z*dHd<d=Z+dId>d?Z,dId@dAZ-dIdBdCZ.dS )Jr   c                   @   r'   )zLoggedIO.SegmentFullz2raised when a segment is full, before opening nextNr(   r-   r-   r-   r.   rL    r0   rL  z<IIB	   z<IIB32s)   z<IB   z<Ir,  Z   c                 C   s>   || _ t|| jd| _d| _|| _|| _d| _d | _d| _	d S )N)Zdisposer   )
rF   r   	_close_fdfdsr   r}  r   rW  	_write_fd_fds_cleaned)r=   rF   r}  r   capacityr-   r-   r.   r<     s   
zLoggedIO.__init__c                 C   s   |    | j  d | _d S r8   )close_segmentr  r  rZ   r-   r-   r.   rY   %  s   

zLoggedIO.closec                 C   s&   |\}}t | ddd |  d S )Nr   ZDONTNEED)r    r  rY   )r=   ts_fdtsrt   r-   r-   r.   r  *  s   zLoggedIO._close_fdc                        fddt |D }|S )zReturns generator yielding required segment dirs in data_dir as `os.DirEntry` objects.
        Start and end are inclusive.
        c                 3   D    | ]}|  r|j rt|j  kr krn n|V  qd S r8   )is_dirr+  r   r   r   f	end_indexstart_indexr-   r.   r   3      z,LoggedIO.get_segment_dirs.<locals>.<genexpr>rE   scandir)r=   data_dirr  r  segment_dirsr-   r  r.   get_segment_dirs/     zLoggedIO.get_segment_dirsc                    r  )zReturns generator yielding required segment files in segment_dir as `os.DirEntry` objects.
        Start and end are inclusive.
        c                 3   r  r8   )is_filer+  r   r   r  r  r-   r.   r   >  r  z-LoggedIO.get_segment_files.<locals>.<genexpr>r  )r=   segment_dirr  r  Zsegment_filesr-   r  r.   get_segment_files:  r  zLoggedIO.get_segment_filesNFc           	      c   s    |d u r|s	t nt}|| j }tj| jd}|s"| j||d}n| j||d}t|dd |d}|D ]*}|s@| j||d}n| j||d}t|dd |d}|D ]}t	|j
|jfV  qRq4d S )Nr   )r  )r  c                 S   rm   r8   r   r+  )dirr-   r-   r.   <lambda>N     
 z+LoggedIO.segment_iterator.<locals>.<lambda>)r   reversec                 S   rm   r8   r  r   r-   r-   r.   r  T  r  )MIN_SEGMENT_INDEXMAX_SEGMENT_INDEXr   rE   rF   rr   r  r   r  r   r+  )	r=   r   r  Zstart_segment_dirZ	data_pathdirsr  filesr!  r-   r-   r.   r   E  s$   
zLoggedIO.segment_iteratorc                 C   s    | j ddD ]\}}|  S d S )NTr  )r   r=   r   r   r-   r-   r.   r   Z  s   zLoggedIO.get_latest_segmentc                 C   s,   | j ddD ]\}}| |r|  S qdS )z+Return the last committed segment.
        Tr  N)r   is_committed_segmentr  r-   r-   r.   r   _  s
   
z$LoggedIO.get_segments_transaction_idc                 C   sZ   |    |d | _d}| jddD ]\}}||kr#| | |d7 }q td|| dS )z:Delete segment files left by aborted transactions
        r   r   Tr  zICleaned up %d uncommitted segment files (== everything after segment %d).N)r  r   r   r:  rh   r  )r=   r   r;  r   r   r-   r-   r.   rd   g  s   


zLoggedIO.cleanupc           
      C   s0  z|  |}W n
 ty   Y dS w t| |dI}z|| jj tj W n! t	yH } z|j
t
jkrBW Y d}~W d   dS |d}~ww || jj| jkr\	 W d   dS W d   n1 sfw   Y  d}	 z
t|\}}}}	W n ty   Y dS  ty   Y |S w |tkrd}qm|rdS qn)z4Check if segment ends with a COMMIT_TAG tag
        Frp   NT)rH  r   r`   segment_filenameseekr   r   rE   SEEK_ENDre   rf   EINVALrs   COMMITr~  r  rI  )
r=   r   iteratorrt   r   Zseen_commitrV  r   rW  r[  r-   r-   r.   r  v  sH   

zLoggedIO.is_committed_segmentc                 C   s"   t j| jdt|| j t|S )Nr   )rE   rF   rr   r   r   r=   r   r-   r-   r.   r    s   "zLoggedIO.segment_filenamec                 C   s   |s|s| j r| j | jkr|r| j|   | jsd| j| j dkrEtj	| jdt
| j| j }tj|sEt| ttj	| jd t| | jdd| _| jt t| _ | j| jv rd| j| j= | jS )Nr   r   Tr   )rW  r}  rL  r  r  r   r   rE   rF   rr   r   r   r   r   r   r  r   MAGIC	MAGIC_LENr  )r=   no_newwant_newrC  dirnamer-   r-   r.   get_write_fd  s    

zLoggedIO.get_write_fdc                    sp   t    fdd} fdd}|  z	j \}}W n ty,   | }Y |S w j |f |S )Nc                     s"   t d}  | fj< | S )Nrp   )r`   r  r  r  nowr   r=   r-   r.   open_fd  s   z LoggedIO.get_fd.<locals>.open_fdc                     sT    j  td kr& _ tj D ]\} }|\}} | tkr%j| = qd S d S )Nr   )r  Z
FD_MAX_AGEr  r  r  )kr  r  rt   )r  r=   r-   r.   	clean_old  s   z"LoggedIO.get_fd.<locals>.clean_old)rm  rn  r  rM  Zupd)r=   r   r  r  r  rt   r-   r  r.   get_fd  s   zLoggedIO.get_fdc                 C   s:   | j d }| _ |d ur|  jd7  _d| _|  d S d S )Nr   r   )r  r   rW  rY   )r=   rt   r-   r-   r.   r    s   zLoggedIO.close_segmentc                 C   s<   || j v r	| j |= z
t| | W d S  ty   Y d S w r8   )r  r   r  rw   r  r-   r-   r.   r:    s   
zLoggedIO.delete_segmentc              	   C   sP   t j| jd}| |}|D ]}zt | W q ty!   Y qw t| dS )zDDelete empty segment dirs, i.e those with no segment files.
        r   N)rE   rF   rr   r  rmdirre   r   )r=   r  r  r  r-   r-   r.   rT    s   
zLoggedIO.clear_empty_dirsc                 C   s"   |  |}tj|otj|S r8   )r  rE   rF   r   r   r  r-   r-   r.   rF    s   
zLoggedIO.segment_existsc                 C   s   t j| |S r8   )rE   rF   r   r  r  r-   r-   r.   r0    s   zLoggedIO.segment_sizec                 C   s   |  |}|d |tS )Nr   )r  r  rs   r  )r=   r   rt   r-   r-   r.   r     s   


zLoggedIO.get_segment_magicr   Tc              	   c   s    |  |}|| |dkr#|ttkr!td| dd dt}|| jj}|rj| j|| j|||t	t
tf|d\}}}	}
|rJ||	||
fV  n||	||fV  ||7 }|  |}|| || jj}|s,dS dS )a7  
        Return object iterator for *segment*.

        If read_data is False then include_data must be False as well.
        Integrity checks are skipped: all data obtained from the iterator must be considered informational.

        The iterator returns four-tuples of (tag, key, offset, data|size).
        r   zInvalid segment magic [segment z	, offset ]rc  N)r  r  rs   r  r  r   r   r   _readrJ  rO  rI  )r=   r   rW  rB  rd  rt   headerr   rV  r   r   r-   r-   r.   rH    s*   
	


zLoggedIO.iter_objectsc                 C   s  t d|  || jv r| j|= tj|t| jj k r:t	|dd}|
t W d    d S 1 s3w   Y  d S t	|dd}t|d}tj| dtjdv}t|}|}za|
t t|| jjkr| j|d | jj \}	}
}|
tks|tks|
| jjk s|
t|kst|d|
 d@ |	kr|d	d  }q_|
|d |
  ||
d  }t|| jjksgW ~|  n~|  w W d    n1 sw   Y  W d    n1 sw   Y  W d    d S W d    d S 1 sw   Y  d S )
Nzattempting to recover Tr   rp   r   )accessr,      r   )rh   r5  r  rE   rF   r   r  r   r   r   r   r  r`   mmapr  ZACCESS_READ
memoryviewr.  r   r/  
MAX_TAG_IDr!   r   )r=   r   r   rt   Zdst_fdZsrc_fdmmr   dcrcr   rV  r-   r-   r.   ro  "  sJ   


$	
"zLoggedIO.recover_segmentc              	   C   s   || j kr| jr| j  | |}|| || jj}| || j|||t	f|\}}}	}
||	kr;t
d|||r?|
S |S )z
        Read entry from *segment* at *offset* with *id*.

        If read_data is False the size of the entry is returned instead and integrity checks are skipped.
        The return value should thus be considered informational.
        zJInvalid segment entry header, is not for wanted id [segment {}, offset {}])r   r  syncr  r  rs   rN  r   r  rJ  r   format)r=   r   rW  r>   rd  rt   r  r   rV  r   r   r-   r-   r.   rs   A  s   


"zLoggedIO.readc              
   C   s  t |tks
J dz||}W n tjy( }	 z
td|||	d d }	~	ww || ju r5|\}
}}}n|| ju rB|\}
}}d }nt	d|t
krStd|||||jk ratd|||||j }|r||}t||krtd|||t|t|tt|dd  d@ |
krtd	|||d u r|ttfv r|d d
 |d
d  }}nB|d u r|ttfv r|d
}|d
8 }t|d
krtd||d
t|| }||tj| }d }||krtd||||||vrtd||||||fS )Nz7Exceeding MAX_TAG_ID will break backwards compatibilityz8Invalid segment entry header [segment {}, offset {}]: {}z$_read called with unsupported formatz?Invalid segment entry size {} - too big [segment {}, offset {}]zAInvalid segment entry size {} - too small [segment {}, offset {}]zPSegment entry data short read [segment {}, offset {}]: expected {}, got {} bytesr,  r  z7Segment entry checksum mismatch [segment {}, offset {}]r   zOSegment entry key short read [segment {}, offset {}]: expected {}, got {} byteszPSegment entry data short seek [segment {}, offset {}]: expected {}, got {} byteszPInvalid segment entry header, did not get acceptable tag [segment {}, offset {}])maxr  r   structr4  r   r  rN  r   	TypeErrorr/  r   rs   r.  r!   r  rJ  rO  tellr  rE   SEEK_CUR)r=   rt   fmtr  r   rW  Zacceptable_tagsrd  Z	hdr_tupler   r  r   rV  r   lengthr   oldposZseekedr-   r-   r.   r  S  st   





"
zLoggedIO._readc           
      C   s   t |}|tkrtd| dt d| j|tjk|d}|| jj }| j}| j	
|t}| j
t|t|t|d@ }	|d|	|||f |  j|7  _| j|fS )NzMore than allowed put data [z > r  r  rC  r      )r.  ZMAX_DATA_SIZEr   r  r   MANIFEST_IDrN  r   rW  header_no_crc_fmtr'  rJ  crc_fmtr!   r   rr   r   )
r=   r>   r   rC  	data_sizert   r   rW  r  r  r-   r-   r.   rK    s    
zLoggedIO.write_putc                 C   sv   | j |tjk|d}| j| jjt}| jt	|t	|d@ }|
d|||f |  j| jj7  _| j| jjfS )Nr  r  r  )r  r   r  r  r'  rN  r   rO  r  r!   r   rr   rW  r   )r=   r>   rC  rt   r  r  r-   r-   r.   rQ    s   zLoggedIO.write_deletec                 C   sh   | j | |d}|r|  | j| jjt}| jt|d@ }|	d
||f |   | jd S )N)r  r  r  r  r   )r  r  r  r'  r   r   rI  r  r!   r   rr   r  r   )r=   r7  rt   r  r  r-   r-   r.   r     s   
zLoggedIO.write_commit)r  )NF)FFF)r   FTrE  r  )/r)   r*   r+   r   rL  r  Structr   r   rN  r  r  r'  rI  Z_commitr!   r  r<   rY   r  ZMIN_SEGMENT_DIR_INDEXZMAX_SEGMENT_DIR_INDEXr  r  r  r  r   r   r   rd   r  r  r  r  r  r:  rT  rF  r0  r   rH  ro  rs   r  rK  rQ  r   r-   r-   r-   r.   r   
  sL    






 
 
%


7
r   r  )Drf   r  rE   r   rv   r  rm  binasciir   r   collectionsr   configparserr   	functoolsr   	itertoolsr   	constantsZ	hashindexr
   helpersr   r   r   r   r   r   r   r   r   r   r   r   r   Zlockingr   r   r   rh   r   Zlrucacher   platformr   r   r   r    Zalgorithms.checksumsr!   Zcrypto.file_integrityr"   r#   r)   r  r.  r  r   rJ  rO  rI  r  r   r  r&   r   rN  r   r-   r-   r-   r.   <module>   sj    	
         ^   &