o
    +ke:=                     @   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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 e ZejejB ejB d	fd
dZdd Zdd ZdBddZdd Zdd Zdd Zdd ZedZdd Zdd Zdd  Z dd!d"d#Z!d$d% Z"d&d' Z#d(d) Z$d*d+ Z%e%d,d-d.Z&e&e%d/B Z'e&Z(e&e%d0d/B Z)e)e%d1B Z*e%d2d.d/Z+dddd3d4d5d6Z,dddd3d7d8d9Z-d:d; Z.d dlZ/d dlZ0d dl Z1d d<l2m3Z3m4Z4m5Z5m6Z6m7Z7 dCd>d?Z8dDd@dAZ9dS )E    N   )Error)prepare_subprocess_env   )is_win32)*)create_loggerTc              
   C   sB   zt j| |dd W dS  ty  } z	|rtt| d}~ww )a  
    Ensures that the dir exists with the right permissions.
    1) Make sure the directory exists in a race-free operation
    2) If mode is not None and the directory has been created, give the right
    permissions to the leaf directory. The current umask value is masked out first.
    3) If pretty_deadly is True, catch exceptions, reraise them with a pretty
    message.
    Returns if the directory has been created and has the right permissions,
    An exception otherwise. If a deadly exception happened it is reraised.
    T)modeexist_okN)osmakedirsOSErrorr   str)pathr	   Zpretty_deadlye r   3usr/lib/python3.10/site-packages/borg/helpers/fs.py
ensure_dir   s   r   c                  C   s:   t jdpt jd} | st jdt jdd } | S )zGet home directory / base directory for borg:

    - BORG_BASE_DIR, if set
    - HOME, if set
    - ~$USER, if USER is set
    - ~
    BORG_BASE_DIRHOMEz~%sZUSER )r   environgetr   
expanduser)base_dirr   r   r   get_base_dir)   s   r   c                  C   s0   t jd} | du rt jt d} t|  | S ),Determine where to repository keys and cacheZBORG_KEYS_DIRNkeysr   r   r   r   joinget_config_dirr   )Zkeys_dirr   r   r   get_keys_dir;   s
   r!   c                 C   sB   t jd}|du rt jt d}| rt j|| }t| |S )z4Determine where to store local security information.ZBORG_SECURITY_DIRNZsecurityr   )Zrepository_idZsecurity_dirr   r   r   get_security_dirE   s   r"   c                  C   s   t jt d} t jdst jd| } t jdt j| d}t| t j|t}t j|s^t	t
dd }dd	lm} ||d
d}|| W d   |S 1 sYw   Y  |S )r   z.cacher   XDG_CACHE_HOMEZBORG_CACHE_DIRborgz
        # This file is a cache directory tag created by Borg.
        # For information about cache directory tags, see:
        #       http://www.bford.info/cachedir/spec.html
        asciir   )SaveFileT)binaryN)r   r   r   r   r   r   r   CACHE_TAG_NAMEexistsCACHE_TAG_CONTENTStextwrapdedentencodeplatformr&   write)Z
cache_homeZ	cache_dirZcache_tag_fnZcache_tag_contentsr&   fdr   r   r   get_cache_dirQ   s"   

r1   c                  C   sN   t jt d} t jdst jd| } t jdt j| d}t| |S )z%Determine where to store whole configz.configr   XDG_CONFIG_HOMEZBORG_CONFIG_DIRr$   )r   r   r   r   r   r   r   )Zconfig_homeZ
config_dirr   r   r   r    i   s   r    c                 C   s   t j| t}z8t j|r=t|d}|tt}|tkr)	 W d   W dS W d   W dS 1 s5w   Y  W dS W dS  t	yI   Y dS w )zDetermines whether the specified path is a cache directory (and
    therefore should potentially be excluded from the backup) according to
    the CACHEDIR.TAG protocol
    (http://www.bford.info/cachedir/spec.html).
    rbNTF)
r   r   r   r(   r)   openreadlenr*   r   )r   tag_pathZtag_fileZtag_datar   r   r   dir_is_cachedirw   s&   r8   c                 C   sT   g }|rt | r|t |dur(|D ]}tj| |}tj|r'|| q|S )a  Determines whether the specified path is excluded by being a cache
    directory or containing user-specified tag files/directories. Returns a
    list of the names of the tag files/directories (either CACHEDIR.TAG or the
    matching user-specified files/directories).
    N)r8   appendr(   r   r   r   r)   )r   Zexclude_cachesZexclude_if_presentZ	tag_namestagr7   r   r   r   dir_is_tagged   s   

r;   z^((\.\.)?/+)+c                 C   s   t d| pdS )z3Make path safe by making it relative and local
    r   .)_safe_resub)r   r   r   r   make_path_safe   s   r?   c                 C   s(   t | pt | pt | pt | S )z7return True if we support hardlinked items of this type)statS_ISREGS_ISBLKS_ISCHRS_ISFIFO)r	   r   r   r   hardlinkable   s   (rE   c              
   C   sN   zd|   fW S  ty& } ztd| j| d| jfW  Y d }~S d }~ww )Nr   z&scandir_inorder: Unable to stat %s: %sr   )inoder   loggerdebugr   name)Zdirentr   r   r   r   scandir_keyfunc   s   rJ   )r0   c                 C   s"   |d ur|n| }t t|tdS )N)key)sortedr   scandirrJ   )r   r0   argr   r   r   scandir_inorder   s   rO   c                C   s|   t | d*}t| }|jdkr|s(|t|j |  t	|  W d   n1 s2w   Y  t
|  dS )a  Attempt to securely erase a file by writing random data over it before deleting it.

    If avoid_collateral_damage is True, we only secure erase if the total link count is 1,
    otherwise we just do a normal "delete" (unlink) without first overwriting it with random.
    This avoids other hardlinks pointing to same inode as <path> getting damaged, but might be less secure.
    A typical scenario where this is useful are quick "hardlink copies" of bigger directories.

    If avoid_collateral_damage is False, we always secure erase.
    If there are hardlinks pointing to the same inode as <path>, they will contain random garbage afterwards.
    r+br   N)r4   r   r@   filenost_nlinkr/   urandomst_sizeflushfsyncunlink)r   Zavoid_collateral_damager0   str   r   r   secure_erase   s   rY   c                 C   s   zt |  W dS  tyW } zC|jtjkr t | }|jdkr" zt| d}|  W d   n1 s7w   Y  W n	 tyF   |w t |  W Y d}~dS d}~ww )a  
    Safely unlink (delete) *path*.

    If we run out of space while deleting the file, we try truncating it first.
    BUT we truncate only if path is the only hardlink referring to this content.

    Use this when deleting potentially large files when recovering
    from a VFS error such as ENOSPC. It can help a full file system
    recover. Refer to the "File system interaction" section
    in repository.py for further explanations.
    r   rP   N)	r   rW   r   errnoENOSPCr@   rR   r4   truncate)r   Z
unlink_errrX   r0   r   r   r   safe_unlink   s&   


r]   c                 C   sD   d|vsJ | dkrd|v rt jnt j}d|v r|jS |S t| |S )N+-rb)sysstdinstdoutbufferr4   )r   r	   streamr   r   r   	dash_open   s
   
rg   c                  G   s&   d}| D ]}|t td| dO }q|S )Nr   O_)getattrr   )flagsresultflagr   r   r   rh      s   rh   BINARYZNOCTTYZRDONLYZNOFOLLOWZNONBLOCKNOATIMEZ	DIRECTORYF)r   	parent_fdrI   noatimec              
   C   s   |r	|dur	|}n|d}}t rtj|rdS | }|ru|tdB }ztj|||d}W |S  tyD   ||kr9 tj|||d}Y |S  tyt }	 z%ddlm	}
 d|
v rh|	j
t
jkrh||krhtj|||d}n W Y d}	~	|S d}	~	ww tj|||d}|S )a  
    Use os.open to open a fs item.

    If parent_fd and name are given, they are preferred and openat will be used,
    path is not used in this case.

    :param path: full (but not necessarily absolute) path
    :param parent_fd: open directory file descriptor
    :param name: name relative to parent_fd
    :param flags: open flags for os.open() (int)
    :param noatime: True if access time shall be preserved
    :return: file descriptor
    Nrn   )dir_fdr   )workaroundsZretry_erofs)r   r   r   isdirrh   r4   PermissionErrorr   r   rr   rZ   ZEROFS)rj   r   ro   rI   rp   fnameZ_flags_normalZ_flags_noatimer0   excrr   r   r   r   os_open  s6   


rw   )r   ro   rI   follow_symlinksc                 C   s,   |r	|dur	|}n| d}}t j|||dS )aP  
    Use os.stat to open a fs item.

    If parent_fd and name are given, they are preferred and statat will be used,
    path is not used in this case.

    :param path: full (but not necessarily absolute) path
    :param parent_fd: open directory file descriptor
    :param name: name relative to parent_fd
    :return: stat info
    N)rq   rx   )r   r@   )r   ro   rI   rx   ru   r   r   r   os_stat:  s   
ry   c                 C   sF   t dd}ztjdd| g|dW S  ty"   tjd| g|d Y S w )NT)systemZ
fusermountz-u)envumount)r   
subprocesscallFileNotFoundError)Z
mountpointr{   r   r   r   r|   N  s   
r|   )_sanitize_params_get_candidate_namesTMP_MAX_text_openflags_bin_openflags  c              	   C   s   t j| } t }|tu rtt j|}ttD ]J}t	|}t j
| || | }	td|	 z	t |	||}
W n$ ty@   Y q ty[   t jdkrZt j| rZt | t jrZY q w |
|	f  S ttjd)z>Code common to mkstemp, TemporaryFile, and NamedTemporaryFile.ztempfile.mkstempntz#No usable temporary file name found)_osr   abspathr   bytesmapfsencoderanger   nextr   _sysauditr4   FileExistsErrorrt   rI   rs   accessW_OK_errnoEEXIST)dirpresufrj   output_typer	   namesseqrI   filer0   r   r   r   _mkstemp_inner_  s&   $r   c                 C   s4   t || |\}} }}|rt}nt}t||| |||S )a  User-callable function to create and return a unique temporary
    file.  The return value is a pair (fd, name) where fd is the
    file descriptor returned by os.open, and name is the filename.
    If 'suffix' is not None, the file name will end with that suffix,
    otherwise there will be no suffix.
    If 'prefix' is not None, the file name will begin with that prefix,
    otherwise a default prefix is used.
    If 'dir' is not None, the file will be created in that directory,
    otherwise a default directory is used.
    If 'text' is specified and true, the file is opened in text
    mode.  Else (the default) the file is opened in binary mode.
    If any of 'suffix', 'prefix' and 'dir' are not None, they must be the
    same type.  If they are bytes, the returned name will be bytes; str
    otherwise.
    The file is readable and writable only by the creating user ID.
    If the operating system uses permission bits to indicate whether a
    file is executable, the file is executable by no one. The file
    descriptor is not inherited by children of this process.
    Caller is responsible for deleting the file when done with it.
    )r   r   r   r   )suffixprefixr   textr	   r   rj   r   r   r   mkstemp_mode{  s
   r   )N)r   )NNNFr   ):rZ   r   os.pathrer@   r}   rb   r+   errorsr   processr   Zplatformflagsr   	constantsrG   r   S_IRWXUS_IRWXGS_IRWXOr   r   r!   r"   r1   r    r8   r;   compiler=   r?   rE   rJ   rO   rY   r]   rg   rh   Z
flags_baseZflags_specialZflags_special_followZflags_normalZflags_noatimeZ	flags_dirrw   ry   r|   r   r   r   tempfiler   r   r   r   r   r   r   r   r   r   r   <module>   s\    


$	.
