11import logging
2+ import re
23
34from mfd_connect .exceptions import ConnectionCalledProcessError
45
56logger = logging .getLogger (__name__ )
67
8+ # Allowed parent directories for ramdisk mount points
9+ ALLOWED_MOUNT_PREFIXES = ("/mnt/ramdisk" ,)
10+
711
812class Ramdisk :
9- def __init__ (self , host , mount_point , size_gib ):
13+ def __init__ (self , host , mount_point : str , size_gib : int ):
1014 self ._host = host
11- self ._mount_point = mount_point
15+ self ._mount_point = self . _validate_mount_point ( mount_point )
1216 self ._size_gib = size_gib
1317
18+ @staticmethod
19+ def _validate_mount_point (mount_point : str ) -> str :
20+ """Validate mount point to prevent accidental deletion of important directories."""
21+ # Normalize path
22+ mount_point = "/" + mount_point .strip ("/" )
23+
24+ # Safety checks
25+ if (
26+ not mount_point
27+ or ".." in mount_point
28+ or "//" in mount_point
29+ or not re .match (r"^[a-zA-Z0-9/_-]+$" , mount_point )
30+ ):
31+ raise ValueError (f"Invalid mount point: { mount_point } " )
32+
33+ # Must be under allowed prefix with at least 2 path components
34+ path_parts = [p for p in mount_point .split ("/" ) if p ]
35+ if len (path_parts ) < 2 or not any (
36+ mount_point .startswith (p ) for p in ALLOWED_MOUNT_PREFIXES
37+ ):
38+ raise ValueError (
39+ f"Mount point must be under { ALLOWED_MOUNT_PREFIXES } "
40+ f"with at least 2 components: { mount_point } "
41+ )
42+
43+ return mount_point
44+
1445 def is_mounted (self ) -> bool :
1546 """Check if the mount point is currently mounted."""
1647 try :
@@ -22,39 +53,33 @@ def is_mounted(self) -> bool:
2253 return False
2354
2455 def mount (self ):
56+ """Mount a tmpfs ramdisk, unmounting any existing mount first."""
2557 try :
26- self ._host .connection .execute_command (f"sudo mkdir -p { self ._mount_point } " )
27-
28- # Check if already mounted and unmount first
2958 if self .is_mounted ():
30- logger .warning (
31- f"Mount point { self ._mount_point } is already mounted, unmounting first"
32- )
59+ logger .warning (f"{ self ._mount_point } already mounted, unmounting first" )
3360 self .unmount ()
3461
62+ self ._host .connection .execute_command (f"sudo mkdir -p { self ._mount_point } " )
3563 self ._host .connection .execute_command (
36- f"sudo mount -t tmpfs -o size={ self ._size_gib } G,nosuid,nodev,noexec tmpfs { self ._mount_point } "
64+ f"sudo mount -t tmpfs -o size={ self ._size_gib } G,nosuid,nodev,noexec "
65+ f"tmpfs { self ._mount_point } "
3766 )
67+ logger .info (f"Mounted ramdisk at { self ._mount_point } " )
3868 except ConnectionCalledProcessError as e :
3969 logger .error (f"Failed to mount ramdisk at { self ._mount_point } : { e } " )
4070 raise
4171
4272 def unmount (self ):
4373 """Unmount the ramdisk and remove the mount point directory."""
4474 try :
45- if not self .is_mounted ():
46- logger .warning (f"Mount point { self ._mount_point } is not mounted" )
47- else :
75+ if self .is_mounted ():
4876 self ._host .connection .execute_command (
4977 f"sudo umount { self ._mount_point } "
5078 )
51- logger .info (f"Successfully unmounted { self ._mount_point } " )
52- except ConnectionCalledProcessError as e :
53- logger .error (f"Failed to unmount { self ._mount_point } : { e } " )
54- raise
79+ logger .info (f"Unmounted { self ._mount_point } " )
5580
56- try :
57- self ._host .connection .execute_command (f"sudo rmdir { self ._mount_point } " )
58- logger .info (f"Successfully removed directory { self ._mount_point } " )
81+ # Safe to remove - already validated in __init__, check not mounted
82+ self ._host .connection .execute_command (f"sudo rm -rf { self ._mount_point } " )
83+ logger .debug (f"Removed directory { self ._mount_point } " )
5984 except ConnectionCalledProcessError as e :
60- logger .warning (f"Could not remove directory { self ._mount_point } : { e } " )
85+ logger .warning (f"Cleanup issue at { self ._mount_point } : { e } " )
0 commit comments