a )g'@s$ddlmZmZmZeZdZdZdZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlZddlZddlZddlZddlZddlmZddlmZmZddlmZmZmZdd lmZdd l m!Z!dd l"m#Z#dd l$m%Z%zdd l&m'Z'Wn e(ydd l)m'Z'Yn0e*dZ+e*dZ,e*dZ-e*dZ.e*dZ/e*dZ0e*dZ1e*dZ2e*dZ3ddZ4ddZ5Gddde6Z7Gddde8Z9Gdd d e8Z:Gd!d"d"e:Z;Gd#d$d$e:ZGd)d*d*e9Z?d+d,Z@d-d.ZAeBd/kr eAdS)0)absolute_importdivisionprint_functiona --- module: unarchive version_added: '1.4' short_description: Unpacks an archive after (optionally) copying it from the local machine description: - The C(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 C(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 C(remote_src=yes). - For Windows targets, use the M(community.windows.win_unzip) module instead. options: src: description: - If C(remote_src=no) (default), local path to archive file to copy to the target server; can be absolute or relative. If C(remote_src=yes), path on the target server to existing archive file to unpack. - If C(remote_src=yes) and C(src) contains C(://), 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 C(remote_src). - This option is mutually exclusive with C(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 C(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 C(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 C(include) is not empty, only files listed here will be extracted. - Mutually exclusive with C(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 C(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 C(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 C(false) used on personally controlled sites using self-signed certificate. - Prior to 2.2 the code worked as if this was set to C(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/ a_ 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 I(list_files) is 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 I(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 BadZipfile)to_bytes to_nativeto_text) AnsibleModule) get_bin_path)get_best_parsable_locale) fetch_file)quotez: 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 groupcCs`td}t|d4}tt|j|dD]}t||}q(Wdn1sN0Y|d@S)z# Return a CRC32 checksum of a file rbNl)binasciicrc32openiterrread)pathZ buffer_sizecrcfZb_blockr=/usr/lib/python3.9/site-packages/ansible/modules/unarchive.pyrs   ,rcCstdd|S)z6 Quote meta-characters in the args for the unix shell z([^A-Za-z0-9_])z\\\1)resub)stringrrr shell_escape%src@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 zipinfoflagbinariesselfr/r0r1r4rrr__init__0s   zZipArchive.__init__cCsd|ddd}d}tddD]<}tddD],}||d|dvr*|d|d|7}q*q||@S)zA Convert a Unix permission string (rw-r--r--) into a mode (0644) Nr)rwxst)range)r>ZmodestrumaskZrevstrmodejirrr_permstr_to_octalDszZipArchive._permstr_to_octalcCsz|j|jd|jg\}}}|r.td|j|ddD]6}|dd}|j|dt |d|j |d<q>dS)Nz-vz,Neither python zipfile nor unzip can read %srA) r4 run_commandr,r/r splitlinessplitr8appendintr:)r>rcouterrlinefieldsrrr_legacy_file_listQs zZipArchive._legacy_file_listc Cs|jr|j|Szt|j}WnDtyb}z,|jddrL|nWYd}~nRd}~00z$|D]}t |j |j|j <qnWn"t y| tdYn0|j|S)Nrbad magic numberz#Unable to list files in the archive)r:rr/rargslower startswithr[infolistrUZCRCfilename Exceptioncloser )r>rarchiveeitemrrr_crc32[s    zZipArchive._crc32c Cs4|jr |jSg|_zt|j}WnDtyd}z,|jddrN|nWYd}~nd}~00z|D]r}|j r|j D] }t ||r|j t |qqpd}|j r|j D]}t ||rd}qq|sp|j t |qpWn>ty$}z$|tdt |WYd}~n d}~00||jS)Nrr\FT'Unable to list files in the archive: %s)r8rr/rr]r^r_r[Znamelistr7fnmatchrTr r5rbrcr )r>rdrememberr( exclude_flagr'rrrfiles_in_archivers8      &zZipArchive.files_in_archivec,Cs2 |jr|j|jdd|jg}n|jdd|jg}|jrD|dg|j|jrV||j|j|\}}}|}d}d}|dkrd}nd}t d}t |t } t } t } t} zt| j} Wnttfy| } Yn0zt| j}Wntttfy| }Yn0d}}|jdrzt|jd}WnRtyztt|jd}Wn$tttfyt| }Yn0Yn0|j}|j}n"z| }WntyYn0| }d}}|jd rPzt|jd }WnVttfy@ztt|jd }Wn$tttfy:t| }Yn0Yn0|j}|j}n"z|}WntylYn0| }| D]}d}|!dd }t"|d krqzt"|dd krt"|dd krʐqzt"|d dkrqz|dddvszt#|ddd$dsqz|dd}|ddd}|d}|d}t|d}t%|d dd}||jvrv|d|7}qz|ddkr|dkr|d||f7}d}n.|dkrd}n|dkrd}n|dkrd}t"|d kr|ddkrd }n|d!krd }nd"}|}nd#| &vr"|}nd}t"|d$ks@t'(|sLt)d%|tj*+|j,t-|dd} zt.| }!WnFtyd}|j/0||d&|7}|d'||f7}YqzYn0|dkrt12|!j3sd}|j/0||d(|7}|d)||f7}qz|dkrNt14|!j3sNd}d}|j/0||d*|7}|d)||f7}qz|dkrt15|!j3sd}|j/0||d+|7}|d)||f7}qzt6d,|}"t7j7t89|d d-dd }#t8:|#;}$t14|!j3r|jjkrd}|d4|||!j>f7}d5|"d<t14|!j3rt?| |j@}%|%|A|krd}|d6||A||%f7}d7|"d<|dkr>|jd8rtB|jd8trH|jd8}&nzt|jd8d }&Wnty}'zhztCD|!|jd8}&WnDty}'z*|jjE|d9tF|'tGHd:WYd}'~'n d}'~'00WYd}'~'n d}'~'00n$|dkr|I|d}&n |I||}&|&t1J|!j3kr>d}d;|"d<<|d=||&t1J|!j3f7}d}(})zt|!jKj}(Wnttfyv|!jK})Yn0| dkr|| ks|| krt)d>||| f|(r|(|krd}|d?||(|f7}d@|"d <n.|) r|)|k rd}|dA||)|f7}d@|"d <d}*}+zt|!jLj}*Wn tttf y@|!jL}+Yn0| dk r|||k s`|| k r||| v r|t)dB||| f|* r|*|k rd}|dC||*|f7}dD|"d <n.|+ r|+|k rd}|dE||+|f7}dD|"d <|rz||j/v r|j/0||dFd+|"|f7}qz|j/ rd}tM||||||dGS)HNz-Tz-s-xr)rTFownergrouprO rPzdl-?zrwxstah-rGrAsurrogate_or_stricterrorszPath %s is excluded on request r@/dz8Path %s incorrectly tagged as "%s", but is a directory. lL-r?Z rwxrwxrwxzrwx---z rw-rw-rw-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 rFz!File %s is newer, excluding file z$File %s differs in mtime (%f vs %f) z#File %s differs in size (%d vs %d) rEz5File %s differs in CRC32 checksum (0x%08x vs 0x%08x) crJz%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 ) unarchivedrVrWrXcmddiff)Nr;r.r/r5extendr7r4rQosrIplatformsystem getgroupsgetuidgetgidpwdgetpwuidpw_name TypeErrorKeyErrorgrpgetgrgidgr_name ValueError OverflowErrorr1getpwnamrUpw_uidrbgetgrnamgr_gidrRrSlen frozensetissubsetr r^ZIP_FILE_MODE_REmatchr rjoinr0rlstatr6rTstatS_ISDIRst_modeS_ISREGS_ISLNKlistdatetimetimestrptimemktimeZ timetupler2st_mtimest_sizerr&rg isinstancer Z_symbolic_mode_to_octal fail_jsonr traceback format_excrMS_IMODEst_uidst_gidr9),r>rrVrWrXold_outrrrIZ systemtypegroupsrun_uidZrun_gidZ run_ownerZ run_groupZ fut_ownerZfut_uidZtpwZ fut_groupZfut_gidZtgrrYZchangeZpcsZztypeZpermstrversionZostypesizerZftypeZ file_umaskr0stZitemizedZ dt_objectZ timestamprrJrernuidrogidrrr is_unarchiveds        $.                                 L     (   zZipArchive.is_unarchivedcCs|jdg}|jr||j||j|jr@|dg|j|jrR||j|d|jg|j |\}}}t ||||dS)Nz-ormz-drrVrWrX) r,r3rrTr/r5r7r0r4rQr9)r>rrVrWrXrrr unarchives    zZipArchive.unarchivec Csg}|jD]B}zt||dt|dWq tyJ||dYq 0q |rhddjd|dfS|jd|jg}|j |\}}}|dkrdSdd |j|ffS) NrsrFz7Unable to find required '{missing}' binary in the path.z' or ')missingz-lTN)Command "%s" could not handle archive: %s) r<setattrr rrTformatrr,r/r4rQ)r>rbrrVrWrXrrrcan_handle_archives  zZipArchive.can_handle_archiveN) r!r"r#r?rMr[rgpropertyrlrrrrrrrr$.s   %6r$c@s@eZdZddZddZeddZddZd d Zd d Z d S) TgzArchivecCs||_||_||_|jd|_||_|jjrD|jjdd|jjddd|jjdD|_ |jjd|_ d|_ d|_ d |_ g|_dS) Nr%Tz>remote module (%s) does not support check mode when using gtar)ZskippedrcSsg|]}|dqS)rw)rstrip).0rrrr rz'TgzArchive.__init__..r'r(z-z)r/r0r1r2r3r4 check_mode exit_json_namer5r7r,tar_typezipflagr8r=rrrr?s zTgzArchive.__init__cCsJ|jdg}|j|\}}}d}|dr0d}n|drFd|vrFd}|S)Nz --versionZbsdtarr}tarZGNUgnu)r,r4rQr_)r>rrVrWrXrrrr _get_tar_types  zTgzArchive._get_tar_typec CsJ|jr |jS|jdd|jg}|jr.||j|jrF|dg|j|jrb|dd|jD|d|jg|j r||j t |j }|j j ||jt ||||dd\}}}|d krtd ||D]r}tt|d }|d r|d d}d }|jr.|jD]}t||rd}q.q|s|jt|q|jS)Nz--list-C--show-transformed-namescSsg|] }d|qSz --exclude=rrrrrrrrz/TgzArchive.files_in_archive..-fLANGLC_ALL LC_MESSAGESLANGUAGEcwdZenviron_updaterrhrwrsFT)r8r,r0rrTr3rr5r/r7r r4rQr9r rRr codecs escape_decoder_ri) r>rlocalerVrWrXrarkr'rrrrl s:   &      zTgzArchive.files_in_archivec Cs:|jdd|jg}|jr"||j|jr:|dg|j|jdr\|dt|jd|jdr~|dt|jd|jj dr|d |j r|d d |j D|d |j g|j r||j t |j}|jj||jt||||d d\}}}d}|}d}t}||D]} t| r:q&|dkrh|jdsht| rh|| d7}|dkr|jdst| r|| d7}|jdst| r|| d7}t| r|| d7}t| r|| d7}t| r|| d7}t| r&|| d7}q&|r(d}t|||||dS)Nz--diffrrrn--owner=ro--group=r--keep-newer-filescSsg|] }d|qSrrrrrrrCrz,TgzArchive.is_unarchived..rrrTr)r rJF)rrVrWrXr)r,r0rrTr3rr1rr4r2r5r/r7r rQr9rrrR EMPTY_FILE_REsearch OWNER_DIFF_RE GROUP_DIFF_RE MODE_DIFF_REMOD_TIME_DIFF_REMISSING_FILE_REINVALID_OWNER_REINVALID_GROUP_RE) r>rrrVrWrXrrrrYrrrr6sT       & " "         zTgzArchive.is_unarchivedc Cs|jdd|jg}|jr"||j|jr:|dg|j|jdr\|dt|jd|jdr~|dt|jd|jj dr|d |j r|d d |j D|d |j g|j r||j t |j}|jj||jt||||d d\}}}t||||dS)Nz --extractrrrnrrorrrcSsg|] }d|qSrrrrrrryrz(TgzArchive.unarchive..rrrr)r,r0rrTr3rr1rr4r2r5r/r7r rQr9)r>rrrVrWrXrrrrls&       &zTgzArchive.unarchivec Csztd|_Wn8tyFztd|_Wnty@YYdS0Yn0||_|jdkrpdd|j|jffSz|jr~WdSWn:ty}z"dd|jt|ffWYd}~Sd}~00dd |jfS) NZgtarr)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,rrrrlr r )r>rerrrrs      ,zTgzArchive.can_handle_archiveN) r!r"r#r?rrrlrrrrrrrrs  +6rcseZdZfddZZS) TarArchivecs tt|||||d|_dS)Nr))superrr?rr= __class__rrr?szTarArchive.__init__r!r"r#r? __classcell__rrrrrsrcseZdZfddZZS)TarBzipArchivecs tt|||||d|_dS)Nz-j)rrr?rr=rrrr?szTarBzipArchive.__init__rrrrrrsrcseZdZfddZZS) TarXzArchivecs tt|||||d|_dS)Nz-J)rrr?rr=rrrr?szTarXzArchive.__init__rrrrrrsrcseZdZfddZZS)TarZstdArchivecs tt|||||d|_dS)Nz--use-compress-program=zstd)rrr?rr=rrrr?szTarZstdArchive.__init__rrrrrrsrcs(eZdZfddZfddZZS) ZipZArchivecs&tt|||||d|_d|_dS)Nz-Z)r*)r+r.)rrr?r;r<r=rrrr?szZipZArchive.__init__csXtt|\}}|s||fS|j|jg}|j|\}}}d|vrLdSdd|fS)Nr-rFz/Command "unzip -Z" could not handle archive: %s)rrrr.r;r4rQr^)r>Zunzip_availableZ error_msgrrVrWrXrrrrs  zZipZArchive.can_handle_archive)r!r"r#r?rrrrrrrs rc Csttttttttg}t}|D]4}|||||}|\}} |rF|S| | qd |} |j d|| fddS)NrzdFailed to find handler for "%s". Make sure the required command to extract the file is installed. %sr) r$rrrrrrsetraddrr) r/destr1r4handlersZreasonshandlerobjZ can_handlereasonZ reason_msgrrr pick_handlers   rcCs,tttdddtdddtdddtddtdddtdddtdd gd tdd gd tdd gd tdddtd d dtdddtdddd dddgd}|jd}|jd}t|dd}|jd}||j}tj|s|s|jd|dn&d|vr t ||}n|jd|dt |tj s<|jd|dz&tj |dkr`|jd|dWn>t y}z$|jd|t|fdWYd}~n d}~00tj|s|jd|dt||||}t|jj||d}|} |jr| d |d <n| drd|d <nxz>||d!<|d!d"dkrP|jfd#d$||fi|Wn0ty|jfd#d$||fi|Yn 0d|d <| d%drd&| d%i|d%<|d%dr|jsg} |jD]} tj|t| dd|d<z|j||d dd'|d <WnFttfyJ}z(|jfd#d(t|i|WYd}~n d}~00d)| vr| d)d} | | vr| | q| r| D]z} d*|| f|d<z|j||d dd'|d <WnFttfy}z(|jfd#d(t|i|WYd}~n d}~00q|jd+r|j|d,<|jfi|dS)-NrT)typerequiredboolF)rdefault)rrstr)relementsr rUi) r/r remote_srcZcreates list_filesrr'r(r%Zvalidate_certsr&copyZdecrypt)r(r')Z argument_specZadd_file_common_argsZsupports_check_modeZmutually_exclusiver/rrtrurzSource '%s' failed to transferrz://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)rrr/rZchangedZextract_resultsrVrzfailed to unpack %s to %srZprepared)expandz1Unexpected error when accessing exploded file: %srwz%s/%srfiles)r r9r2rZload_file_common_argumentsrrexistsrraccessR_OKgetsizerbr isdirrrr!rrrIOErrorgetrlrZset_fs_attributes_if_differentOSErrorrSrTr)r4r/rr0rr1rerZres_argsZ check_resultsZ top_foldersraZtop_folder_pathrrrrmains                   .    " 2  6  r__main__)CZ __future__rrrrZ __metaclass__Z DOCUMENTATIONZEXAMPLESZRETURNrrrrirrrrrrrr functoolsrzipfilerrZansible.module_utils._textrr r Zansible.module_utils.basicr Z#ansible.module_utils.common.processr Z"ansible.module_utils.common.localer Zansible.module_utils.urlsrZshlexr ImportErrorZpipescompilerrrrrrrrrrrrbr objectr$rrrrrrrrr!rrrr sn<               D.  w