11__author__ = 'desultory'
2- __version__ = '1.1.1 '
2+ __version__ = '1.1.2 '
33
44from pathlib import Path
55
66
77MOUNT_PARAMETERS = ['destination' , 'source' , 'type' , 'options' , 'base_mount' , 'skip_unmount' , 'remake_mountpoint' ]
8+ SOURCE_TYPES = ['uuid' , 'partuuid' , 'label' ]
89
910
1011def _process_mounts_multi (self , mount_name , mount_config ):
1112 """
1213 Processes the passed mounts into fstab mount objects
1314 under 'mounts'
1415 """
16+ # If the mount already exists, merge the options and update it
1517 if mount_name in self ['mounts' ]:
1618 self .logger .info ("Updating mount: %s" % mount_name )
1719 self .logger .debug ("[%s] Updating mount with: %s" % (mount_name , mount_config ))
@@ -21,14 +23,36 @@ def _process_mounts_multi(self, mount_name, mount_config):
2123 mount_config .pop ('options' )
2224 mount_config = dict (self ['mounts' ][mount_name ], ** mount_config )
2325
24- for parameter in mount_config :
25- if parameter not in MOUNT_PARAMETERS :
26+ # Validate the mount config
27+ for parameter , value in mount_config .items ():
28+ if parameter == 'source' and isinstance (value , dict ):
29+ for source_type in SOURCE_TYPES :
30+ if source_type in value :
31+ break
32+ else :
33+ self .logger .info ("Valid source types: %s" % SOURCE_TYPES )
34+ raise ValueError ("Invalid source type in mount: %s" % value )
35+ elif parameter not in MOUNT_PARAMETERS :
2636 raise ValueError ("Invalid parameter in mount: %s" % parameter )
2737
38+ # Set defaults
2839 mount_config ['destination' ] = Path (mount_config .get ('destination' , mount_name ))
2940 mount_config ['base_mount' ] = mount_config .get ('base_mount' , False )
3041 mount_config ['options' ] = set (mount_config .get ('options' , '' ))
3142
43+ # Check if the mount exists on the host if it's not a base mount
44+ if not mount_config ['base_mount' ]:
45+ # Only check the root mount after the source has been defined
46+ if mount_name == 'root' :
47+ if 'source' in mount_config :
48+ _validate_host_mount (self , mount_config , '/' )
49+ # The source must be defined for non-root mounts
50+ elif 'source' not in mount_config :
51+ raise ValueError ("[%s] No source specified in mount: %s" % (mount_name , mount_config ))
52+ else :
53+ _validate_host_mount (self , mount_config )
54+
55+ # Add imports based on the mount type
3256 if mount_type := mount_config .get ('type' ):
3357 if mount_type == 'vfat' :
3458 self ['_kmod_depend' ] = 'vfat'
@@ -54,14 +78,11 @@ def _get_mount_source(self, mount, pad=False):
5478
5579 out_str = ''
5680 if isinstance (source , dict ):
57- if 'uuid' in source :
58- out_str = f"UUID={ source ['uuid' ]} "
59- elif 'partuuid' in source :
60- out_str = f"PARTUUID={ source ['partuuid' ]} "
61- elif 'label' in source :
62- out_str = f"LABEL={ source ['label' ]} "
63- else :
64- raise ValueError ("Unable to process source entry: %s" % repr (source ))
81+ # Create the source string from the dict
82+ for source_type in SOURCE_TYPES :
83+ if source_type in source :
84+ out_str = f"{ source_type .upper ()} ={ source [source_type ]} "
85+ break
6586 else :
6687 out_str = source
6788
@@ -157,6 +178,11 @@ def _get_mounts_source(self, mount):
157178 """
158179 Returns the source device of a mountpoint on /proc/mounts
159180 """
181+ # Make the mount a string and ensure it starts with a /
182+ mount = str (mount )
183+ if not mount .startswith ('/' ) and not mount .startswith (' /' ):
184+ mount = '/' + mount
185+
160186 self .logger .debug ("Getting mount source for: %s" % mount )
161187 # Add space padding to the mount name
162188 mount = mount if mount .startswith (' ' ) else ' ' + mount
@@ -177,9 +203,16 @@ def _get_blkid_info(self, device):
177203 """
178204 Gets the blkid info for a device
179205 """
206+ from subprocess import run
180207 self .logger .debug ("Getting blkid info for: %s" % device )
181208
182- mount_info = self ._run (['blkid' , str (device )]).stdout .decode ().strip ()
209+ cmd = run (['blkid' , str (device )], capture_output = True )
210+ if cmd .returncode != 0 :
211+ self .logger .warning ("Unable to find blkid info for: %s" % device )
212+ return None
213+
214+ mount_info = cmd .stdout .decode ().strip ()
215+
183216 if not mount_info :
184217 self .logger .warning ("Unable to find blkid info for: %s" % device )
185218 return None
@@ -188,33 +221,50 @@ def _get_blkid_info(self, device):
188221 return mount_info
189222
190223
191- def mount_root (self ):
224+ def _validate_host_mount (self , mount , destination_path = None ):
192225 """
193- Mounts the root partition.
194- Warns if the root partition isn't found on the current system.
226+ Checks if a defined mount exists on the host
195227 """
196- root_source = self .config_dict ['mounts' ]['root' ]['source' ]
197- host_root_dev = _get_mounts_source (self , '/' )
228+ if not self ['hostonly' ]:
229+ self .logger .debug ("Skipping host mount check as hostonly is not set" )
230+ return
231+
232+ source = mount ['source' ]
233+ destination_path = mount ['destination' ] if destination_path is None else destination_path
198234
199- # If the root device is a string, check that it's the same path as the host root mount
200- if isinstance (root_source , str ):
201- if root_source != host_root_dev :
202- self .logger .warning ("Root device mismatch. Expected: %s, Found: %s" % (root_source , host_root_dev ))
203- elif isinstance (root_source , dict ):
204- # If the root device is a dict, check that the uuid, partuuid, or label matches the host root mount
205- if blkid_info := _get_blkid_info (self , host_root_dev ):
235+ host_source_dev = _get_mounts_source (self , destination_path )
236+ if not host_source_dev :
237+ self .logger .error ("Unable to find mount on host system: %s" % destination_path )
238+ return
239+ else :
240+ self .logger .debug ("Checking host volume: %s" % host_source_dev )
241+
242+ if isinstance (source , str ):
243+ if source != host_source_dev :
244+ self .logger .warning ("Host device mismatch. Expected: %s, Found: %s" % (source , host_source_dev ))
245+ elif isinstance (source , dict ):
246+ # If the source is a dict, check that the uuid, partuuid, or label matches the host mount
247+ if blkid_info := _get_blkid_info (self , host_source_dev ):
206248 # Unholy for-else, breaks if the uuid, partuuid, or label matches, otherwise warns
207- for key , value in root_source .items ():
208- search_str = f" { key .upper ()} =\ "{ value } \" "
249+ for key , value in source .items ():
250+ search_str = f' { key .upper ()} ="{ value } "'
209251 if value in blkid_info :
210- self .logger .debug ("Found root device match: %s" % search_str )
211- break
252+ self .logger .debug ("Fount host device match: %s" % search_str )
253+ return True
212254 else :
213- self .logger .warning ( "Configuration root device not found on host system. Expected: %s" % root_source )
214- self .logger .warning ("Host system root device info: %s" % blkid_info )
255+ self .logger .error ( "Mount device not found on host system. Expected: %s" % source )
256+ self .logger .error ("Host device info: %s" % blkid_info )
215257 else :
216- self .logger .warning ("Unable to find blkid info for: %s" % root_source )
258+ self .logger .warning ("Unable to find blkid info for: %s" % host_source_dev )
259+
260+ raise ValueError ("Unable to validate host mount: %s" % mount )
217261
262+
263+ def mount_root (self ):
264+ """
265+ Mounts the root partition.
266+ Warns if the root partition isn't found on the current system.
267+ """
218268 root_path = self .config_dict ['mounts' ]['root' ]['destination' ]
219269
220270 return [f"mount { root_path } || (echo 'Failed to mount root partition' && bash)" ]
0 commit comments