o nnhP@sPddlmZdZdZdZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlZddlmZddlmZddlmZmZmZdd lmZdd lmZdd lmZdd lm Z zdd l!m"Z"Wne#ydd l$m"Z"Ynwzddlm%Z%Wne#yddlm&Z%Ynwe 'dZ(e 'dZ)e 'dZ*e 'dZ+e 'dZ,e 'dZ-e 'dZ.e 'dZ/e 'dZ0e 'dZ1ddZ2ddZ3Gddde4Z5Gd d!d!e6Z7Gd"d#d#e6Z8Gd$d%d%e8Z9Gd&d'd'e8Z:Gd(d)d)e8Z;Gd*d+d+e8Zd0d1Z?e@d2kr&e?dSdS)3) annotationsa --- module: unarchive version_added: '1.4' short_description: Unpacks an archive after (optionally) copying it from the local machine description: - The M(ansible.builtin.unarchive) module unpacks an archive. It will not unpack a compressed file that does not contain an archive. - By default, it will copy the source file from the local system to the target before unpacking. - Set O(remote_src=yes) to unpack an archive which already exists on the target. - If checksum validation is desired, use M(ansible.builtin.get_url) or M(ansible.builtin.uri) instead to fetch the file and set O(remote_src=yes). - For Windows targets, use the M(community.windows.win_unzip) module instead. options: src: description: - If O(remote_src=no) (default), local path to archive file to copy to the target server; can be absolute or relative. If O(remote_src=yes), path on the target server to existing archive file to unpack. - If O(remote_src=yes) and O(src) contains V(://), the remote machine will download the file from the URL first. (version_added 2.0). This is only for simple cases, for full download support use the M(ansible.builtin.get_url) module. type: path required: true dest: description: - Remote absolute path where the archive should be unpacked. - The given path must exist. Base directory is not created by this module. type: path required: true copy: description: - If true, the file is copied from local controller to the managed (remote) node, otherwise, the plugin will look for src archive on the managed machine. - This option has been deprecated in favor of O(remote_src). - This option is mutually exclusive with O(remote_src). type: bool default: yes creates: description: - If the specified absolute path (file or directory) already exists, this step will B(not) be run. - The specified absolute path (file or directory) must be below the base path given with O(dest). type: path version_added: "1.6" io_buffer_size: description: - Size of the volatile memory buffer that is used for extracting files from the archive in bytes. type: int default: 65536 version_added: "2.12" list_files: description: - If set to True, return the list of files that are contained in the tarball. type: bool default: no version_added: "2.0" exclude: description: - List the directory and file entries that you would like to exclude from the unarchive action. - Mutually exclusive with O(include). type: list default: [] elements: str version_added: "2.1" include: description: - List of directory and file entries that you would like to extract from the archive. If O(include) is not empty, only files listed here will be extracted. - Mutually exclusive with O(exclude). type: list default: [] elements: str version_added: "2.11" keep_newer: description: - Do not replace existing files that are newer than files from the archive. type: bool default: no version_added: "2.1" extra_opts: description: - Specify additional options by passing in an array. - Each space-separated command-line option should be a new element of the array. See examples. - Command-line options with multiple elements must use multiple lines in the array, one for each element. type: list elements: str default: [] version_added: "2.1" remote_src: description: - Set to V(true) to indicate the archived file is already on the remote system and not local to the Ansible controller. - This option is mutually exclusive with O(copy). type: bool default: no version_added: "2.2" validate_certs: description: - This only applies if using a https URL as the source of the file. - This should only set to V(false) used on personally controlled sites using self-signed certificate. - Prior to 2.2 the code worked as if this was set to V(true). type: bool default: yes version_added: "2.2" extends_documentation_fragment: - action_common_attributes - action_common_attributes.flow - action_common_attributes.files - decrypt - files attributes: action: support: full async: support: none bypass_host_loop: support: none check_mode: support: partial details: Not supported for gzipped tar files. diff_mode: support: partial details: Uses gtar's C(--diff) arg to calculate if changed or not. If this C(arg) is not supported, it will always unpack the archive. platform: platforms: posix safe_file_operations: support: none vault: support: full todo: - Re-implement tar support using native tarfile module. - Re-implement zip support using native zipfile module. notes: - Requires C(zipinfo) and C(gtar)/C(unzip) command on target host. - Requires C(zstd) command on target host to expand I(.tar.zst) files. - Can handle I(.zip) files using C(unzip) as well as I(.tar), I(.tar.gz), I(.tar.bz2), I(.tar.xz), and I(.tar.zst) files using C(gtar). - Does not handle I(.gz) files, I(.bz2) files, I(.xz), or I(.zst) files that do not contain a I(.tar) archive. - Existing files/directories in the destination which are not in the archive are not touched. This is the same behavior as a normal archive extraction. - Existing files/directories in the destination which are not in the archive are ignored for purposes of deciding if the archive should be unpacked or not. seealso: - module: community.general.archive - module: community.general.iso_extract - module: community.windows.win_unzip author: Michael DeHaan au - name: Extract foo.tgz into /var/lib/foo ansible.builtin.unarchive: src: foo.tgz dest: /var/lib/foo - name: Unarchive a file that is already on the remote machine ansible.builtin.unarchive: src: /tmp/foo.zip dest: /usr/local/bin remote_src: yes - name: Unarchive a file that needs to be downloaded (added in 2.0) ansible.builtin.unarchive: src: https://example.com/example.zip dest: /usr/local/bin remote_src: yes - name: Unarchive a file with extra options ansible.builtin.unarchive: src: /tmp/foo.zip dest: /usr/local/bin extra_opts: - --transform - s/^xxx/yyy/ ab dest: description: Path to the destination directory. returned: always type: str sample: /opt/software files: description: List of all the files in the archive. returned: When O(list_files) is V(True) type: list sample: '["file1", "file2"]' gid: description: Numerical ID of the group that owns the destination directory. returned: always type: int sample: 1000 group: description: Name of the group that owns the destination directory. returned: always type: str sample: "librarians" handler: description: Archive software handler used to extract and decompress the archive. returned: always type: str sample: "TgzArchive" mode: description: String that represents the octal permissions of the destination directory. returned: always type: str sample: "0755" owner: description: Name of the user that owns the destination directory. returned: always type: str sample: "paul" size: description: The size of destination directory in bytes. Does not include the size of files or subdirectories contained within. returned: always type: int sample: 36 src: description: - The source archive's path. - If O(src) was a remote web URL, or from the local ansible controller, this shows the temporary location where the download was stored. returned: always type: str sample: "/home/paul/test.tar.gz" state: description: State of the destination. Effectively always "directory". returned: always type: str sample: "directory" uid: description: Numerical ID of the user that owns the destination directory. returned: always type: int sample: 1000 N)partial)ZipFile)to_bytes to_nativeto_text) AnsibleModule) get_bin_path)get_best_parsable_locale) fetch_file)quote) BadZipFile) BadZipfilez: Uid differs$z: Gid differs$z: Mode differs$z: Mod time differs$z4: : Warning: Cannot stat: No such file or directory$z2: Warning: Cannot stat: No such file or directory$z([r-][w-][SsTtx-]){3}z: Invalid ownerz: Invalid groupz: Symlink differs$cCsftd}t|d}tt|j|dD]}t||}qWd|d@S1s*wY|d@S)z# Return a CRC32 checksum of a file rbNl)binasciicrc32openiterrread)path buffer_sizecrcfb_blockrD/usr/local/lib/python3.10/dist-packages/ansible/modules/unarchive.pyr s   rcCstdd|S)z6 Quote meta-characters in the args for the unix shell z([^A-Za-z0-9_])z\\\1)resub)stringrrr shell_escape*sr c@s eZdZdS)UnarchiveErrorN)__name__ __module__ __qualname__rrrrr!/sr!c@sPeZdZddZddZddZddZed d Zd d Z d dZ ddZ dS) ZipArchivecCsz||_||_||_|jd|_||_|jd|_|jd|_g|_|jjd|_ d|_ d|_ g|_ t |_d|_d|_dS)N extra_optsio_buffer_sizeexcludeinclude)unzipcmd_path)zipinfozipinfo_cmd_path)srcb_dest file_argsparamsoptsmoduler'excludesincludes include_filesr-r/_files_in_archivedict _infodict zipinfoflagbinariesselfr0r1r2r5rrr__init__5s    zZipArchive.__init__cCsd|ddd}d}tddD]}tddD]}||d|dvr+|d|d|7}qq||@S)zA Convert a Unix permission string (rw-r--r--) into a mode (0644) Nr)rwxst)range)r?modestrumaskrevstrmodejirrr_permstr_to_octalIs zZipArchive._permstr_to_octalcCs|j|jd|jg\}}}|r|j|td|j|ddD]}|dd}|j |dt |d|j |d<q%dS)Nz-vz,Neither python zipfile nor unzip can read %srB) r5 run_commandr-r0debugr! splitlinessplitr9appendintr;)r?rcouterrlinefieldsrrr_legacy_file_listVs  zZipArchive._legacy_file_listc Cs|jr|j|Szt|j}Wn"ty1}z|jddr&|nWYd}~n'd}~wwz|D] }t |j |j|j <q7Wnt yS| tdw|j|S)Nrbad magic numberz#Unable to list files in the archive)r;rr0r argslower startswithr_infolistrYCRCfilename Exceptioncloser!)r?rarchiveeitemrrr_crc32as(      zZipArchive._crc32c Cs*|jr|jSg|_zt|j}Wn$ty4}z|jddr'|nWYd}~|jSd}~wwz@|D]9}|j rT|j D]}t ||rR|j t |qBq:d}|j ri|j D] }t ||rhd}nq\|ss|j t |q:Wnty}z |tdt |d}~ww||jS)Nrr`FT'Unable to list files in the archive: %s)r9rr0r rarbrcr_namelistr8fnmatchrXrr6rgrhr!)r?rirjmemberr) exclude_flagr(rrrfiles_in_archivexsN       zZipArchive.files_in_archivec,CsD |jr|j|jdd|jg}n|jdd|jg}|jr"|dg|j|jr+||j|j|\}}}|j||}d}d}|dkrGd}nd}t d}t |t } t } t } t } zt| j} Wn ttfyx| } Ynwzt| j}Wntttfy| }Ynwd}}|jdrz t|jd}Wn'tyz tt|jd}Wntttfyt| }YnwYnw|j}|j}nz| }Wn tyYnw| }d}}|jd r)z t|jd }Wn+ttfy!z tt|jd }Wntttfyt| }YnwYnw|j}|j }nz|}Wn ty7Ynw| }|!D]}d}|"dd }t#|d krRq>t#|dd vr]q>t#|d dkrhq>|dddvst$|ddd%dsq>|dd}|ddd}|d}|d}t|d}t&|d dd}||jvr|d|7}q>|ddkr|dkr|d||f7}d}n|dkrd}n|dkrd}n|dkrd}t#|d kr|ddkrd }n |d!krd }nd"}|}n t#|d kr|d#krd }nd"}|}n d$| 'vr|}nd}t#|d%ks,t()|s2t*d&|t j+,|j-t.|dd} zt /| }!Wn!tyfd}|j01||d'|7}|d(||f7}Yq>w|dkrt23|!j4sd}|j01||d)|7}|d*||f7}q>|dkrt25|!j4sd}d}|j01||d+|7}|d*||f7}q>|dkrt26|!j4sd}|j01||d,|7}|d*||f7}q>t7d-|}"t8j8t9:|d d.dd }#t9;|#<}$t25|!j4rP|jj=d/r4|$|!j>krd}|j01||d0|7}d1|"d2<n8t25|!j4r3|$|!j>kr3|d3|7}|j1|q>n|$|!j>krPd}|j01||d4||$|!j>f7}d1|"d2<t25|!j4rm||!j?krmd}|d5|||!j?f7}d6|"d<t25|!j4rt@| |jA}%|%|B|krd}|d7||B||%f7}d8|"d<|dkr(|jd9rtC|jd9tr|jd9}&n^z t|jd9d }&WnSty}'z4z tDE|!|jd9}&Wn"ty}'z|jjF|d:tG|'tHId;WYd}'~'nd}'~'wwWYd}'~'nd}'~'ww|dkr|J|d}&n|J||}&|&t2K|!j4kr(d}d<|"d=<|d>||&t2K|!j4f7}d}(})z t|!jLj}(WnttfyD|!jL})Ynw| dkr]|| ksT|| kr]t*d?||| f|(ru|(|krud}|d@||(|f7}dA|"d <n|)r|)|krd}|dB||)|f7}dA|"d <d}*}+z t|!jMj}*Wntttfy|!jM}+Ynw| dkr||ks|| kr|| vrt*dC||| f|*r|*|krd}|dD||*|f7}dE|"d <n|+r|+|krd}|dF||+|f7}dE|"d <|r||j0vr|j01||dGd,|"|f7}q>|j0rd}tN||||||dHS)INz-Tz-s-xr*rTFownergrouprR)rRrv rSzdl-?zrwxstah-rHrBsurrogate_or_stricterrorszPath %s is excluded on request rA/dz8Path %s incorrectly tagged as "%s", but is a directory. lL-r? rwxrwxrwxzrwx---z rw-rw-rw-zrwxa---bsd z"ZIP info perm format incorrect, %szPath %s is missing z>%s++++++.?? %s z/File %s already exists, but not as a directory zc%s++++++.?? %s z7Directory %s already exists, but not as a regular file z2Directory %s already exists, but not as a symlink z .%s.......??z %Y%m%d.%H%M%S keep_newerz!File %s is older, replacing file rGz!File %s is newer, excluding file z$File %s differs in mtime (%f vs %f) z#File %s differs in size (%d vs %d) rFz5File %s differs in CRC32 checksum (0x%08x vs 0x%08x) crMz%s)rmsg exceptionpz*Path %s differs in permissions (%o vs %o) z/Cannot change ownership of %s to %s, as user %sz8Path %s is owned by user %s, not by user %s as expected oz6Path %s is owned by uid %s, not by uid %s as expected z5Cannot change group ownership of %s to %s, as user %sz:Path %s is owned by group %s, not by group %s as expected gz6Path %s is owned by gid %s, not by gid %s as expected z%s %s ) unarchivedrZr[r\cmddiff)Or<r/r0r6extendr8r5rTrUosrKplatformsystem getgroupsgetuidgetgidpwdgetpwuidpw_name TypeErrorKeyErrorgrpgetgrgidgr_name ValueError OverflowErrorr2getpwnamrYpw_uidrggetgrnamgr_gidrVrWlen frozensetissubsetrrbZIP_FILE_MODE_REmatchr!rjoinr1rlstatr7rXstatS_ISDIRst_modeS_ISREGS_ISLNKlistdatetimetimestrptimemktime timetupler3st_mtimest_sizerr'rl isinstancer_symbolic_mode_to_octal fail_jsonr traceback format_excrPS_IMODEst_uidst_gidr:),r?rrZr[r\old_outrrrK systemtypegroupsrun_uidrun_gid run_owner run_group fut_ownerfut_uidtpw fut_groupfut_gidtgrr]changepcsztypepermstrversionostypesizerftype file_umaskr1stitemized dt_object timestamprrMrjrtuidrugidrrr is_unarchiveds         .                                 *     (   zZipArchive.is_unarchivedcCs|jdg}|jr||j||j|jr |dg|j|jr)||j|d|jg|j |\}}}t ||||dS)Nz-orsz-drrZr[r\) r-r4rrXr0r6r8r1r5rTr:)r?rrZr[r\rrr unarchives    zZipArchive.unarchivec Csg}|jD] }zt||dt|dWqty%||dYqw|r3ddjd|dfS|jd|jg}|j |\}}}|dkrIdS|j |dd |j|ffS) NryrFz7Unable to find required '{missing}' binary in the path.z' or ')missingz-lTN)Command "%s" could not handle archive: %s) r=setattrr rrXformatrr-r0r5rTrU)r?rbrrZr[r\rrrcan_handle_archives   zZipArchive.can_handle_archiveN) r"r#r$r@rPr_rlpropertyrrrrrrrrrr%3s   %> r%c@s@eZdZddZddZeddZddZd d Zd d Z d S) TgzArchivecCs||_||_||_|jd|_||_|jjr"|jjdd|jjddd|jjdD|_ |jjd|_ d|_ d|_ d |_ g|_dS) Nr&Tz>remote module (%s) does not support check mode when using gtar)skippedrcSsg|]}|dqS)r})rstrip).0rrrr sz'TgzArchive.__init__..r(r)z-z)r0r1r2r3r4r5 check_mode exit_json_namer6r8r-tar_typezipflagr9r>rrrr@s  zTgzArchive.__init__cCsL|jdg}|j|\}}}d}|drd}|S|dr$d|vr$d}|S)Nz --versionbsdtarrtarGNUgnu)r-r5rTrc)r?rrZr[r\rrrr _get_tar_types  zTgzArchive._get_tar_typec CsN|jr|jS|jdd|jg}|jr||j|jr#|dg|j|jr1|dd|jD|d|jg|j rB||j t |j }|j j ||jt ||||dd\}}}|d krj|j |td ||D]5}tt|d }|d r|d d}d }|jr|jD] }t||rd}nq|s|jt|qn|jS)Nz--list-C--show-transformed-namescSg|]}d|qSz --exclude=rrrrrrr&z/TgzArchive.files_in_archive..-fLANGLC_ALL LC_MESSAGESLANGUAGEcwdenviron_updaterrmr}ryFT)r9r-r1rrXr4rr6r0r8r r5rTr:rUr!rVrcodecs escape_decodercro) r?rlocalerZr[r\rfrqr(rrrrrs@   &       zTgzArchive.files_in_archivec Cs4|jdd|jg}|jr||j|jr|dg|j|jdr.|dt|jd|jdr?|dt|jd|jj drJ|d |j rX|d d |j D|d |j g|j ri||j t |j}|jj||jt||||d d\}}}d}|}d}t}||D]x} t| rq|dkr|jdst| r|| d7}|dkr|jdst| r|| d7}|jdst| r|| d7}t| r|| d7}t| r|| d7}t| r|| d7}t| r|| d7}t| r || d7}q|rd}t|||||dS)Nz--diffrrrt--owner=ru--group=r--keep-newer-filescSrrrrrrrrUrz,TgzArchive.is_unarchived..rrrTr*r rMF)rrZr[r\r)r-r1rrXr4rr2r r5r3r6r0r8r rTr:rrrV EMPTY_FILE_REsearch OWNER_DIFF_RE GROUP_DIFF_RE MODE_DIFF_REMOD_TIME_DIFF_REMISSING_FILE_REINVALID_OWNER_REINVALID_GROUP_RESYMLINK_DIFF_RE) r?rr rZr[r\rrrr]rrrrHsZ       &              zTgzArchive.is_unarchivedc Cs|jdd|jg}|jr||j|jr|dg|j|jdr.|dt|jd|jdr?|dt|jd|jj drJ|d |j rX|d d |j D|d |j g|j ri||j t |j}|jj||jt||||d d\}}}t||||dS)Nz --extractrrrtr rur rr cSrrrrrrrrrz(TgzArchive.unarchive..rrrr)r-r1rrXr4rr2r r5r3r6r0r8r rTr:)r?rr rZr[r\rrrrs&       &zTgzArchive.unarchivec Csztd|_Wnty#ztd|_Wn ty YYdSwYnw||_|jdkr8dd|j|jffSz|jr?WdSWnty]}zdd|jt|ffWYd}~Sd}~wwdd |jfS) Ngtarr)Fz:Unable to find required 'gtar' or 'tar' binary in the pathrFz7Command "%s" detected as tar type %s. GNU tar required.rrzNCommand "%s" found no files in archive. Empty archive files are not supported.)r r-rrrrrr!r)r?rjrrrrs,    "zTgzArchive.can_handle_archiveN) r"r#r$r@rrrrrrrrrrrrs ,8 rceZdZfddZZS) TarArchivec tt|||||d|_dS)Nr*)superrr@rr> __class__rrr@s zTarArchive.__init__r"r#r$r@ __classcell__rrrrrrcr)TarBzipArchivecr)Nz-j)rr#r@rr>rrrr@ zTarBzipArchive.__init__r rrrrr#r"r#cr) TarXzArchivecr)Nz-J)rr%r@rr>rrrr@r$zTarXzArchive.__init__r rrrrr%r"r%cr)TarZstdArchivecr)Nz--use-compress-program=zstd)rr&r@rr>rrrr@s zTarZstdArchive.__init__r rrrrr&r"r&cs(eZdZfddZfddZZS) ZipZArchivecs&tt|||||d|_d|_dS)Nz-Zl)r+)r,r/)rr'r@r<r=r>rrrr@s zZipZArchive.__init__csXtt|\}}|s||fS|j|jg}|j|\}}}d|vr&dSdd|fS)Nr.rFz/Command "unzip -Z" could not handle archive: %s)rr'rr/r<r5rTrb)r?unzip_available error_msgrrZr[r\rrrrs   zZipZArchive.can_handle_archive)r"r#r$r@rr!rrrrr's  r'c Csttttttttg}t}|D]}|||||}|\}} |r#|S| | qd |} |j d|| fddS)NrzdFailed to find handler for "%s". Make sure the required command to extract the file is installed. %sr) r%r'rrr#r%r&setraddrr) r0destr2r5handlersreasonshandlerobj can_handlereason reason_msgrrr pick_handlers   r5cCsXtttdddtdddtdddtddtdddtdddtdd gd tdd gd tdd gd tdddtd d dtdddtdddd dddgd}|jd}|jd}tj|}t|dd}tj|st|dj ||d|jd}| |j}tj |s|s|j d|dnd|vrt ||}n|j d|dt|tjs|j d|dtj|}ztj|dkr|j d|dWnty}z|j d|t|fdWYd}~nd}~wwtj|s|j d|dt||||}t|jj||d } |} |jr| d! | d"<nF| d!r d| d"<n|j d/d%d&||fi| WntyW|j d/d%d&||fi| Ynwd| d"<| d'drkd(| d'i| d'<| d'dr|jsg} |jD]X} tj|t| dd|d<z|j|| d"dd)| d"<Wn#ttfy}z|j d/d%d*t|i| WYd}~nd}~wwd+| vr| d+d} | | vr| !| q{| r| D]=}d,||f|d<z|j|| d"dd)| d"<Wqttfy}z|j d/d%d*t|i| WYd}~qd}~ww|jd-r"|j| d.<|j"d/i| dS)0NrT)typerequiredboolF)r6default)r6rstr)r6elementsr9rYi) r0r- remote_srccreates list_filesrr(r)r&validate_certsr'copydecrypt)r)r() argument_specadd_file_common_argssupports_check_modemutually_exclusiver0r-rzr{zNRelative destination path '{dest}' was resolved to absolute path '{abs_dest}'.)r-abs_destr<zSource '%s' failed to transferr*z://zSource '%s' does not existzSource '%s' not readablerz)Invalid archive '%s', the file is 0 byteszSource '%s' not readable, %sz#Destination '%s' is not a directory)r0r-r0rchangedextract_resultsrZrzfailed to unpack %s to %srprepared)expandz1Unexpected error when accessing exploded file: %sr}z%s/%sr>filesr)#rr:r3rrabspathrisabswarnrload_file_common_argumentsexistsrr accessR_OKgetsizergrisdirr5rr"rrrIOErrorgetrrrset_fs_attributes_if_differentOSErrorrWrXr)r5r0r-rFr1r<r2rjr0res_args check_results top_foldersrftop_folder_pathrrrrmains                      $      (   *  r]__main__)A __future__r DOCUMENTATIONEXAMPLESRETURNrrrrorrrrrrrr functoolsrzipfiler+ansible.module_utils.common.text.convertersrrransible.module_utils.basicr#ansible.module_utils.common.processr "ansible.module_utils.common.localer ansible.module_utils.urlsr shlexr ImportErrorpipesr rcompilerrrrrrrrrrrr rgr!objectr%rrr#r%r&r'r5r]r"rrrrs~ <                   P1