
    JjG*                     T    d 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 G d d          ZdS )a  Filesystem abstraction for multi-user mode.

Provides :class:`UserFS`, a unified interface for file operations.

All I/O uses native Python (``aiofiles`` / ``os``).  In multi-user mode
the server process is added to each provisioned user's group, and home
directories are ``chmod 2770`` (setgid + group rwx), so standard file
operations work without subprocess.

After each write operation a ``sudo chown`` call fixes file ownership
so that files belong to the provisioned user, not the server process.
    Nc            	          e Zd ZdZd!dedz  dedz  fdZd"dededz  defd	Zdedefd
ZdeddfdZ	deddfdZ
deddfdZdedefdZd#dededefdZdedefdZdedefdZdedefdZdedefdZdedee         fdZdedeeeee         ee         f                  fdZd#dedededdfdZdededdfdZdeddfdZdeddfdZdededdfd ZdS )$UserFSuM  Filesystem operations scoped to an optional OS user.

    *username* is used for ownership fixups after writes (``None`` = stdlib).
    *home* is the user's home directory (default working directory).

    When *username* is set, path validation prevents access to other
    users' home directories (``/home/<other_user>/…``).
    Nusernamehomec                 H    || _         |pt          j                    | _        d S N)r   osgetcwdr   )selfr   r   s      j/home/rurouni/.cache/uv/archive-v0/6mr0qdqUAO9GXgnL/lib/python3.11/site-packages/open_terminal/utils/fs.py__init__zUserFS.__init__!   s     'BIKK			    pathcwdreturnc                    t           j                            |          r~| j        rX| j        dk    rMdD ]J}||k    r	| j        } n:|                    |dz             r!| j        |t          |          d         z   } nKt           j                            |          S t           j                            t           j                            |p| j        |                    S )a  Resolve *path* to an absolute path relative to the user's home.

        Absolute paths are normalised in place.  Relative paths are joined
        to *cwd* (if provided) or ``self.home`` so that they resolve against
        the session's working directory rather than the server process's
        ``os.getcwd()``.

        In multi-user mode, paths under ``/home/user`` (the server process's
        default home) are automatically rewritten to the provisioned user's
        home directory, since LLMs often hardcode that path.
        
/home/user)r   z	/home/usr/N)	r	   r   isabsr   r   
startswithlennormpathjoin)r   r   r   prefixs       r   resolve_pathzUserFS.resolve_path)   s     7== 	* } l!:!:9  Fv~~#y#66 #y4F+== 7##D)))wS-=DIt D DEEEr   c                 <   | j         sdS t          j                            |          }|                    d          sdS |                    d          }t          |          dk    r4|d         }t          j                            | j                  }||k    rdS dS )zAReturn *False* if *path* is inside another user's home directory.Tz/home/r         F)	r   r	   r   abspathr   splitr   basenamer   )r   r   resolvedpartstarget_user_dirown_home_names         r   is_path_allowedzUserFS.is_path_allowedG   s    } 	47??4((""8,, 	4s##u::??#AhOG,,TY77M-//utr   c                     |                      |          s0t          dt          j                            |           d          dS )z2Reject paths inside another user's home directory.zAccess denied: z belongs to another userN)r&   PermissionErrorr	   r   r   r   r   s     r   _check_pathzUserFS._check_pathV   sP    ##D)) 	!Q"'//$"7"7QQQ  	 	r   c                    K   | j         rit          j        t          j        dd| j          d| j          |gdd           d{V  t          j        t          j        ddd|gdd           d{V  dS dS )	zFix ownership of *path* to the provisioned user.

        Also sets group-write permission so the server process (which is
        in the provisioned user's group) can overwrite the file on
        subsequent writes.
        sudochown:Tcheckcapture_outputNchmodzg+w)r   asyncio	to_thread
subprocessrunr)   s     r   _chownzUserFS._chown]   s       = 
	#T]"D"DT]"D"DdK4         
 #%.4           
	 
	r   c           	      p  K   | j         s)t          j                            |d           d{V  dS t	          j        t          j        dd| j         dd|gdd           d{V  t          j        	                    |          }t          j        	                    | j
                  }||k    r|                    |d	z             rmt	          j        t          j        dd
d|gdd           d{V  t          j                            |          }||k    r|                    |d	z             idS dS dS dS )a  Create parent directories for *path* with correct permissions.

        In multi-user mode, uses ``sudo -u`` to create directories as the
        provisioned user (so creation succeeds even inside ``755`` dirs
        made by ``run_command``), then sets ``2770`` on each directory in
        the chain so the server process has group-write access.

        In single-user mode, falls back to plain ``makedirs``.
        T)exist_okNr,   z-umkdirz-pr/   r   r2   2770)r   aiofilesr	   makedirsr3   r4   r5   r6   r   r   r   r   dirname)r   r   targetr   s       r   _ensure_parentszUserFS._ensure_parentsp   sy      } 	+&&td&;;;;;;;;;FNT4='4>t
 
 
 	
 	
 	
 	
 	
 	
 	
 !!$''w	**nn!2!24#:!>!>n#&&14         
 W__V,,F nn!2!24#:!>!>nnnnnnnnnr   c                    K   |                      |           t          j        |d          4 d{V }|                                 d{V cddd          d{V  S # 1 d{V swxY w Y   dS )zRead raw bytes from *path*.rbNr*   r<   openread)r   r   fs      r   rE   zUserFS.read   s      =t,, 	" 	" 	" 	" 	" 	" 	">>>>>>	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"s   A  
A*-A*utf-8encodingc                    K   |                      |           t          j        |d|d          4 d{V }|                                 d{V cddd          d{V  S # 1 d{V swxY w Y   dS )zRead text from *path*.rstrict)rH   errorsNrC   )r   r   rH   rF   s       r   	read_textzUserFS.read_text   s     =sXhOOO 	" 	" 	" 	" 	" 	" 	"ST>>>>>>	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"s   A##
A-0A-c                    K   |                      |           t          j        j                            |           d{V S )zCheck if *path* exists.N)r*   r<   r	   r   existsr)   s     r   rO   zUserFS.exists   E      [%,,T222222222r   c                    K   |                      |           t          j        j                            |           d{V S )z"Check if *path* is a regular file.N)r*   r<   r	   r   isfiler)   s     r   rR   zUserFS.isfile   rP   r   c                    K   |                      |           t          j        j                            |           d{V S )zCheck if *path* is a directory.N)r*   r<   r	   r   isdirr)   s     r   rT   zUserFS.isdir   sE      [%++D111111111r   c                    K   |                      |           t          j                            |           d{V }|j        |j        t          j                            |          rdnddS )z(Return size, mtime, and type for *path*.N	directoryfile)sizemodifiedtype)r*   r<   r	   statst_sizest_mtimer   rT   )r   r   ss      r   r[   zUserFS.stat   st      +""4((((((((I
#%7==#6#6BKKF
 
 	
r   c                 t    K                                     fd}t          j        |           d{V S )z3List directory contents with type, size, and mtime.c                     g } t          t          j                            D ]}t          j                            |          }                    |          s8	 t          j        |          }|                     |t          j                            |          rdnd|j	        |j
        d           # t          $ r Y w xY w| S )NrV   rW   )namerZ   rX   rY   )sortedr	   listdirr   r   r&   r[   appendrT   r\   r]   OSError)entriesra   fullr^   r   r   s       r   
_list_syncz"UserFS.listdir.<locals>._list_sync   s    Grz$//00  w||D$//++D11 	ANN $/1w}}T/B/B N !	$%J	$ $        HNs   AB88
CCNr*   r3   r4   )r   r   rh   s   `` r   rc   zUserFS.listdir   s_      	 	 	 	 	 	" &z222222222r   c                 t    K                                     fd}t          j        |           d{V S )zWalk directory tree. Returns list of (dirpath, dirnames, filenames).

        In multi-user mode, directories belonging to other users are pruned
        so their contents are never yielded.
        c                      g } t          j                  D ]A\  }}fd|D             |d d <   fd|D             }|                     ||f           B| S )Nc                 z    g | ]7}                     t          j                            |                    5|8S  r&   r	   r   r   ).0ddirpathr   s     r   
<listcomp>z7UserFS.walk.<locals>._walk_filtered.<locals>.<listcomp>   sM       ++BGLL!,D,DEE  r   c                 z    g | ]7}                     t          j                            |                    5|8S rm   rn   )ro   rF   rq   r   s     r   rr   z7UserFS.walk.<locals>._walk_filtered.<locals>.<listcomp>   sM       ++BGLL!,D,DEE  r   )r	   walkrd   )resultdirnames	filenamesrq   r   r   s      @r   _walk_filteredz#UserFS.walk.<locals>._walk_filtered   s    F02 > >,9    '      (  	 w)<====Mr   Nri   )r   r   rx   s   `` r   rt   zUserFS.walk   sa       		 	 	 	 	 	 &~666666666r   contentc                   K   |                      |           t          j                            |          }|r|                     |           d{V  t          j        |d|          4 d{V }|                    |           d{V  ddd          d{V  n# 1 d{V swxY w Y   |                     |           d{V  dS )z5Write text *content* to *path*, creating parent dirs.Nw)rH   	r*   r	   r   r>   r@   r<   rD   writer7   )r   r   ry   rH   parentrF   s         r   r}   zUserFS.write   sl     && 	/&&v.........=sX>>> 	# 	# 	# 	# 	# 	# 	#!'''"""""""""	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	# 	#kk$s   1B
B),B)datac                   K   |                      |           t          j                            |          }|r|                     |           d{V  t          j        |d          4 d{V }|                    |           d{V  ddd          d{V  n# 1 d{V swxY w Y   |                     |           d{V  dS )z1Write raw *data* to *path*, creating parent dirs.Nwbr|   )r   r   r   r~   rF   s        r   write_byteszUserFS.write_bytes   se     && 	/&&v.........=t,, 	  	  	  	  	  	  	 ''$--	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 kk$s   /B
B'*B'c                 j   K   |                      |           |                     |           d{V  dS )z$Create directory *path* and parents.N)r*   r@   r)   s     r   r:   zUserFS.mkdir   sF      ""4(((((((((((r   c                 
  K   |                      |           t          j                            |          r't	          j        t          j        |           d{V  dS t          j        	                    |           d{V  dS )z"Remove *path* (file or directory).N)
r*   r	   r   rT   r3   r4   shutilrmtreer<   remover)   s     r   r   zUserFS.remove  s      7== 	+#FM488888888888+$$T***********r   sourcedestinationc                    K   |                      |           |                      |           t          j        t          j        ||           d{V  |                     |           d{V  dS )zMove *source* to *destination*.N)r*   r3   r4   r   mover7   )r   r   r   s      r   r   zUserFS.move  s         %%%V[AAAAAAAAAkk+&&&&&&&&&&&r   )NNr   )rG   )__name__
__module____qualname____doc__strr   r   boolr&   r*   r7   r@   bytesrE   rM   rO   rR   rT   dictr[   listrc   tuplert   r}   r   r:   r   r   rm   r   r   r   r      s        ( (t (#* ( ( ( (F F F3: F F F F F<C D              &-# -$ - - - -F"s "u " " " "" "C "3 "S " " " "3 3 3 3 3 3
3 3 3 3 3 3
2 2 2 2 2 2

s 
t 
 
 
 
3# 3$t* 3 3 3 3,7s 7tE#tCy$s)2K,L'M 7 7 7 78     c  S  t         c    4        ) ) ) ) ) )
+ + + + + +' '3 '4 ' ' ' ' ' 'r   r   )r   r3   r	   r   r5   r<   aiofiles.osr   rm   r   r   <module>r      s      				          y' y' y' y' y' y' y' y' y' y'r   