
­­­­­­­­­­­­­­­­­­
<!DOCTYPE html>
<html>
3
i.                @   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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 ddlm Z 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st:d Z;dZ<dZ=dZ>eee?Z@G dd dZAG dd dZBeBjCjDd kst:dS )!    N)hexlify	unhexlify)defaultdict)ConfigParser)datetime)partial)islice   )*)NSIndex)ErrorErrorWithTracebackIntegrityErrorformat_file_sizeparse_file_size)Location)ProgressIndicatorPercent)
bin_to_hex)hostname_is_unique)secure_erasesafe_unlink)msgpack)Lock	LockError
LockErrorT)create_logger)LRUCache)SaveFileSyncFilesync_dirsafe_fadvise)crc32)IntegrityCheckedFileFileIntegrityErrors   BORG_SEGs   ATTICSEG      c               @   sb  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}dGdHZ)dIdJ Z*d~dKdLZ+ddMdNZ,dOdP Z-dQdR Z.dSdT Z/dUdV Z0dWdX Z1ddYdZZ2d[d\ Z3dd]d^Z4d_d` Z5dadb Z6dcdd Z7dedf Z8dgdh Z9ddidjZ:ddkdlZ;dmdn Z<ddodpZ=ddqdrZ>ddsdtZ?dudv Z@ddwdxZAdydz 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               @   s   e Zd ZdZdS )zRepository.DoesNotExistzRepository {} does not exist.N)__name__
__module____qualname____doc__ r+   r+   "/usr/lib64/python3.6/repository.pyDoesNotExist{   s   r-   c               @   s   e Zd ZdZdS )zRepository.AlreadyExistsz"A repository already exists at {}.N)r'   r(   r)   r*   r+   r+   r+   r,   AlreadyExists~   s   r.   c               @   s   e Zd ZdZdS )zRepository.PathAlreadyExistsz!There is already something at {}.N)r'   r(   r)   r*   r+   r+   r+   r,   PathAlreadyExists   s   r/   c               @   s   e Zd ZdZdS )z!Repository.ParentPathDoesNotExistz:The parent path of the repo directory [{}] does not exist.N)r'   r(   r)   r*   r+   r+   r+   r,   ParentPathDoesNotExist   s   r0   c               @   s   e Zd ZdZdS )zRepository.InvalidRepositoryz0{} is not a valid repository. Check repo config.N)r'   r(   r)   r*   r+   r+   r+   r,   InvalidRepository   s   r1   c               @   s   e Zd ZdZdS )z"Repository.InvalidRepositoryConfigz?{} does not have a valid configuration. Check repo config [{}].N)r'   r(   r)   r*   r+   r+   r+   r,   InvalidRepositoryConfig   s   r2   c               @   s   e Zd ZdZdS )zRepository.AtticRepositoryz8Attic repository detected. Please run "borg upgrade {}".N)r'   r(   r)   r*   r+   r+   r+   r,   AtticRepository   s   r3   c               @   s   e Zd ZdZdS )zRepository.CheckNeededz3Inconsistency detected. Please run "borg check {}".N)r'   r(   r)   r*   r+   r+   r+   r,   CheckNeeded   s   r4   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 j|| d S )N)
isinstancebytesr   super__init__)selfidZrepo)	__class__r+   r,   r8      s    
z"Repository.ObjectNotFound.__init__)r'   r(   r)   r*   r8   __classcell__r+   r+   )r;   r,   ObjectNotFound   s   r=   c               @   s   e Zd ZdZdS )z%Repository.InsufficientFreeSpaceErrorzNInsufficient free space to complete transaction (required: {}, available: {}).N)r'   r(   r)   r*   r+   r+   r+   r,   InsufficientFreeSpaceError   s   r>   c               @   s   e Zd ZdZdS )zRepository.StorageQuotaExceededzJThe storage quota ({}) has been exceeded ({}). Try deleting some archives.N)r'   r(   r)   r*   r+   r+   r+   r,   StorageQuotaExceeded   s   r?   FNTc
       
      C   s   t j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)
r9   rA   createrL   rH   rD   rM   rN   rQ   rR   r+   r+   r,   r8      s$    zRepository.__init__c             C   s   | j r| j  dstdd S )NFz&cleanup happened in Repository.__del__)rD   closeAssertionError)r9   r+   r+   r,   __del__   s    zRepository.__del__c             C   s   d| j j| jf S )Nz<%s %s>)r;   r'   rA   )r9   r+   r+   r,   __repr__   s    zRepository.__repr__c             C   s@   | j rd| _ | j| j d| _| j| jt| j| j| jd | S )NFT)rH   rD   )	rJ   rS   rA   rK   openboolrL   rH   rI   )r9   r+   r+   r,   	__enter__   s    zRepository.__enter__c             C   sR   |d k	rF|t ko|jtjk}| jr6|r6tjd d}nd}| j|d | j  d S )NzGNo space left on device, cleaning up partial transaction to free space.TF)cleanup)OSErrorerrnoZENOSPCrG   loggerwarning	_rollbackrT   )r9   exc_typeZexc_valZexc_tbZno_space_left_on_devicer[   r+   r+   r,   __exit__   s    

zRepository.__exit__c             C   s
   t | jS )N)r   r:   )r9   r+   r+   r,   id_str   s    zRepository.id_strc             C   sT   y:t tjj| dd}|jd}d|kp0d|kS Q R X W n tk
rN   dS X dS )z;Check whether there is already a Borg repository at *path*.READMErbd   s   Borg Backup repositorys   Borg repositoryNF)rX   r@   rA   joinreadr\   )rA   fdZreadme_headr+   r+   r,   is_repository   s    
zRepository.is_repositoryc             C   s   yt j|}W n tk
r"   Y n8X | j|r8| j|tj|j sPt j|rZ| j|x>|}t j	j
t j	j|t j}||krP | j|r\| j|q\W dS )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.
        N)r@   statFileNotFoundErrorrj   r.   S_ISDIRst_modelistdirr/   rA   rB   rg   pardir)r9   rA   stZprevious_pathr+   r+   r,   check_can_create_repository   s    



z&Repository.check_can_create_repositoryc             C   sv  | j | | jr.tjj|tj}tj|dd tjj|sxytj| W n. t	k
rv } z| j
||W Y dd}~X nX ttjj|dd}|jt W dQ R X tjtjj|d tdd}|jd |jdd	d
 |jddtt |jddtt |jddtt| j | jr2|jddt| j n|jddd |jddd |jddttjd | j|| dS )z0Create a new empty repository at `path`
        T)exist_okNrd   wdata)interpolation
repositoryversion1segments_per_dirmax_segment_sizerM   rN   0additional_free_spacer:       )rr   rR   r@   rA   rg   rp   makedirsexistsmkdirrl   r0   rX   writeZREPOSITORY_READMEr   Zadd_sectionsetstrZDEFAULT_SEGMENTS_PER_DIRZDEFAULT_MAX_SEGMENT_SIZEintrM   rN   r   urandomsave_config)r9   rA   parent_patherrri   configr+   r+   r,   rS     s0    


zRepository.createc              C   sT  t jj|d}t jj|d}t jj|r>tjd t|dd t jj|rd}yt j|| W nn tk
r } z6|j	t	j
t	jt	jt	jt	jt	jfkrtj| n W Y d d }~X n tk
r   tj| Y nX y"t|}|j| W d Q R X W nD tk
r4 } z&| jr tjd|j|jf  W Y d d }~X nX t jj|rPt|dd 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.zT%s: Failed writing to '%s'. This is expected when working on read-only repositories.)r@   rA   rg   isfiler^   r_   r   linkr\   r]   ZEMLINKZENOSYSZEPERMZEACCESZENOTSUPZEIOAttributeErrorr   r   PermissionErrorrI   strerrorfilename)r9   rA   r   Zconfig_pathZold_config_pathZlink_error_msgeri   r+   r+   r,   r   *  s2    
"
"zRepository.save_configc             C   s8   | j s
t|jd}| j jdd| | j| j| j  d S )Nzutf-8rw   key)r   rU   decoder   r   rA   )r9   keydatar+   r+   r,   save_keyM  s    

zRepository.save_keyc             C   s    | j jddddj }|jdS )Nrw   r    )fallbackzutf-8)r   getstripencode)r9   r   r+   r+   r,   load_keyS  s    zRepository.load_keyc             C   sp   | j r| jj  rtdtjj| jd}y,t|d}tj	t
|j ddS Q R X W n tk
rj   d S X d S )Nz-bug in code, exclusive lock should exist herenoncerbig)	byteorder)rI   rD   got_exclusive_lockrU   r@   rA   rg   rX   r   
from_bytesr   rh   rl   )r9   
nonce_pathri   r+   r+   r,   get_free_nonceX  s     zRepository.get_free_noncec             C   s   | j r| jj  rtd| j |kr.tdtjj| jd}y4t	|dd}|j
t|jddd W d Q R X W n@ tk
r } z$| j r tjd	|j|jf  W Y d d }~X nX d S )
Nz-bug in code, exclusive lock should exist herez6nonce space reservation with mismatched previous stater   F)binary   r   )r   zT%s: Failed writing to '%s'. This is expected when working on read-only repositories.)rI   rD   r   rU   r   	Exceptionr@   rA   rg   r   r   r   to_bytesr   r^   r_   r   r   )r9   Znext_unreservedZstart_noncer   ri   r   r+   r+   r,   commit_nonce_reservationc  s    &z#Repository.commit_nonce_reservationc             C   sB   | j rt| jd | j  tjtjj| jd tj| j dS )z.Destroy the repository at `self.path`
        z is in append-only moder   N)	rM   
ValueErrorrA   rT   r@   removerg   shutilZrmtree)r9   r+   r+   r,   destroyt  s
    zRepository.destroyc                s2   t  fddtj jD }|r*|d S d S d S )Nc             3   sT   | ]L}|j d r|dd j rtjtjj j|jdkrt|dd V  qdS )zindex.   Nr   )
startswithisdigitr@   rk   rA   rg   st_sizer   ).0fn)r9   r+   r,   	<genexpr>~  s   z6Repository.get_index_transaction_id.<locals>.<genexpr>r	   )sortedr@   ro   rA   )r9   indicesr+   )r9   r,   get_index_transaction_id}  s
    z#Repository.get_index_transaction_idc             C   sh   | j  }| jj }|d k	r6|d kr6d| j }| j|||krd|d k	rT||krTd }n|}| j|| d S )Nz,%s" - although likely this is "beyond repair)r   rC   get_segments_transaction_idrA   r4   replay_segments)r9   index_transaction_idsegments_transaction_idmsgZreplay_fromr+   r+   r,   check_transaction  s    


zRepository.check_transactionc             C   s   | j   | j S )N)r   r   )r9   r+   r+   r,   get_transaction_id  s    zRepository.get_transaction_idc             C   s   t tjj| jdj  d S )NrD   )r   r@   rA   rg   
break_lock)r9   r+   r+   r,   r     s    zRepository.break_lockc             C   s   | j d k	r| j j|| d S )N)rD   migrate_lock)r9   Zold_idZnew_idr+   r+   r,   r     s    
zRepository.migrate_lockc       	      C   sB  || _ ytj|}W n tk
r2   | j|Y nX tj|jsJ| j||rrttj j	|d||t
 dj | _nd | _td d| _y0ttj j	| j d}| jj| W d Q R X W n( tk
r   | j  | j| j Y nX d| jj kr| j  | j|d| jjdd}|dkr2| j  | j|d	| t| jjdd
| _| jtkrj| j  | j|dt | jjdd| _t| jjdddd| _| jp| jjdddd| _| jd krt| jjdddd| _t| jjddj | _ t!| j | j| j| _"| j#r>| j"j$ }|d k	r>| j"j%|t&kr>| j  | j'|d S )NrD   )ZtimeoutZkill_stale_locks)rv   r   rw   zno repository section foundrx   r	   z;repository version %d is not supported by this borg versionr{   zmax_segment_size >= %drz   r}   r   )r   rM   FrN   r:   )(rA   r@   rk   rl   r-   rm   rn   r1   r   rg   r   acquirerD   r   r   rX   Z	read_filerT   Zsectionsr2   Zgetintr   r   r{   ZMAX_SEGMENT_SIZE_LIMITrz   r}   rM   Z
getbooleanrN   r   r   r:   LoggedIOrC   rQ   get_latest_segmentget_segment_magicATTIC_MAGICr3   )	r9   rA   rL   rH   rD   rq   ri   Zrepo_versionsegmentr+   r+   r,   rX     sT    
$


zRepository.openc             C   s0   | j r,| jr| jj  d | _| j j  d | _ d S )N)rD   rC   rT   release)r9   r+   r+   r,   rT     s    

zRepository.closec             C   sT   | j r| j }| j  || j  | j  | jj  | js@| j  | j  | j  dS )zCommit transaction
        N)	rP   rollbackcheck_free_spacelog_storage_quotarC   write_commitrM   compact_segmentswrite_index)r9   
save_spaceZ	exceptionr+   r+   r,   commit  s    
zRepository.commitc             C   s   d| }t jj| j|}y$t|d}tj|}W d Q R X W n tk
rP   d S X |jddkrxtj	d|jd| d S || j
 S )Nzintegrity.%dre   s   versionr$   z'Unknown integrity data version %r in %s)r@   rA   rg   rX   r   unpackrl   r   r^   r_   r   )r9   transaction_idr   integrity_fileZintegrity_pathri   	integrityr+   r+   r,   _read_integrity  s    zRepository._read_integrityc             C   s   |d krt  S tjj| jd| }| j|d}y$t|d|d}t j|S Q R X W nb ttt	fk
r } z@t
jd| tj| |s | j| j  | j  | j| j S d }~X nX d S )Nzindex.%ds   indexF)r   integrity_datazARepository index missing or corrupted, trying to recover from: %s)r   r@   rA   rg   r   r"   rh   r   r\   r#   r^   r_   unlinkprepare_txnr   r   
open_index)r9   r   auto_recover
index_pathr   ri   excr+   r+   r,   r     s    
zRepository.open_indexc          (   C   s  d| _ | jrZ| jj  rZ| jd k	r*tdy| jj  W n  ttfk
rX   d| _  Y nX | j	 sj|d kry| j
|dd| _	W nL tttfk
r } z*tjd| | j  | j
|dd| _	W Y d d }~X nX |d kri | _t | _d| _| jj  n|r| jj| tjj| jd| }tjj| jd| }| j|d	}y(t|d|d
}tj|}W d Q R X W nl tj tj!t"tfk
r }	 zBtjd|	 t#|	t"stj$| tj$| | j  | j%| d S d }	~	X nX |d dkr>tj&d| |d | _t | _d| _x,t'|d D ]}
tj&d|
 | j(|
 qW tj&d nF|d dkr^td|d  n&|d | _t|d | _|j)dd| _| j*  x@| jj+ D ]2\}}x&t,|D ]}
|
|kr|j-|
 qW qW d S )NTz-bug in code, exclusive lock should exist hereF)r   z9Checking repository transaction due to previous error: %sr   zhints.%dzindex.%ds   hints)r   r   zARepository hints file missing or corrupted, trying to recover: %ss   versionr	   zUpgrading from v1 hints.%ds   segmentss   compactz%Rebuilding sparse info for segment %dzUpgrade to v2 hints completer$   zUnknown hints file version: %ds   storage_quota_use).rG   rI   rD   r   rL   rU   Zupgrader   r   rE   r   r   r\   r#   r^   r_   r   segments	FreeSpacecompactrO   rF   clearrC   r[   r@   rA   rg   r   r"   r   r   ZUnpackExceptionZ	ExtraDatarl   r5   r   r   debugr   _rebuild_sparser   r   itemslistr   )r9   r   
do_cleanupr   Z
hints_pathr   r   ri   hintsr   r   r   Zshadowed_segmentsr+   r+   r,   r     sp    
"





zRepository.prepare_txnc          !   C   s  dd }dd }d| j | j| jd}ddi}| jj }|d k	sBt| jrttj	j
| j	dd	$}td
|tj jtf |d W d Q R X d| }tj	j
| j	|}t|d |dd}	tj||	 ||	 W d Q R X |	j|d< d| }
tj	j
| j	|
}t|d |
dd}	| jj|	 ||	 W d Q R X |	j|d< d| }tj	j
| j	|}t|d d}	tj||	 ||	 W d Q R X || t| j	 || || t| j	 d| }xLtj| j	D ]<}|jds̐q|j|rܐqtjtj	j
| j	| qW d | _d S )Nc             S   s   | j   tj| j  d S )N)flushr@   fsyncfileno)ri   r+   r+   r,   flush_and_syncW  s    z.Repository.write_index.<locals>.flush_and_syncc             S   s   t j| d |  d S )Nz.tmp)r@   rename)filer+   r+   r,   
rename_tmp[  s    z*Repository.write_index.<locals>.rename_tmpr$   )s   versions   segmentss   compacts   storage_quota_uses   versionZtransactionsaztransaction %d, UTC time %s)r   zhints.%dz.tmpT)r   r   s   hintszindex.%ds   indexzintegrity.%dwbz.%dindex.hints.
integrity.)r   r   r   )r   r   rO   rC   r   rU   rM   rX   r@   rA   rg   printr   ZutcnowZstrftimeZ
ISO_FORMATr"   r   packr   rE   r   r   ro   r   endswithr   )r9   r   r   r   r   r   logZ
hints_nameZ
hints_fileri   Z
index_nameZ
index_fileZintegrity_namer   Zcurrentnamer+   r+   r,   r   V  sV    

$



zRepository.write_indexc             C   s  | j j d }t| jd t| jd  d }||7 }|| j7 }| js| jt }t| jdk rd}xD| jj	 D ]6\}}y|| j
j|| 7 }W qn tk
r   Y qnX qnW tjd t||}tjd| ||7 }n||7 }ytj| j}W n4 tk
r } ztjdt|  dS d}~X nX |j|j }	tjd	j||	 |	|k r| jrdtjd
 | j  n| jdd t|}
t|	}| j|
|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: Nz2check_free_space: required bytes {}, free bytes {}z@Not enough free space to initialize repository at this location.T)r[   )rE   sizelenr   r   r}   rM   r{   MAX_OBJECT_SIZEr   rC   segment_sizerl   r^   r   minr@   statvfsrA   r\   r_   r   f_bavailf_frsizeformatrK   errorr   r`   r   r>   )r9   Zrequired_free_spaceZ
hints_sizeZfull_segment_sizeZcompact_working_spacer   ZfreeZst_vfsZos_errorZ
free_spaceZformatted_requiredZformatted_freer+   r+   r,   r     sB    
 








zRepository.check_free_spacec             C   s$   | j r tjdt| jt| j  d S )Nz!Storage quota: %s out of %s used.)rN   r^   infor   rO   )r9   r+   r+   r,   r     s    zRepository.log_storage_quotac                s  j s
dS j}j }j}g td d fdd	} jd ttj ddd	d
}x tj j	 D ]\}j
js jd j = |j  qnj
j}d| | }|dj kr|d| k r jd|d | |j  qn|jd  jd| |d | xj
jddD ]\}	}
}}|	tkrHq,jj|
}||fk}|	tkr|ryj
j|
|dd\}}W n0 tjk
r   |  j
j|
|\}}Y nX ||fj|
< |j|d ||  d7  < |  d8  < q,|	tkrZ| rZyj|
 j W n ttfk
r:   Y nX  jt|j
jj 8  _n|	tkr,| r,|
jkpt fddj|
 D }|dkp|k}|s|ryj
j!|
dd\}}W n. tjk
r   |  j
j!|
\}}Y nX j |  |7  < |j|d nj|
 s,j|
= q,W | dksJt"dj# |j  qnW |j$  |dd j} j%dt&||   jd dS )zBCompact sparse segments by copying data into new segments
        Nzborg.debug.compact_segmentsTc                sv   j j| d} jd| rdnd| xHD ]@} jd| jj|}|dksVtdj j| j|= q*W g d S )N)intermediatez+complete_xfer: wrote %scommit at segment %dzintermediate r   z)complete_xfer: deleting unused segment %dr   z<Corrupted segment reference count - corrupted index or hints)rC   r   r   r   poprU   delete_segmentr   )r  r   count)r^   r9   unusedr+   r,   complete_xfer  s    
z2Repository.compact_segments.<locals>.complete_xferzcompaction started.zCompacting segments %3.0f%%r	   zrepository.compact_segments)totalr   stepmsgidz3segment %d not found, but listed in compaction datag      ?g?g333333?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 )Nr+   )r   Zshadowed)r   r+   r,   r   '  s    z.Repository.compact_segments.<locals>.<genexpr>z<Corrupted segment reference count - corrupted index or hintsF)r  z+compaction freed about %s repository space.zcompaction completed.)T)'r   rO   r   r   r   r   r   r   r   r   rC   segment_existsr_   showr   r{   
setdefaultiter_objects
TAG_COMMITrE   r   TAG_PUT	write_putr   SegmentFullrF   r   KeyErrorr   put_header_fmtr   
TAG_DELETEanywrite_deleterU   appendfinishr  r   )r9   Zquota_use_beforer   r   r	  piZfreeable_spacer   Zfreeable_ratiotagr   offsetru   Zin_indexZis_index_objectZnew_segmentZshadowed_put_existsZdelete_is_not_stabler   Zquota_use_afterr+   )r^   r   r9   r  r,   r     s    
"
"

zRepository.compact_segmentsc       
      C   s   | j }d | _ | j|dd ztdd | jj D }t|ddd}x\t| jj D ]J\}\}}|j| |d k	rz||krzqR||krP | jj|}	| j	||	 qRW |j
  | j  W d || _ | j  X d S )NF)r   c             s   s   | ]
}d V  qdS )r	   Nr+   )r   _r+   r+   r,   r   g  s    z-Repository.replay_segments.<locals>.<genexpr>zReplaying segments %3.0f%%zrepository.replay_segments)r
  r   r  )rL   r   sumrC   segment_iteratorr   	enumerater  r  _update_indexr  r   r   )
r9   r   r   Zremember_exclusivesegment_countr  ir   r   objectsr+   r+   r,   r   a  s&    
zRepository.replay_segmentsc             C   s  d| j |< xN|D ]D\}}}}|tkry6| j| \}}	| j|  |7  < | j |  d8  < W n tk
rr   Y nX ||f| j|< | j |  d7  < |  j|7  _q|tkr y| jj|\}}W n tk
r   Y nHX | jj	|rX| j |  d8  < | jj
|||dd}| j|  |7  < q|tkr.qqdj||}
|dkrP| j|
q||
 qW | j | dkr~| jj|| j|< dS )z2some code shared between replay_segments and checkr   r	   F)	read_datazUnexpected tag {} in segment {}N)r   r  rE   r   r  rO   r  r  rC   r  rh   r  r  r4   r   )r9   r   r(  Zreportr  r   r   r   sr!  r   r+   r+   r,   r%  x  s:    



zRepository._update_indexc             C   s   y| j j|}W n  tk
r0   | jj| dS X | j| dkrN|| j|< dS d| j|< xl| j j|ddD ]X\}}}}|tkr| jj	|d||fkr| j|  |7  < qj|t
krj| j|  |7  < qjW dS )	zNRebuild sparse bytes count for a single segment relative to the current index.Nr   F)r)  r	   r   r   )r   r   )rC   r   rl   r   r  r   r  r  rE   r   r  )r9   r   r   r  r   r   r   r+   r+   r,   r     s    

zRepository._rebuild_sparsec          !      s  | j r|rt| jd d  fdd}tjd | j s>ty"| j }| j|}tj	d| W n< t
k
r } z | jj }d}tj	d| W Y dd}~X nX |dkrtj	d	 | j }|dkrtj	d
 | jj }|dkr|d dS |r| jj| | jj }tj	d| tj	d| | jd tdd | jj D }tj	d| t|dddd}	xt| jj D ]\}
\}}|	j|
 ||krqhtj	d| yt| jj|}W nX tk
r } z:|t| g }|r| jj|| t| jj|}W Y dd}~X nX | j||| qhW |	j  |rV|dkrV|dj| |d | j_| jj  tjd |rX| rXt |t | j!kr|d tj"dt | tj"dt | j! n
tjd d}d}xB| j!j# D ]4\}}|j$||}||krtj%|t&||| qW xR|j# D ]F\}}|| j!kr&q| j!j$||}||krtj%|t&||| qW |rn| j'  | j(  | j)   r|rtjd n
tj"d  n
tjd!   p|S )"zCheck repository consistency

        This method verifies all segment checksums and makes sure
        the index is consistent with the data stored in the segments.
        z is in append-only modeFc                s   d t j|  d S )NT)r^   r  )r   )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   s   | ]
}d V  qdS )r	   Nr+   )r   r!  r+   r+   r,   r     s    z#Repository.check.<locals>.<genexpr>zFound %d segmentszChecking segments %3.1f%%g?zrepository.check)r
  r   r  r  zchecking segment file %s...zAdding commit tag to segment {}r	   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>z6Completed repository check, errors found and repaired.z)Completed repository check, errors found.z.Completed repository check, no problems found.)*rM   r   rA   r^   r  rG   rU   r   r   r   r   rC   r   r   r   r[   r   r"  r#  r   r$  r  r   r  r   r   recover_segmentr%  r  r  r   r   r   rE   r  	iteritemsr   r_   r   r   r   r   )r9   Zrepairr   r,  r   Zcurrent_indexr   r   r&  r  r'  r   r   r(  r   Zline_formatZ	not_foundr   valueZcurrent_valuer+   )r+  r,   check  s    










"





zRepository.checkc             c   s   x| j j D ]z\}}y6x0| j j|ddD ]\}}}}|||||fV  q(W W q tk
r } ztjd||t|f  W Y dd}~X qX q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...
        T)r  z6Segment %d (%s) has IntegrityError(s) [%s] - skipping.N)rC   r#  r  r   r^   r  r   )r9   r   r   r  r   r   ru   r   r+   r+   r,   scan_low_level  s    
zRepository.scan_low_levelc            C   s,   |r| j j| j j  d| _d| _d| _dS )z	
        NF)rC   r[   r   rE   rG   rP   )r9   r[   r+   r+   r,   r`   ,  s
    zRepository._rollbackc             C   s   | j dd d S )NF)r[   )r`   )r9   r+   r+   r,   r   5  s    zRepository.rollbackc             C   s    | j s| j| j | _ t| j S )N)rE   r   r   r   )r9   r+   r+   r,   __len__9  s    zRepository.__len__c             C   s    | j s| j| j | _ || j kS )N)rE   r   r   )r9   r:   r+   r+   r,   __contains__>  s    zRepository.__contains__c             C   s4   | j s| j| j | _ 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>I  s    z#Repository.list.<locals>.<listcomp>)marker)rE   r   r   r   r.  )r9   limitr6  r+   r+   r,   r   C  s    zRepository.listc             C   s   |dk	r|dk rt d| js2| j }| j|| _|dk}|rBdn| j| \}}g }x| jj|D ]\}}	| jj||ddd}
xxyt|
\}}}}W n tt	fk
r   P Y nX |dkrd}q|t
kr||f| jj|kr|j| t||kr|S qW qbW |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 = Noner   F)r)  r  )r   r   )r   rE   r   r   rC   r#  r  nextStopIterationr   r  r   r  r   )r9   r7  r6  r   Zat_startZstart_segmentZstart_offsetresultr   r   Zobj_iteratorr  r:   r   r   r+   r+   r,   scanK  s.    
zRepository.scanc             C   s^   | j s| j| j | _ y| j | \}}| jj|||S  tk
rX   | j|| jd Y nX d S )N)rE   r   r   rC   rh   r  r=   rA   )r9   r:   r   r   r+   r+   r,   r   v  s    zRepository.getc             c   s   x|D ]}| j |V  qW d S )N)r   )r9   idsZis_preloadedr4  r+   r+   r,   get_many  s    
zRepository.get_manyc             C   s   | j s| j| j  y| j| \}}W n tk
r:   Y nX | j|||dd | jj||\}}|  jt	|| jj
j 7  _| jj|d | j|  d7  < ||f| j|< | jr| j| jkr| jt| jt| j| _| j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.
        F)update_shadow_indexr   r	   N)rG   r   r   rE   r  _deleterC   r  rO   r   r  r   r   r  rN   r?   r   rP   )r9   r:   ru   waitr   r   r+   r+   r,   put  s     zRepository.putc             C   sd   | j s| j| j  y| jj|\}}W n$ tk
rL   | j|| jdY nX | j|||dd 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.
        NT)r>  )	rG   r   r   rE   r  r  r=   rA   r?  )r9   r:   r@  r   r   r+   r+   r,   delete  s    zRepository.deletec            C   s   |r| j j|g j| | j|  d8  < | jj|||dd}| j|  |7  < | jj|\}}| j|  |7  < | jj|d d S )Nr	   F)r)  r   )rF   r  r  r   rC   rh   r   r  )r9   r:   r   r   r>  r   r+   r+   r,   r?    s    zRepository._deletec             C   s   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+   )r9   r@  r+   r+   r,   async_response  s    zRepository.async_responsec             C   s   dS )z>Preload objects (only applies to remote repositories)
        Nr+   )r9   r<  r+   r+   r,   preload  s    zRepository.preload)FFNTFNTF)NT)F)T)T)N)FF)NN)NN)F)T)T)T)Cr'   r(   r)   r*   r   r-   r.   r/   r0   r1   r2   r3   r   r4   r=   r>   r?   r8   rV   rW   rZ   rb   propertyrc   staticmethodrj   rr   rS   r   r   r   r   r   r   r   r   r   r   r   rX   rT   r   r   r   r   r   r   r   r   r   r%  r   r0  r1  r`   r   r2  r3  r   r;  r   r=  rA  rB  r?  rC  rD  r+   r+   r+   r,   r&   5   s~   D  
##		
2


GF: 
'
d	

+	



r&   c               @   sd  e Zd ZG dd deZejdZejdks0t	ejdZ
e
jdksHt	ejdZejdks`t	ejd	Zejd
ksxt	ejdeZejeee Zd>ddZdd Zdd Zd?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&d' Zd(d) Zd*d+ Z d,d- Z!dAd0d1Z"d2d3 Z#dBd4d5Z$dCd6d7Z%dDd8d9Z&dEd:d;Z'dFd<d=Z(dS )Gr   c               @   s   e Zd ZdZdS )zLoggedIO.SegmentFullz2raised when a segment is full, before opening nextN)r'   r(   r)   r*   r+   r+   r+   r,   r    s   r  z<IIB	   z<IIB32s)   z<IB   z<Ir   Z   c             C   s>   || _ t|| jd| _d| _|| _|| _d| _d | _d| _	d S )N)Zdisposer   )
rA   r   	_close_fdfdsr   r7  rz   r   	_write_fd_fds_cleaned)r9   rA   r7  rz   Zcapacityr+   r+   r,   r8     s    zLoggedIO.__init__c             C   s   | j   | jj  d | _d S )N)close_segmentrL  r   )r9   r+   r+   r,   rT     s    
zLoggedIO.closec             C   s&   |\}}t |j ddd |j  d S )Nr   ZDONTNEED)r    r   rT   )r9   ts_fdtsri   r+   r+   r,   rK    s    zLoggedIO._close_fdNFc             #   s    d kr|sdnd t jj| jd} | j t j|}|sPfdd|D }nfdd|D }t|t|d	}x|D ]x}t jt jj||}|s fd
d|D }n fdd|D }t|t|d	}x&|D ]}t|t jj|||fV  qW qvW d S )Nr   r$   r~   r	   ru   c                s$   g | ]}|j  rt| kr|qS r+   )r   r   )r   dir)start_segment_dirr+   r,   r5    s    z-LoggedIO.segment_iterator.<locals>.<listcomp>c                s$   g | ]}|j  rt| kr|qS r+   )r   r   )r   rR  )rS  r+   r,   r5    s    )r   reversec                s$   g | ]}|j  rt| kr|qS r+   )r   r   )r   r   )r   r+   r,   r5    s    c                s$   g | ]}|j  rt| kr|qS r+   )r   r   )r   r   )r   r+   r,   r5    s    l        l    )r@   rA   rg   rz   ro   r   r   )r9   r   rT  Z	data_pathdirsrR  	filenamesr   r+   )r   rS  r,   r#    s"    



zLoggedIO.segment_iteratorc             C   s    x| j ddD ]
\}}|S W d S )NT)rT  )r#  )r9   r   r   r+   r+   r,   r     s    zLoggedIO.get_latest_segmentc             C   s,   x&| j ddD ]\}}| j|r|S qW dS )z+Return the last committed segment.
        T)rT  N)r#  is_committed_segment)r9   r   r   r+   r+   r,   r     s    
z$LoggedIO.get_segments_transaction_idc             C   sh   |d | _ d}xF| jddD ]6\}}||krP|| jkr>| j|= t| |d7 }qP qW tjd|| dS )z:Delete segment files left by aborted transactions
        r	   r   T)rT  zICleaned up %d uncommitted segment files (== everything after segment %d).N)r   r#  rL  r   r^   r   )r9   r   r  r   r   r+   r+   r,   r[     s    


zLoggedIO.cleanupc       
   (   C   s  y| j |}W n tk
r"   dS X t| j|dn}y|j| jj tj W n6 t	k
r } z|j
t
jkrpdS |W Y dd}~X nX |j| jj| jkrdS W dQ R X d}xZyt|\}}}}	W n( tk
r   dS  tk
r   P Y nX |tk rd}q|rdS qW |S )z4Check if segment ends with a COMMIT_TAG tag
        Fre   NT)r  r   rX   segment_filenameseek
header_fmtr   r@   SEEK_ENDr\   r]   ZEINVALrh   COMMITr8  r9  r  )
r9   r   iteratorri   r   Zseen_commitr  r   r   r!  r+   r+   r,   rW  %  s6    
zLoggedIO.is_committed_segmentc             C   s"   t jj| jdt|| j t|S )Nru   )r@   rA   rg   r   rz   )r9   r   r+   r+   r,   rX  E  s    zLoggedIO.segment_filenamec             C   s   | r*| j r*| j | jkr*|r"| j| j  | js| j| j dkrtjj	| jdt
| j| j }tjj|stj| ttjj	| jd t| j| jdd| _| jjt t| _ | j| jkr| j| j= | jS )Nr   ru   T)r   )r   r7  r  rO  rM  r   rz   r@   rA   rg   r   r   r   r   r   rX  r   MAGIC	MAGIC_LENrL  )r9   Zno_newr  dirnamer+   r+   r,   get_write_fdH  s     

zLoggedIO.get_write_fdc                sp   t j   fdd} fdd}|  yj \}}W n tk
rX   | }Y nX jj |f |S )Nc                 s"   t jd}  | fj< | S )Nre   )rX   rX  rL  )ri   )nowr   r9   r+   r,   open_fdc  s    z LoggedIO.get_fd.<locals>.open_fdc                 sT    j  td krP _ x6tjj D ]$\} }|\}} | tkr(j| = q(W d S )Nr   )rN  Z
FD_MAX_AGEr   rL  r   )krP  rQ  ri   )rb  r9   r+   r,   	clean_oldh  s    z"LoggedIO.get_fd.<locals>.clean_old)timeZ	monotonicrL  r  Zupd)r9   r   rc  re  rQ  ri   r+   )rb  r   r9   r,   get_fd^  s    zLoggedIO.get_fdc             C   s6   | j d  }| _ |d k	r2|  jd7  _d| _|j  d S )Nr	   r   )rM  r   r   rT   )r9   ri   r+   r+   r,   rO  ~  s
    zLoggedIO.close_segmentc             C   s>   || j kr| j |= yt| j| W n tk
r8   Y nX d S )N)rL  r   rX  rl   )r9   r   r+   r+   r,   r    s    
zLoggedIO.delete_segmentc             C   s"   | j |}tjj|o tjj|S )N)rX  r@   rA   r   getsize)r9   r   r   r+   r+   r,   r    s    
zLoggedIO.segment_existsc             C   s   t jj| j|S )N)r@   rA   rh  rX  )r9   r   r+   r+   r,   r     s    zLoggedIO.segment_sizec             C   s   | j |}|jd |jtS )Nr   )rg  rY  rh   r_  )r9   r   ri   r+   r+   r,   r     s    

zLoggedIO.get_segment_magicr   Tc          	   c   s   | j |}|j| |dkr>|jttkr:tdj|dt}|j| jj}x||r| j	|| j|||t
ttf|d\}}}	}
|r||	||
fV  n||	||fV  ||7 }| j |}|j| |j| jj}qNW 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 {}, offset {}])r)  N)rg  rY  rh   r_  r^  r   r  rZ  r   _readr  r  r  )r9   r   r   r  r)  ri   headerr   r  r   ru   r+   r+   r,   r    s$    	



zLoggedIO.iter_objectsc          &   C   s  t jd|  || jkr | j|= tjj|t| jj k r^t	|dd}|j
t W d Q R X d S t	|dd}t|d}tj|j dtjd}t|}|}z|j
t xt|| jjkrT| jj|d | jj \}	}
}|
tks&|tks&|
| jjk s&|
t|ks&t|d|
 d@ |	kr4|d	d  }q|j
|d |
  ||
d  }qW W d ~|j  X W d Q R X W d Q R X W d Q R X d S )
Nzattempting to recover T)r   re   r   )accessr   l    r	   )r^   r  rL  r@   rA   rh  r_  rZ  r   r   r   r^  rX   mmapr   ZACCESS_READ
memoryviewr   r   r   
MAX_TAG_IDr!   r   )r9   r   r   ri   Zdst_fdZsrc_fdZmmru   dcrcr   r  r+   r+   r,   r-    s0    

"(zLoggedIO.recover_segmentc             C   s   || j kr| jr| jj  | j|}|j| |j| jj}| j|| j|||t	f|\}}}	}
||	krvt
dj|||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   rM  syncrg  rY  rh   r  r   ri  r  r   r  )r9   r   r   r:   r)  ri   rj  r   r  r   ru   r+   r+   r,   rh     s    


"
zLoggedIO.readc             C   s&  t |tkstdy|j|}W n8 tjk
rZ }	 ztdj|||	d W Y d d }	~	X nX || jkrt|\}
}}}n"|| j	kr|\}
}}d }nt
d|tkrtdj|||||jk rtdj|||||j }|rt|j|}t||krtdj|||t|t|tt|dd  d@ |
kr@td	j|||d kr |ttfkr |d d
 |d
d   }}n|d kr|ttfkr|jd
}|d
8 }t|d
krtdj||d
t||j }|j|tj| }d }||kr tdj||||||krtdj||||||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   l    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 {}])maxrn  rU   r   structr  r   r  r  rZ  	TypeErrorr   r   rh   r   r!   rm  r  r  tellrY  r@   SEEK_CUR)r9   ri   Zfmtrj  r   r   Zacceptable_tagsr)  Z	hdr_tupler   rp  r   r  r   Zlengthru   ZoldposZseekedr+   r+   r,   ri    sZ     





$




zLoggedIO._readc       
      C   s   t |}|tkr tdj|t| j|d}|| jj }| j}| jj	|t
}| jj	t|t|t|d@ }	|jdj|	|||f |  j|7  _| j|fS )Nz$More than allowed put data [{} > {}])r  l        )r   ZMAX_DATA_SIZEr   r  ra  r  r   r   header_no_crc_fmtr   r  crc_fmtr!   r   rg   r   )
r9   r:   ru   r  Z	data_sizeri   r   r   rj  rp  r+   r+   r,   r  )  s     zLoggedIO.write_putc             C   sn   | j |d}| jj| jjt}| jjt|t|d@ }|jdj	|||f |  j
| jj7  _
| j| jjfS )N)r  l    rw  )ra  rx  r   r  r   r  ry  r!   r   rg   r   r   )r9   r:   r  ri   rj  rp  r+   r+   r,   r  7  s    zLoggedIO.write_deletec             C   sr   |r| j  }|j  n| j  | j  }| jj| jjt}| jjt	|d@ }|j
dj||f | j  | jd S )Nl    rw  r	   )ra  rq  rO  rx  r   rZ  r   r  ry  r!   r   rg   r   )r9   r  ri   rj  rp  r+   r+   r,   r   ?  s    
zLoggedIO.write_commit)rJ  )NF)FF)r   FT)T)T)F)F)F))r'   r(   r)   r   r  rs  StructrZ  r   rU   r  rx  ry  r   r  Z_commitr!   r\  r8   rT   rK  r#  r   r   r[   rW  rX  ra  rg  rO  r  r  r   r   r  r-  rh   ri  r  r  r   r+   r+   r+   r,   r     sD   






 
 
%

7

r   rH  )Er]   rl  r@   r   rk   rs  rf  Zbinasciir   r   collectionsr   Zconfigparserr   r   	functoolsr   	itertoolsr   Z	constantsZ	hashindexr   Zhelpersr   r   r   r   r   r   r   r   r   r   r   r   Zlockingr   r   r   r^   r   Zlrucacher   platformr   r   r   r    Zalgorithms.checksumsr!   Zcrypto.file_integrityr"   r#   r'   r^  r   r_  r   rU   r  r  r  rn  r   r   r&   r   r  r   r+   r+   r+   r,   <module>   sh   	
             