Ë Ó~óà£~S›ãóN—dZddlZddlZddlZddlZddlZddlZddlZddlm Z eje «Z Gd„de «ZGd„de «ZGd „d e«Zd Zd Zd „ZGd„de«ZdZej.e«ZdZej.e«ZdZdZdZGd„de«Zd„Zy)a¯ This module implements python implements a way to get file block. Two methods are supported - the FIEMAP ioctl and the 'SEEK_HOLE / SEEK_DATA' features of the file seek syscall. The former is implemented by the 'FilemapFiemap' class, the latter is implemented by the 'FilemapSeek' class. Both classes provide the same API. The 'filemap' function automatically selects which class can be used and returns an instance of the class. éNé)Ú BmapHelperscó—eZdZdZy)Ú ErrorNotSuppz” An exception of this type is raised when the 'FIEMAP' or 'SEEK_HOLE' feature is not supported either by the kernel or the file-system. N©Ú__name__Ú __module__Ú __qualname__Ú__doc__©óú5/usr/lib/python3.12/site-packages/bmaptool/Filemap.pyrr*s „ñð  r rcó—eZdZdZy)ÚErrorz;A class for all the other exceptions raised by this module.Nrr r rrr3s„ÙEàr rcó:—eZdZdZd„Zd„Zd„Zd„Zd„Zd„Z d„Z y ) Ú _FilemapBasezÉ This is a base class for a couple of other classes in this module. This class simply performs the common parts of the initialization process: opens the image file, gets its size, etc. có2—d|_t|d«r||_|j|_n||_|j « t j|jj««j|_ tj|j«|_|j|jzdz |jz|_ |jj#« t j$|jj««ftj*|j«s;tj,|j«}td |j›d |›d �«‚t.j1d|jz«t.j1d|j|j |jfz«y#t$r*}td|jj›d|›�«‚d}~wwxYw#t$r }td|j›d|›�«‚d}~wwxYw#t$r }td|j›d|›�«‚d}~wwxYw#t&$r+}td |j›d|j(›d �«‚d}~wwxYw)z‚ Initialize a class instance. The 'image' argument is full path to the file or file object to operate on. FÚfilenoz#cannot get information about file 'ú': Nzcannot get block size for 'rzcannot flush image file 'zcannot synchronize image file 'Ú z(image file on incompatible file system 'z': 'z': see docs for fixzopened image "%s"z-block size %d, blocks count %d, image size %d)Ú_f_image_needs_closeÚhasattrÚ_f_imageÚnameÚ _image_pathÚ_open_image_fileÚosÚfstatrÚst_sizeÚ image_sizeÚIOErrorrrÚget_block_sizeÚ block_sizeÚ blocks_cntÚflushÚfsyncÚOSErrorÚstrerrorÚis_compatible_file_systemÚget_file_system_typeÚ_logÚdebug)ÚselfÚimageÚerrÚfstypes rÚ__init__z_FilemapBase.__init__@s#€ð %*ˆÔ!ä �5˜(Ô #Ø!ˆDŒMØ$Ÿz™zˆDÕ à$ˆDÔ Ø × !Ñ !Ô #ð Ü Ÿh™h t§}¡}×';Ñ';Ó'=Ó>×FÑFˆDŒOð  XÜ)×8Ñ8¸¿¹ÓGˆDŒOð Ÿ?™?¨T¯_©_Ñ<¸qÑ@ÀTÇ_Á_ÑTˆŒð VØ �M‰M× Ñ Ô !ð Ü �H‰H�T—]‘]×)Ñ)Ó+Ó ,Ñ -ô×4Ñ4°T×5EÑ5EÔFÜ ×5Ñ5°d×6FÑ6FÓGˆFÝà×#Ó#¢Vð-óð ô � ‰ Ð&¨×)9Ñ)9Ñ9Ô:Ü � ‰ Ø ;Ø�‰ §¡°·±ÐAñ Bõ øôCò ÝØ@DÇ Á ×@RÓ@RÑTWÐXóð ûð ûôò XÝÀ×@PÓ@PÑRUÐVÓWÐ Wûð Xûôò Vݸd×>NÓ>NÑPSÐTÓUÐ Uûð Vûô ò Ýà×#Ó# S§\£\ð3óð ûð ús`Ár có—td«‚)až This method has has to be implemented by child classes. This is a generator which yields ranges of mapped blocks in the file. The ranges are tuples of 2 elements: [first, last], where 'first' is the first mapped block and 'last' is the last mapped block. The ranges are yielded for the area of the file of size 'count' blocks, starting from block 'start'. r9r:©r-ÚstartÚcounts rÚget_mapped_rangesz_FilemapBase.get_mapped_ranges—s€ôÐ3Ó4Ð4r có—td«‚)z¬ This method has has to be implemented by child classes. Just like 'get_mapped_ranges()', but yields unmapped block ranges instead (holes). r9r:rBs rÚget_unmapped_rangesz _FilemapBase.get_unmapped_ranges¤r>r N) rr r r r1r4rr=r@rErGr r rrr9s+„ñò 5 òn"ò )ò5ò5ò 5ó5r réécó— tj|j«||«S#t$rP}|jtj k(rYd}~y|jtj k(r td«‚‚d}~wwxYw)a]This is a helper function which invokes 'os.lseek' for file object 'file_obj' and with specified 'offset' and 'whence'. The 'whence' argument is supposed to be either '_SEEK_DATA' or '_SEEK_HOLE'. When there is no more data or hole starting from 'offset', this function returns '-1'. Otherwise the data or hole position is returned.NéÿÿÿÿzFthe kernel or file-system does not support "SEEK_HOLE" and "SEEK_DATA")rÚlseekrr'ÚerrnoÚENXIOÚEINVALr)Úfile_objÚoffsetÚwhencer/s rÚ_lseekrS³sm€ð Ü�x‰x˜Ÿ™Ó)¨6°6Ó:Ð:øÜ ò ð �9‰9œŸ ™ Ò #ÜØ �Y‰Yœ%Ÿ,™,Ò &Üð.óð ð ûð ús‚$'§ B°A;Á)A;Á;Bcó:—eZdZdZd„Zd„Zd„Zd„Zd„Zd„Z d„Z y ) Ú FilemapSeekzà This class uses the 'SEEK_HOLE' and 'SEEK_DATA' to find file block mapping. Unfortunately, the current implementation requires the caller to have write access to the image file. cóz—tj||«tjd«|j «y)ú5Refer the '_FilemapBase' class for the documentation.zFilemapSeek: initializingN)rr1r+r,Ú_probe_seek_hole©r-r.s rr1zFilemapSeek.__init__Ñs-€ô ×јd EÔ*Ü � ‰ Ð.Ô/à ×ÑÕr cóþ—tjj|j«} t j d|¬«} tj|j«|j«t|dt«}|dk7r#tjd|z«td «‚|j«y#t $r}td|›d|›�«‚d}~wwxYw#t $r}td|›d|›�«‚d}~wwxYw) af Check whether the system implements 'SEEK_HOLE' and 'SEEK_DATA'. Unfortunately, there seems to be no clean way for detecting this, because often the system just fakes them by just assuming that all files are fully mapped, so 'SEEK_HOLE' always returns EOF and 'SEEK_DATA' always returns the requested offset. I could not invent a better way of detecting the fake 'SEEK_HOLE' implementation than just to create a temporary file in the same directory where the image file resides. It would be nice to change this to something better. zw+)Údirzcannot create a temporary in "z": Nz#cannot truncate temporary file in "rzlseek(0, SEEK_HOLE) returned %dzdthe file-system does not support "SEEK_HOLE" and "SEEK_DATA" but only provides a stub implementation)rÚpathÚdirnamerÚtempfileÚ TemporaryFiler'rÚ ftruncaterr#rSÚ _SEEK_HOLEr+r,r3)r-Ú directoryÚtmp_objr/Úoffss rrXzFilemapSeek._probe_seek_holeÚsæ€ô—G‘G—O‘O D×$4Ñ$4Ó5ˆ ð Ü×,Ñ,¨T°yÔAˆGð  Ü �L‰L˜Ÿ™Ó)¨4¯?©?Ô ;ô �g˜q¤*Ó-ˆØ �1Š9ô �J‰JÐ8¸4Ñ?Ô @Üð1óð ð � ‰ �øô/ò ÝÚ;DÁcÐJóð ûð ûôò ÝÚ@IÉ3ÐOóð ûð ús/«B;Á.CÂ; CÃCÃCà C<Ã&C7Ã7C<có¾—t|j||jzt«}|dk(rd}n||jz|k(}tj d||fz«|S)rWrKFz+FilemapSeek: block_is_mapped(%d) returns %s)rSrr#Ú _SEEK_DATAr+r,)r-r<rdÚresults rr=zFilemapSeek.block_is_mappedsW€ä�d—m‘m U¨T¯_©_Ñ%<¼jÓIˆØ �2Š:؉Fà˜TŸ_™_Ñ,°Ñ5ˆFä � ‰ Ð@ÀEÈ6À?ÑRÔS؈ r có&—|j|« S©rW©r=r;s rr@zFilemapSeek.block_is_unmappedó€à×'Ñ'¨Ó.Ð.Ð.r c#óØK—||k7sJ‚||jz}|||jzz} t|j||«}|dk(s||k\s||jk(ryt|j||«}|dk(s||jk(r|j|jz}||kDr|}||jz}||jzdz }t j d||fz«||f–—Œ¾­w)z² This function implements 'get_mapped_ranges()' and 'get_unmapped_ranges()' depending on what is passed in the 'whence1' and 'whence2' arguments. rKrz$FilemapSeek: yielding range (%d, %d)N)r#rSrr r$r+r,) r-rCrDÚwhence1Úwhence2ÚendÚlimitÚ start_blkÚend_blks rÚ _get_rangeszFilemapSeek._get_rangessñèø€ð˜'Ò!Ð!Ð!Ø�d—o‘oÑ%ˆØ�e˜dŸo™oÑ-Ñ-ˆàܘ4Ÿ=™=¨#¨wÓ7ˆEؘŠ{˜e ušn°¸¿¹Ò0HØä˜Ÿ™¨¨wÓ7ˆCØ�bŠy˜C 4§?¡?Ò2Ø—o‘o¨¯©Ñ7�Ø�UŠ{Ø�à §¡Ñ0ˆIؘTŸ_™_Ñ,¨qÑ0ˆGÜ �J‰JÐ=ÀÈGÐ@TÑTÔ UؘgÐ&Ò &ðùs‚C(C*có|—tjd||||zdz fz«|j||tt«S)rWz+FilemapSeek: get_mapped_ranges(%d, %d(%d))r)r+r,rsrfrarBs rrEzFilemapSeek.get_mapped_ranges/sB€ä � ‰ Ø 9Ø�e˜U U™]¨QÑ.Ð/ñ 0ô ð×Ñ  u¬j¼*ÓEÐEr có|—tjd||||zdz fz«|j||tt«S)rWz-FilemapSeek: get_unmapped_ranges(%d, %d(%d))r)r+r,rsrarfrBs rrGzFilemapSeek.get_unmapped_ranges7sB€ä � ‰ Ø ;Ø�e˜U U™]¨QÑ.Ð/ñ 0ô ð×Ñ  u¬j¼*ÓEÐEr N) rr r r r1rXr=r@rsrErGr r rrUrUÊs-„ñò  ò)òV ò/ò'ò6FóFr rUz=QQLLLLz =QQQQQLLLLl f@icó@—eZdZdZd„Zd„Zd„Zd„Zd„Zd„Z d„Z d „Z y ) Ú FilemapFiemapa This class provides API to the FIEMAP ioctl. Namely, it allows to iterate over all mapped blocks and over all holes. This class synchronizes the image file every time it invokes the FIEMAP ioctl in order to work-around early FIEMAP implementation kernel bugs. cóÊ—tj||«tjd«t|_|xj t zc_|j tz|_|jdkDsJ‚|jtz|_|xj t z c_tjddg|j z«|_ |jd«y)zr Initialize a class instance. The 'image' argument is full the file object to operate on. zFilemapFiemap: initializingrÚBN) rr1r+r,Ú_FIEMAP_BUFFER_SIZEÚ _buf_sizeÚ _FIEMAP_SIZEÚ_FIEMAP_EXTENT_SIZEÚ_fiemap_extent_cntÚarrayÚ_bufr=rYs rr1zFilemapFiemap.__init__as®€ô ×јd EÔ*Ü � ‰ Ð0Ô1ä,ˆŒð �Šœ,Ñ&�Ø"&§.¡.Ô4GÑ"GˆÔØ×&Ñ&¨Ò*Ð*Ð*Ø×0Ñ0Ô3FÑFˆŒØ �Šœ,Ñ&�ô—K‘K  a S¨4¯>©>Ñ%9Ó:ˆŒ ð ×јQÕr c ó.—|jdk7r.|dks||jk\rtd||jfz«‚tjt|j d||j z||j ztd|jd«  tj|jt|j d«tj*t|j dt,«S#t$rž}|jtjk(r"d}t j#|«t%|«‚|jtj&k(r"d}t j#|«t%|«‚td|j(›d|›�«‚d}~wwxYw) a; Invoke the FIEMAP ioctl for 'count' blocks of the file starting from block number 'block'. The full result of the operation is stored in 'self._buf' on exit. Returns the unpacked 'struct fiemap' data structure in form of a python list (just like 'struct.upack()'). rz-bad block number %d, should be within [0, %d]rzCFilemapFiemap: the FIEMAP ioctl is not supported by the file-systemz>FilemapFiemap: the FIEMAP ioctl is not supported by the kernelzthe FIEMAP ioctl failed for 'rN)r$rÚstructÚ pack_intoÚ_FIEMAP_FORMATr€r#Ú_FIEMAP_FLAG_SYNCr~ÚfcntlÚioctlrÚ _FIEMAP_IOCTLr!rMÚ EOPNOTSUPPr+r,rÚENOTTYrÚunpackr|)r-r<rDr/Úerrstrs rÚ_invoke_fiemapzFilemapFiemap._invoke_fiemapzs[€ð �?‰?˜aÒ  U¨Q¢Y°%¸4¿?¹?Ò2JÜØ?ؘ$Ÿ/™/Ð*ñ+óð ô ×ÑÜ Ø �I‰IØ Ø �D—O‘OÑ #Ø �D—O‘OÑ #Ü Ø Ø × #Ñ #Ø ô ð Ü �K‰K˜Ÿ ™ ¤}°d·i±iÀÔ Cô*�}‰}œ^¨T¯Y©Y°}¼ Ð-EÓFÐFøô)ò ð�y‰yœE×,Ñ,Ò,ð)ðô— ‘ ˜6Ô"Ü" 6Ó*Ð*Ø�y‰yœEŸL™LÒ(àWðô— ‘ ˜6Ô"Ü" 6Ó*Ð*ÝØ:>×:JÓ:JÉCÐPóð ûð! úsÂ0C-Ã- FÃ6BFÆFcóz—|j|d«}t|d«}tjd||fz«|S)rWrrHz-FilemapFiemap: block_is_mapped(%d) returns %s)r�Úboolr+r,)r-r<Ú struct_fiemaprgs rr=zFilemapFiemap.block_is_mapped³s@€à×+Ñ+¨E°1Ó5ˆ ô �m AÑ&Ó'ˆÜ � ‰ ÐBÀeÈVÀ_ÑTÔU؈ r có&—|j|« Srirjr;s rr@zFilemapFiemap.block_is_unmapped¾rkr có~—tt|zz}tjt|j ||tz«S)z Unpack a 'struct fiemap_extent' structure object number 'index' from the internal 'self._buf' buffer. )r|r}r‚r‹Ú_FIEMAP_EXTENT_FORMATr€)r-ÚindexrQs rÚ_unpack_fiemap_extentz#FilemapFiemap._unpack_fiemap_extentÂs;€ô Ô 3°eÑ ;Ñ;ˆÜ�}‰}Ü ! 4§9¡9¨V°fÔ?RÑ6RÐ#Só ð r c#óÄK—|}|||zkrÒ|j||«}|d}|dk(ryd}||krŸ|j|«}|d}||jz} |d} | |jz} ||jzdk(sJ‚| |jzdk(sJ‚| ||zdz kDryt| |«} t | | z||z«dz } | | f–—|dz }||krŒŸ  z}|||zkrŒÑyy­w)aJ Implements most the functionality for the 'get_mapped_ranges()' generator: invokes the FIEMAP ioctl, walks through the mapped extents and yields mapped block ranges. However, the ranges may be consecutive (e.g., (1, 100), (100, 200)) and 'get_mapped_ranges()' simply merges them. rHrNér)r�r•r#ÚmaxÚmin)r-rCrDr<r�Úmapped_extentsÚextentÚ fiemap_extentÚ extent_startÚ extent_blockÚ extent_lenÚ extent_countÚfirstÚlasts rÚ_do_get_mapped_rangesz#FilemapFiemap._do_get_mapped_rangesÍs1èø€ðˆØ�e˜e‘mÒ#Ø ×/Ñ/°°uÓ=ˆMà*¨1Ñ-ˆNØ Ò"ààˆFؘ>Ò)Ø $× :Ñ :¸6Ó B� ð -¨QÑ/� à+¨t¯©Ñ>� à*¨1Ñ-� à)¨T¯_©_Ñ<� ð$ d§o¡oÑ5¸Ò:Ð:Ð:Ø! D§O¡OÑ3°qÒ8Ð8Ð8à %¨%¡-°!Ñ"3Ò3Øä˜L¨%Ó0�ܘ<¨,Ñ6¸À¹ ÓFÈÑJ�ؘd�mÒ#à˜!‘ �ð/˜>Ó)ð2! <Ñ/ˆEðE�e˜e‘mÕ#ùs‚C C à C ÃC c#ó`K—tjd||||zdz fz«|j||«} t|«\}}|D]4\}}||dz k(r|}Œtjd||fz«||f–—||}}Œ6tjd||fz«||f–—y#t$rYywxYw­w)rWz-FilemapFiemap: get_mapped_ranges(%d, %d(%d))rNú&FilemapFiemap: yielding range (%d, %d))r+r,r£ÚnextÚ StopIteration)r-rCrDÚiteratorÚ first_prevÚ last_prevr¡r¢s rrEzFilemapFiemap.get_mapped_rangesûsÛèø€ä � ‰ Ø ;Ø�e˜U U™]¨QÑ.Ð/ñ 0ô ð×-Ñ-¨e°UÓ;ˆð Ü$(¨£NÑ !ˆJ˜ ó$‰KˆE�4ؘE A™IÒ%Ø ‘ ä— ‘ Ø<À ÈIÐ?VÑVôð" 9Ð-Ò-Ø(-¨t˜I‘ ð$ô � ‰ Ð;¸zÈ9Ð>UÑUÔVؘ9Ð%Ó%øôò Ù ð üs)‚4B.·BÁAB. B+Â(B.Â*B+Â+B.c#óPK—tjd||||zdz fz«|}|j||«D]5\}}||kDr&tjd||dz fz«||dz f–—|dz}Œ7|||zkr-tjd|||zdz fz«|||zdz f–—yy­w)rWz/FilemapFiemap: get_unmapped_ranges(%d, %d(%d))rr¥N)r+r,r£)r-rCrDÚ hole_firstr¡r¢s rrGz!FilemapFiemap.get_unmapped_rangessÞèø€ä � ‰ Ø =Ø�e˜U U™]¨QÑ.Ð/ñ 0ô ðˆ Ø×5Ñ5°e¸UÖC‰KˆE�4Ø�zÒ!Ü— ‘ Ø<À ÈEÐTUÉIÐ?VÑVôð" 5¨1¡9Ð-Ò-à ™‰JðDð ˜ ™ Ò %Ü �J‰JØ8ؘu u™}¨qÑ0Ð1ñ2ô ð˜u u™}¨qÑ0Ð1Ó 1ð &ùs‚B$B&N) rr r r r1r�r=r@r•r£rErGr r rrwrwXs2„ñò ò27Gòr ò/ò  ò,0ò\&ó42r rwcóN— t|«S#t$rt|«cYSwxYw)a¨ Create and return an instance of a Filemap class - 'FilemapFiemap' or 'FilemapSeek', depending on what the system we run on supports. If the FIEMAP ioctl is supported, an instance of the 'FilemapFiemap' class is returned. Otherwise, if 'SEEK_HOLE' is supported an instance of the 'FilemapSeek' class is returned. If none of these are supported, the function generates an 'Error' type exception. )rwrrU)r.s rÚfilemapr®-s,€ð"ܘUÓ#Ð#øÜ ò"ܘ5Ó!Ò!ð"ús ‚ �$£$) r rrMr‚rr†r^ÚloggingÚrÚ getLoggerrr+Ú ExceptionrrÚobjectrrfrarSrUr„Úcalcsizer|r“r}rˆr…rzrwr®r r rÚrµs×ðñ"ó Û Û Û Û ÛÛÝà€w×јÓ"€ô �9ô ô ˆIô ô r5�6ôr5ðl€ Ø € òô.sF�,ôsFðx€àˆv�‰˜~Ó.€ à$Ðà%�f—o‘oÐ&;Ó<Ðà€ ðÐð!ÐôR2�LôR2ój "r