o
    +ke4                     @   s   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mZmZ ddlmZ ddlmZ dd	lmZmZ ee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dS )    N   )
KeyfileKeyKeyfileNotFoundError)REPOSITORY_README)ProgressIndicatorPercent)get_base_dirget_keys_dirget_cache_dir)Lock)create_logger)
RepositoryMAGICs   ATTICSEGc                       sn   e Zd Z fddZdddZdd Zedd	d
ZedddZdd Z	edd Z
dd Zdd Z  ZS )AtticRepositoryUpgraderc                    s&   d|d< d|d< t  j|i | d S )NFlockZcheck_segment_magic)super__init__)selfargskw	__class__ 1usr/lib/python3.10/site-packages/borg/upgrader.pyr      s   z AtticRepositoryUpgrader.__init__TFc              	   C   sV  | d d}|s%| j  dtj d}td| |s%tj| j |tjd td t	tj 
| j ddd	d
 | _dd | j D }z|  }W n tyX   td Y nw | || W d   n1 siw   Y  t	tj 
| j ddd | _z$| | | j||d | j||||d |   W | j  d| _|S | j  d| _w )a  convert an attic repository to a borg repository

        those are the files that need to be upgraded here, from most
        important to least important: segments, key files, and various
        caches, the latter being optional, as they will be rebuilt if
        missing.

        we nevertheless do the order in reverse, as we prefer to do
        the fast stuff first, to improve interactivity.
        Nz.before-upgrade-z%Y-%m-%d-%H:%M:%Szmaking a hardlink copy in %s)copy_functionz1opening attic repository with borg and convertingr   Tg      ?)	exclusivetimeoutc                 S   s   g | ]\}}|qS r   r   ).0ifilenamer   r   r   
<listcomp>.   s    z3AtticRepositoryUpgrader.upgrade.<locals>.<listcomp> no key file found for repository)r   )dryruninplace)r!   r"   progress)pathdatetimenowloggerinfoshutilcopytreeoslinkr
   joinacquirer   ioZsegment_iteratorfind_attic_keyfiler   warningconvert_keyfilesconvert_cacheconvert_repo_indexconvert_segmentsborg_readmerelease)r   r!   r"   r#   Zbackupsegmentskeyfiler   r   r   upgrade   s:   
 



zAtticRepositoryUpgrader.upgradec                 C   sT   t j| jd}t | t|d}|t W d    d S 1 s#w   Y  d S )NREADMEw)r+   r$   r-   removeopenwriter   )r   readmefdr   r   r   r6   A   s
   
"z#AtticRepositoryUpgrader.borg_readmec                 C   s~   t dt|   t| }t|ddd}t| D ]\}}|r#|| |r+td qtj	|t
t|d q|r=|  dS dS )a  convert repository segments from attic to borg

        replacement pattern is `s/ATTICSEG/BORG_SEG/` in files in
        `$ATTIC_REPO/data/**`.

        luckily the magic string length didn't change so we can just
        replace the 8 first bytes of all regular files in there.zconverting %d segments...zConverting segments %3.0f%%zupgrade.convert_segments)totalmsgmsgidgMbP?r"   N)r'   r(   lenr   	enumerateZshowtimesleepr   header_replaceATTIC_MAGICr   Zfinish)r8   r!   r"   r#   Zsegment_countpir   r   r   r   r   r5   G   s   	
z(AtticRepositoryUpgrader.convert_segmentsc              	   C   s   t | dd}|d |t||krY|r!|d || n@t| | d  t | d}|| ||  W d    n1 sEw   Y  t| d  W d    d S W d    d S W d    d S 1 slw   Y  d S )Nzr+br   z.tmpwb)r>   seekreadrF   r?   r+   renameunlink)r   Z	old_magicZ	new_magicr"   segmentZnew_segmentr   r   r   rJ   ]   s"   


"z&AtticRepositoryUpgrader.header_replacec                 C   
   t | S )ae  find the attic keyfiles

        the keyfiles are loaded by `KeyfileKey.find_key_file()`. that
        finds the keys with the right identifier for the repo.

        this is expected to look into $HOME/.attic/keys or
        $ATTIC_KEYS_DIR for key files matching the given Borg
        repository.

        it is expected to raise an exception (KeyfileNotFoundError) if
        no key is found. whether that exception is from Borg or Attic
        is unclear.

        this is split in a separate function in case we want to use
        the attic code here directly, instead of our local
        implementation.)AtticKeyfileKeyfind_key_filer   r   r   r   r0   r   s   
z*AtticRepositoryUpgrader.find_attic_keyfilec                 C   s   t d|   t| }| }W d   n1 sw   Y  |tjtjd}tj	
t tj	| } t d|   |sZt| d}|| W d   dS 1 sSw   Y  dS dS )a  convert key files from attic to borg

        replacement pattern is `s/ATTIC KEY/BORG_KEY/` in
        `get_keys_dir()`, that is `$ATTIC_KEYS_DIR` or
        `$HOME/.attic/keys`, and moved to `$BORG_KEYS_DIR` or
        `$HOME/.config/borg/keys`.

        no need to decrypt to convert. we need to rewrite the whole
        key file because magic string length changed, but that's not a
        problem because the keyfiles are small (compared to, say,
        all the segments).zconverting keyfile %sNr   zwriting borg keyfile to %sr<   )r'   r(   r>   rO   replacerT   FILE_IDr   r+   r$   r-   r   basenamer?   )r9   r!   fdatar   r   r   r2      s   

"z(AtticRepositoryUpgrader.convert_keyfilesc                 C   sd   |   }|du rtd| j  dS tj| jd| }td|  |s0tj|dd|d dS dS )a  convert some repo files

        those are all hash indexes, so we need to
        `s/ATTICIDX/BORG_IDX/` in a few locations:

        * the repository index (in `$ATTIC_REPO/index.%d`, where `%d`
          is the `Repository.get_index_transaction_id()`), which we
          should probably update, with a lock, see
          `Repository.open()`, which i'm not sure we should use
          because it may write data on `Repository.close()`...
        Nz%no index file found for repository %szindex.%dzconverting repo index %s   ATTICIDX   BORG_IDXrE   )	Zget_index_transaction_idr'   r1   r$   r+   r-   r(   r   rJ   )r   r!   r"   Ztransaction_idindexr   r   r   r4      s   z*AtticRepositoryUpgrader.convert_repo_indexc                    s   t jdt jt dd t j | j t jt | j fdd}t j rZt js8t 	 dD ]}|| q:dD ]}||}t
d|  sYt|d	d
 qCdS dS )a  convert caches from attic to borg

        those are all hash indexes, so we need to
        `s/ATTICIDX/BORG_IDX/` in a few locations:

        * the `files` and `chunks` cache (in `$ATTIC_CACHE_DIR` or
          `$HOME/.cache/attic/<repoid>/`), which we could just drop,
          but if we'd want to convert, we could open it with the
          `Cache.open()`, edit in place and then `Cache.close()` to
          make sure we have locking right
        ZATTIC_CACHE_DIRz.cacheZatticc                    s   t j | }t j|r7t j| }t j|r"td| |S td| d|  s5t|| |S td|  d|  dS )a  copy the given attic cache path into the borg directory

            does nothing if dryrun is True. also expects
            attic_cache_dir and borg_cache_dir to be set in the parent
            scope, to the directories path including the repository
            identifier.

            :params path: the basename of the cache file to copy
            (example: "files" or "chunks") as a string

            :returns: the borg file that was created or None if no
            Attic cache file was found.

            z<borg cache file already exists in %s, not copying from Atticzcopying attic cache file from z to zno z cache file found in N)	r+   r$   r-   existsr'   r1   r(   r)   copyfile)r$   Z
attic_fileZ	borg_fileZattic_cache_dirZborg_cache_dirr!   r   r   copy_cache_file   s   z>AtticRepositoryUpgrader.convert_cache.<locals>.copy_cache_file)configfiles)chunkszconverting cache %sr\   r]   N)r+   environgetr$   r-   r   id_strr	   r_   makedirsr'   r(   r   rJ   )r   r!   rb   cacher   ra   r   r3      s*   


	z%AtticRepositoryUpgrader.convert_cacheTFF)T)__name__
__module____qualname__r   r:   r6   staticmethodr5   rJ   r0   r2   r4   r3   __classcell__r   r   r   r   r      s    
(
r   c                   @   s,   e Zd ZdZdZedd Zedd ZdS )rT   z*backwards compatible Attic key file parserz	ATTIC KEYc                   C      t jdt jt ddS )z,Determine where to repository keys and cacheZATTIC_KEYS_DIRz.attickeysr+   rf   rg   r$   r-   r   r   r   r   r   r      s   zAtticKeyfileKey.get_keys_dirc              	   C   s   | j }| }tj|st|j|t|D ]?}tj||}t|*}| 	 }|rG|
| jrG|dd |jkrG|W  d     S W d   n1 sQw   Y  qt|j|)aw  copy of attic's `find_key_file`_

        this has two small modifications:

        1. it uses the above `get_keys_dir`_ instead of the global one,
           assumed to be borg's

        2. it uses `repository.path`_ instead of
           `repository._location.canonical_path`_ because we can't
           assume the repository has been opened by the archiver yet
        
   N)r   r+   r$   r_   r   listdirr-   r>   readlinestrip
startswithrX   rh   cls
repositoryr   Zkeys_dirnamer   rA   liner   r   r   rU      s   
" zAtticKeyfileKey.find_key_fileN)	rl   rm   rn   __doc__rX   ro   r   classmethodrU   r   r   r   r   rT      s    
rT   c                   @   s&   e Zd Zd
ddZdd Zdd Zd	S )BorgRepositoryUpgraderTFc              	   C   sz   t d | , z|  }W n ty   t d Y nw | || W d   dS W d   dS 1 s6w   Y  dS )zDconvert an old borg repository to a current borg repository
        z$converting borg 0.xx to borg currentr    N)r'   r(   find_borg0xx_keyfiler   r1   move_keyfiles)r   r!   r"   r#   r9   r   r   r   r:     s   
"zBorgRepositoryUpgrader.upgradec                 C   rS   N)Borg0xxKeyfileKeyrU   rV   r   r   r   r   %  s   
z+BorgRepositoryUpgrader.find_borg0xx_keyfilec                 C   sD   t j|}t jt |}z	t || W d S  ty!   Y d S w r   )r+   r$   rY   r-   r   rP   FileExistsError)r   r9   r!   r   Znew_keyfiler   r   r   r   (  s   z$BorgRepositoryUpgrader.move_keyfilesNrk   )rl   rm   rn   r:   r   r   r   r   r   r   r     s    
r   c                   @   s(   e Zd ZdZedd Zedd ZdS )r   z.backwards compatible borg 0.xx key file parserc                   C   rq   )NZBORG_KEYS_DIRz.borgrr   rs   r   r   r   r   r   5  s   zBorg0xxKeyfileKey.get_keys_dirc              	   C   s   | j }| }tj|st|j|t|D ]D}tj||}t|/}| 	 }|rL|
| jrL|t| jd d  |jkrL|W  d      S W d    n1 sVw   Y  qt|j|)Nr   )r   r+   r$   r_   r   ru   r-   r>   rv   rw   rx   rX   rF   rh   ry   r   r   r   rU   :  s   
, zBorg0xxKeyfileKey.find_key_fileN)rl   rm   rn   r~   ro   r   r   rU   r   r   r   r   r   2  s    
r   )r%   r+   r)   rH   Z
crypto.keyr   r   	constantsr   helpersr   r   r   r	   Zlockingr
   r'   r   r{   r   r   rl   rK   r   rT   r   r   r   r   r   r   <module>   s$     a%