44
55from zenlib .util import pretty_print , colorize
66
7- __version__ = "1.5.0 "
7+ __version__ = "1.5.3 "
88__author__ = "desultory"
99
1010
@@ -39,9 +39,6 @@ def _mkdir(self, path: Path, resolve_build=True) -> None:
3939 If resolve_build is True, the path is resolved to the build directory.
4040 If not, the provided path is used as-is.
4141 """
42- from os import mkdir
43- from os .path import isdir
44-
4542 if resolve_build :
4643 path = self ._get_build_path (path )
4744
@@ -52,12 +49,15 @@ def _mkdir(self, path: Path, resolve_build=True) -> None:
5249 else :
5350 path_dir = path
5451
55- if not isdir (path_dir .parent ):
52+ if path_dir .is_symlink ():
53+ return self .logger .debug ("Skipping symlink directory: %s" % path_dir )
54+
55+ if not path_dir .parent .is_dir ():
5656 self .logger .debug ("Parent directory does not exist: %s" % path_dir .parent )
5757 self ._mkdir (path_dir .parent , resolve_build = False )
5858
59- if not isdir ( path_dir ):
60- mkdir (path )
59+ if not path_dir . is_dir ( ):
60+ path_dir . mkdir ()
6161 self .logger .log (self ["_build_log_level" ], "Created directory: %s" % path )
6262 else :
6363 self .logger .debug ("Directory already exists: %s" % path_dir )
@@ -100,7 +100,14 @@ def _write(self, file_name: Union[Path, str], contents: list[str], chmod_mask=0o
100100 self .logger .debug ("[%s] Set file permissions: %s" % (file_path , chmod_mask ))
101101
102102 def _copy (self , source : Union [Path , str ], dest = None ) -> None :
103- """Copies a file into the initramfs build directory."""
103+ """Copies a file into the initramfs build directory.
104+ If a destination is not provided, the source is used, under the build directory.
105+
106+ If the destination parent is a symlink, the symlink is resolved.
107+ Crates parent directories if they do not exist
108+
109+ Raises a RuntimeError if the destination path is not within the build directory.
110+ """
104111 from shutil import copy2
105112
106113 if not isinstance (source , Path ):
@@ -111,10 +118,12 @@ def _copy(self, source: Union[Path, str], dest=None) -> None:
111118 dest = source
112119
113120 dest_path = self ._get_build_path (dest )
121+ build_base = self ._get_build_path ("/" )
114122
115123 while dest_path .parent .is_symlink ():
116- self .logger .debug ("Resolving symlink: %s" % dest_path .parent )
117- dest_path = self ._get_build_path (dest_path .parent .resolve () / dest_path .name )
124+ resolved_path = dest_path .parent .resolve () / dest_path .name
125+ self .logger .debug ("Resolved symlink: %s -> %s" % (dest_path .parent , resolved_path ))
126+ dest_path = self ._get_build_path (resolved_path )
118127
119128 if not dest_path .parent .is_dir ():
120129 self .logger .debug ("Parent directory for '%s' does not exist: %s" % (dest_path .name , dest_path .parent ))
@@ -126,23 +135,41 @@ def _copy(self, source: Union[Path, str], dest=None) -> None:
126135 self .logger .debug ("Destination is a directory, adding source filename: %s" % source .name )
127136 dest_path = dest_path / source .name
128137
138+ try : # Ensure the target is in the build directory
139+ dest_path .relative_to (build_base )
140+ except ValueError as e :
141+ raise RuntimeError ("Destination path is not within the build directory: %s" % dest_path ) from e
142+
129143 self .logger .log (self ["_build_log_level" ], "Copying '%s' to '%s'" % (source , dest_path ))
130144 copy2 (source , dest_path )
131145
132146 def _symlink (self , source : Union [Path , str ], target : Union [Path , str ]) -> None :
133- """Creates a symlink"""
147+ """Creates a symlink in the build directory.
148+ If the target is a directory, the source filename is appended to the target path.
149+
150+ Creates parent directories if they do not exist.
151+ If the symlink path is under a symlink, resolve to the actual path.
152+
153+ If the symlink source is under a symlink in the build directory, resolve to the actual path.
154+ """
134155 if not isinstance (source , Path ):
135156 source = Path (source )
136157
137158 target = self ._get_build_path (target )
138159
160+ while target .parent .is_symlink ():
161+ self .logger .debug ("Resolving target parent symlink: %s" % target .parent )
162+ target = self ._get_build_path (target .parent .resolve () / target .name )
163+
139164 if not target .parent .is_dir ():
140165 self .logger .debug ("Parent directory for '%s' does not exist: %s" % (target .name , target .parent ))
141166 self ._mkdir (target .parent , resolve_build = False )
142167
143- while target .parent .is_symlink ():
144- self .logger .debug ("Resolving symlink: %s" % target .parent )
145- target = self ._get_build_path (target .parent .resolve () / target .name )
168+ build_source = self ._get_build_path (source )
169+ while build_source .parent .is_symlink ():
170+ self .logger .debug ("Resolving source parent symlink: %s" % build_source .parent )
171+ build_source = self ._get_build_path (build_source .parent .resolve () / build_source .name )
172+ source = build_source .relative_to (self ._get_build_path ("/" ))
146173
147174 if target .is_symlink ():
148175 if target .resolve () == source :
@@ -153,6 +180,9 @@ def _symlink(self, source: Union[Path, str], target: Union[Path, str]) -> None:
153180 else :
154181 raise RuntimeError ("Symlink already exists: %s -> %s" % (target , target .resolve ()))
155182
183+ if target .relative_to (self ._get_build_path ("/" )) == source :
184+ return self .logger .debug ("Cannot symlink to self: %s -> %s" % (target , source ))
185+
156186 self .logger .debug ("Creating symlink: %s -> %s" % (target , source ))
157187 target .symlink_to (source )
158188
0 commit comments