o ]Lb0@sddlmZddlZddlZddlZddlZddlZddlmZddl m Z m Z m Z m Z mZe jr=ddlmZmZmZmZe ddZe d Zd d ZGd d d eZdddZddZddZGdddeZe edrrejZedurxeZej!Z!ej"Z"dS))absolute_importN)_)encodingerrorpolicypycompatutil)AnyCallableIteratorOptionaldirstateDirsparserscCst|SN)rhfsignorecleanlower)sr4/usr/lib/python3/dist-packages/mercurial/pathutil.py _lowercleansrc@sBeZdZdZdddZdddZd d Zd d Zej d dZ dS) pathauditora^ensure that a filesystem path contains no banned components. the following properties of a path are checked: - ends with a directory separator - under top-level .hg - starts at the root of a windows drive - contains ".." More check are also done about the file system states: - traverses a symlink (e.g. a/symlink_here/b) - inside a nested repository (a callback can be used to approve some nested repositories, e.g., subrepositories) The file system checks are only done when 'realfs' is set to True (the default). They should be disable then we are auditing path for operation on stored history. If 'cached' is set to True, audited paths and sub-directories are cached. Be careful to not keep the cache of unmanaged directories for long because audited paths may be replaced with symlinks. NTFcCsXt|_t|_||_||_||_||_tj |r%t |s%t j |_ dSdd|_ dS)NcSs|Srr)xrrrEsz&pathauditor.__init__..) setaudited auditeddirroot_realfs_cachedcallbackospathlexistsr fscasesensitivenormcase)selfrr!realfscachedrrr__init__;s zpathauditor.__init__cCst|}||}||jvrdSt|rttd|t|}t j |ds9t |ddvs9t j|vrBttd||D]!}d|vre|dd\}}|re|dvrettd|qDd t |vrd d |D}d D]'}||ddvr||} t j j|d| } ttd |t | fqut|} t|t| ksJ|| tt|D]2} t j|d| d} t j| d| d}||jvrq|jr|| ||jr|j|q|jr|j|dSdS)zPCheck the relative path. path may contain a pattern (e.g. foodir/**.txt)Ns$path ends in directory separator: %sr).hg.hg.s#path contains illegal component: %s~r)sHGsHG8B6Cr+cSsg|]}t|qSr)r).0prrr gsz(pathauditor.__call__..)r+r,"path '%s' is inside nested repo %r)r localpathr&r endswithsepr InputErrorr splitpathr"r# splitdriverrospardirsplitisdigitupperindexjoinbytestrlenpoprangeosseprr_checkfsr add)r'r#modenormpathpartsr0firstlastlpartsposbase normpartsiprefix normprefixrrr__call__Gsj                zpathauditor.__call__c Cstj|j|}zt|}Wnty.}z|jtjtjtj fvr#WYd}~dSd}~wwt |j rHt dt|t|f}t|t |j rqtjtj|drs|jra||sut d}t||t|fdSdSdS)z3raise exception if a file system backed check failsNs"path %r traverses symbolic link %rr+r2)r"r#r=rlstatOSErrorerrnoENOENTENOTDIREINVALstatS_ISLNKst_moderrr>rAbortS_ISDIRisdirr!)r'rOr#curpathsterrmsgrrrrCs.   zpathauditor._checkfsc Cs*z||WdSttjfyYdSwNTF)rSrr[)r'r#rrrchecks zpathauditor.checkccs\|jr dVdSzd|_dVW|j|jd|_dS|j|jd|_wrb)r rclearrr'rrrr)s     zpathauditor.cachedrbr) __name__ __module__ __qualname____doc__r*rQrCrc contextlibcontextmanagerr)rrrrr$s  Arc Cst|r|}n|tj}|}tj|stj|||}tj|}|dur+t |}||krE| |rE|t |d}||t |S||krKdSg} zt ||}Wn tyad}Ynw|r{|shdS|tjj|}||t |St|\}} || ||krn|}qNd} z&||krt||||t||d} | tjr| dd} td| } Wn tjyYnwtjtd||f| d) areturn the canonical path of myname, given cwd and root >>> def check(root, cwd, myname): ... a = pathauditor(root, realfs=False) ... try: ... return canonpath(root, cwd, myname, a) ... except error.Abort: ... return 'aborted' >>> def unixonly(root, cwd, myname, expected='aborted'): ... if pycompat.iswindows: ... return expected ... return check(root, cwd, myname) >>> def winonly(root, cwd, myname, expected='aborted'): ... if not pycompat.iswindows: ... return expected ... return check(root, cwd, myname) >>> winonly(b'd:\\repo', b'c:\\dir', b'filename') 'aborted' >>> winonly(b'c:\\repo', b'c:\\dir', b'filename') 'aborted' >>> winonly(b'c:\\repo', b'c:\\', b'filename') 'aborted' >>> winonly(b'c:\\repo', b'c:\\', b'repo\\filename', ... b'filename') 'filename' >>> winonly(b'c:\\repo', b'c:\\repo', b'filename', b'filename') 'filename' >>> winonly(b'c:\\repo', b'c:\\repo\\subdir', b'filename', ... b'subdir/filename') 'subdir/filename' >>> unixonly(b'/repo', b'/dir', b'filename') 'aborted' >>> unixonly(b'/repo', b'/', b'filename') 'aborted' >>> unixonly(b'/repo', b'/', b'repo/filename', b'filename') 'filename' >>> unixonly(b'/repo', b'/repo', b'filename', b'filename') 'filename' >>> unixonly(b'/repo', b'/repo/subdir', b'filename', b'subdir/filename') 'subdir/filename' Nr-TFsconsider using '--cwd %s's%s not under root '%s')hint)r r4rrBr"r#isabsr=rFr startswithr?pconvertsamefilerSreverser9append canonpathpathtoendswithrrr[) rcwdmynameauditorrootsepnamerelrdirnamebasenamermrelpathrrrrtsd +           rtcCs0tj|\}}t|ttjkr|tjS|S)anormalize the specified path as path prefix Returned value can be used safely for "p.startswith(prefix)", "p[len(prefix):]", and so on. For efficiency, this expects "path" argument to be already normalized by "os.path.normpath", "os.path.realpath", and so on. See also issue3033 for detail about need of this function. >>> normasprefix(b'/foo/bar').replace(pycompat.ossep, b'/') '/foo/bar/' >>> normasprefix(b'/').replace(pycompat.ossep, b'/') '/' )r"r#r7r?rrB)r#dr0rrr normasprefixs rccsB|d}|dkr|d|V|dd|}|dks dVdS)N/rlrr-)rfind)r#rKrrrfinddirs6s  rc@s:eZdZdZdddZddZddZd d Zd d Zd S)dirsz6a multiset of directory names from a set of file pathsFcCsli|_|j}t|tr"|r"t|D] \}}|jdkr||qdS|r+d}t||D]}||q-dS)zS a dict map indicates a dirstate while a list indicates a manifest rs3`only_tracked` is only supported with a dict sourceN) _dirsaddpath isinstancedictr iteritemsstaterProgrammingError)r'map only_trackedrfrrarrrr*Bs   z dirs.__init__cCsT|j}t|D] }|drtd|||vr#||d7<dSd||<qdS)Nrz-found invalid consecutive slashes in path: %rr)rrrv ValueErrorr'r#rrLrrrrSs   z dirs.addpathcCs@|j}t|D]}||dkr||d8<dS||=qdS)Nr)rrrrrrdelpath`s  z dirs.delpathcCs t|jSr)iterrrerrr__iter__is z dirs.__iter__cCs ||jvSr)r)r'rrrr __contains__ls zdirs.__contains__N)F) rfrgrhrir*rrrrrrrrr?s   rr)# __future__rrjrTr" posixpathrXi18nrrrrrr TYPE_CHECKINGtypingr r r r importrustrustdirs importmodrrobjectrrtrrr safehasattrr=r}rrrrs2     g 2