Add mount and umount syscall
This commit is contained in:
		
							parent
							
								
									36918e42bf
								
							
						
					
					
						commit
						7bc2c336b6
					
				| @ -277,7 +277,7 @@ The two aforementioned requirements are not only satisfied by the Occlum toolcha | ||||
| 
 | ||||
| To debug an app running upon Occlum, one can harness Occlum's builtin support for GDB via `occlum gdb` command. More info can be found [here](demos/gdb_support/). | ||||
| 
 | ||||
| Meanwhile, one can use `occlum mount` command to access and manipulate the secure filesystem for debug purpose. More info can be found [here](docs/occlum_mount.md). | ||||
| Meanwhile, one can use `occlum mount` command to access and manipulate the secure filesystem for debug purpose. More info can be found [here](docs/mount_cmd.md). | ||||
| 
 | ||||
| If the cause of a problem does not seem to be the app but Occlum itself, then one can take a glimpse into the inner workings of Occlum by checking out its log. Occlum's log level can be adjusted through `OCCLUM_LOG_LEVEL` environment variable. It has six levels: `off`, `error`, `warn`, `debug`, `info`, and `trace`. The default value is `off`, i.e., showing no log messages at all. The most verbose level is `trace`. | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										2
									
								
								deps/sefs
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										2
									
								
								deps/sefs
									
									
									
									
										vendored
									
									
								
							| @ -1 +1 @@ | ||||
| Subproject commit 6eaf1d2f50327631cf62a965f30d56ce40934c76 | ||||
| Subproject commit cee7425b3131ff6bec76aa6a9e9f189386ba47d6 | ||||
							
								
								
									
										44
									
								
								docs/runtime_mount.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										44
									
								
								docs/runtime_mount.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| # Mount and Unmount Filesystems at Runtime | ||||
| 
 | ||||
| ## Background | ||||
| Users can specify the mount configuration in the `Occlum.json` file, then the filesystems are mounted during the libOS startup phase. While this design provides a safe and simple way to access files, it is not as convenient as traditional Host OS. Apps are not allowd to mount and unmount filesystems at runtime. | ||||
| 
 | ||||
| ## How to mount filesystems at runtime? | ||||
| Apps running inside Occlum can mount some specific filesystems via the [mount()](https://man7.org/linux/man-pages/man2/mount.2.html) system call. This makes it flexible to mount and access files at runtime. | ||||
| 
 | ||||
| Currently, we only support to create a new mount with the trusted UnionFS consisting of SEFSs or the untrusted HostFS. The mountpoint is not allowd to be the root directory("/"). | ||||
| 
 | ||||
| ### 1. Mount trusted UnionFS consisting of SEFSs | ||||
| Example code: | ||||
| 
 | ||||
| ``` | ||||
| mount("unionfs", "<target_dir>", "unionfs", 0/* mountflags is ignored */, | ||||
|       "lowerdir=<lower>,upperdir=<upper>,key=<128-bit-key>") | ||||
| ``` | ||||
| 
 | ||||
| Mount options: | ||||
| 
 | ||||
| - The `lowerdir=<lower>` is a mandatory field, which describes the directory path of the RO SEFS on Host OS. | ||||
| - The `upperdir=<upper>` is a mandatory field, which describes the directory path of the RW SEFS on Host OS. | ||||
| - The `key=<128-bit-key>` is an optional field, which describes the 128bit key used to encrypt or decrypt the FS. Here is an example of the key: `key=c7-32-b3-ed-44-df-ec-7b-25-2d-9a-32-38-8d-58-61`. If this field is not provided, it will use the automatic key derived from the enclave sealing key. | ||||
| 
 | ||||
| ### 2. Mount untrusted HostFS | ||||
| Example code: | ||||
| 
 | ||||
| ``` | ||||
| mount("hostfs", “<target_dir>”, "hostfs", 0/* mountflags is ignored */, | ||||
|       "dir=<host_dir>") | ||||
| ``` | ||||
| 
 | ||||
| Mount options: | ||||
| 
 | ||||
| - The `dir=<host_dir>` is a mandatory field, which describes the directory path on Host OS. | ||||
| 
 | ||||
| ## How to unmount filesystems at runtime? | ||||
| 
 | ||||
| Apps running inside Occlum can unmount some specific filesystems via the [umount()/umount2()](https://man7.org/linux/man-pages/man2/umount.2.html) system calls. Note that root directory("/") is not allowd to unmount. | ||||
| 
 | ||||
| Example code: | ||||
| ``` | ||||
| umount("<target_dir>") | ||||
| ``` | ||||
| @ -57,7 +57,7 @@ fn conf_get_hardcoded_file_mac() -> sgx_aes_gcm_128bit_tag_t { | ||||
|     mac | ||||
| } | ||||
| 
 | ||||
| fn parse_mac(mac_str: &str) -> Result<sgx_aes_gcm_128bit_tag_t> { | ||||
| pub fn parse_mac(mac_str: &str) -> Result<sgx_aes_gcm_128bit_tag_t> { | ||||
|     let bytes_str_vec = { | ||||
|         let bytes_str_vec: Vec<&str> = mac_str.split("-").collect(); | ||||
|         if bytes_str_vec.len() != 16 { | ||||
| @ -73,6 +73,22 @@ fn parse_mac(mac_str: &str) -> Result<sgx_aes_gcm_128bit_tag_t> { | ||||
|     Ok(mac) | ||||
| } | ||||
| 
 | ||||
| pub fn parse_key(key_str: &str) -> Result<sgx_key_128bit_t> { | ||||
|     let bytes_str_vec = { | ||||
|         let bytes_str_vec: Vec<&str> = key_str.split("-").collect(); | ||||
|         if bytes_str_vec.len() != 16 { | ||||
|             return_errno!(EINVAL, "The length or format of KEY string is invalid"); | ||||
|         } | ||||
|         bytes_str_vec | ||||
|     }; | ||||
| 
 | ||||
|     let mut key: sgx_key_128bit_t = Default::default(); | ||||
|     for (byte_i, byte_str) in bytes_str_vec.iter().enumerate() { | ||||
|         key[byte_i] = u8::from_str_radix(byte_str, 16).map_err(|e| errno!(e))?; | ||||
|     } | ||||
|     Ok(key) | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct Config { | ||||
|     pub resource_limits: ConfigResourceLimits, | ||||
| @ -119,7 +135,26 @@ pub enum ConfigMountFsType { | ||||
|     TYPE_PROCFS, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| impl ConfigMountFsType { | ||||
|     pub fn from_input(input: &str) -> Result<ConfigMountFsType> { | ||||
|         const ALL_FS_TYPES: [&str; 6] = ["sefs", "hostfs", "ramfs", "unionfs", "devfs", "procfs"]; | ||||
| 
 | ||||
|         let type_ = match input { | ||||
|             "sefs" => ConfigMountFsType::TYPE_SEFS, | ||||
|             "hostfs" => ConfigMountFsType::TYPE_HOSTFS, | ||||
|             "ramfs" => ConfigMountFsType::TYPE_RAMFS, | ||||
|             "unionfs" => ConfigMountFsType::TYPE_UNIONFS, | ||||
|             "devfs" => ConfigMountFsType::TYPE_DEVFS, | ||||
|             "procfs" => ConfigMountFsType::TYPE_PROCFS, | ||||
|             _ => { | ||||
|                 return_errno!(EINVAL, "Unsupported file system type"); | ||||
|             } | ||||
|         }; | ||||
|         Ok(type_) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Default, Debug)] | ||||
| pub struct ConfigMountOptions { | ||||
|     pub mac: Option<sgx_aes_gcm_128bit_tag_t>, | ||||
|     pub layers: Option<Vec<ConfigMount>>, | ||||
| @ -190,19 +225,7 @@ impl ConfigEnv { | ||||
| 
 | ||||
| impl ConfigMount { | ||||
|     fn from_input(input: &InputConfigMount) -> Result<ConfigMount> { | ||||
|         const ALL_FS_TYPES: [&str; 6] = ["sefs", "hostfs", "ramfs", "unionfs", "devfs", "procfs"]; | ||||
| 
 | ||||
|         let type_ = match input.type_.as_str() { | ||||
|             "sefs" => ConfigMountFsType::TYPE_SEFS, | ||||
|             "hostfs" => ConfigMountFsType::TYPE_HOSTFS, | ||||
|             "ramfs" => ConfigMountFsType::TYPE_RAMFS, | ||||
|             "unionfs" => ConfigMountFsType::TYPE_UNIONFS, | ||||
|             "devfs" => ConfigMountFsType::TYPE_DEVFS, | ||||
|             "procfs" => ConfigMountFsType::TYPE_PROCFS, | ||||
|             _ => { | ||||
|                 return_errno!(EINVAL, "Unsupported file system type"); | ||||
|             } | ||||
|         }; | ||||
|         let type_ = ConfigMountFsType::from_input(input.type_.as_str())?; | ||||
|         let target = { | ||||
|             let target = PathBuf::from(&input.target); | ||||
|             if !target.starts_with("/") { | ||||
|  | ||||
| @ -98,6 +98,7 @@ impl ToErrno for rcore_fs::vfs::FsError { | ||||
|             FsError::NameTooLong => ENAMETOOLONG, | ||||
|             FsError::FileTooBig => EFBIG, | ||||
|             FsError::OpNotSupported => EOPNOTSUPP, | ||||
|             FsError::NotMountPoint => EINVAL, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -40,7 +40,12 @@ pub fn init_devfs() -> Result<Arc<MountFS>> { | ||||
|     let mountable_devfs = MountFS::new(devfs); | ||||
|     // Mount the ramfs at '/shm'
 | ||||
|     let ramfs = RamFS::new(); | ||||
|     mount_fs_at(ramfs, &mountable_devfs.root_inode(), &Path::new("/shm"))?; | ||||
|     mount_fs_at( | ||||
|         ramfs, | ||||
|         &mountable_devfs.root_inode(), | ||||
|         &Path::new("/shm"), | ||||
|         true, | ||||
|     )?; | ||||
|     // TODO: Add stdio(stdin, stdout, stderr) into DevFS
 | ||||
|     Ok(mountable_devfs) | ||||
| } | ||||
|  | ||||
| @ -2,7 +2,9 @@ use super::*; | ||||
| 
 | ||||
| pub use self::chdir::do_chdir; | ||||
| pub use self::getcwd::do_getcwd; | ||||
| pub use self::mount::do_mount_rootfs; | ||||
| pub use self::mount::{ | ||||
|     do_mount, do_mount_rootfs, do_umount, MountFlags, MountOptions, UmountFlags, | ||||
| }; | ||||
| pub use self::statfs::{do_fstatfs, do_statfs, Statfs}; | ||||
| pub use self::sync::do_sync; | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,12 @@ | ||||
| use std::path::PathBuf; | ||||
| use std::sync::Once; | ||||
| 
 | ||||
| use config::{parse_key, parse_mac, ConfigMount, ConfigMountFsType, ConfigMountOptions}; | ||||
| use rcore_fs_mountfs::MNode; | ||||
| use util::mem_util::from_user; | ||||
| use util::resolv_conf_util::write_resolv_conf; | ||||
| 
 | ||||
| use super::rootfs::{mount_nonroot_fs_according_to, open_root_fs_according_to}; | ||||
| use super::rootfs::{mount_nonroot_fs_according_to, open_root_fs_according_to, umount_nonroot_fs}; | ||||
| use super::*; | ||||
| 
 | ||||
| lazy_static! { | ||||
| @ -21,7 +26,7 @@ pub fn do_mount_rootfs( | ||||
|         let rootfs = open_root_fs_according_to(&user_config.mount, user_key)?; | ||||
|         rootfs.root_inode() | ||||
|     }; | ||||
|     mount_nonroot_fs_according_to(&new_root_inode, &user_config.mount, user_key)?; | ||||
|     mount_nonroot_fs_according_to(&new_root_inode, &user_config.mount, user_key, true)?; | ||||
|     MOUNT_ONCE.call_once(|| { | ||||
|         let mut root_inode = ROOT_INODE.write().unwrap(); | ||||
|         root_inode.fs().sync().expect("failed to sync old rootfs"); | ||||
| @ -34,3 +39,296 @@ pub fn do_mount_rootfs( | ||||
| 
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn do_mount( | ||||
|     source: &str, | ||||
|     target: &str, | ||||
|     flags: MountFlags, | ||||
|     options: MountOptions, | ||||
| ) -> Result<()> { | ||||
|     debug!( | ||||
|         "mount: source: {}, target: {}, flags: {:?}, options: {:?}", | ||||
|         source, target, flags, options | ||||
|     ); | ||||
| 
 | ||||
|     let target = if target == "/" { | ||||
|         return_errno!(EPERM, "can not mount on root"); | ||||
|     } else if target.len() > 0 && target.as_bytes()[0] == b'/' { | ||||
|         PathBuf::from(target) | ||||
|     } else { | ||||
|         let thread = current!(); | ||||
|         let fs = thread.fs().read().unwrap(); | ||||
|         PathBuf::from(fs.convert_to_abs_path(target)) | ||||
|     }; | ||||
| 
 | ||||
|     if flags.contains(MountFlags::MS_REMOUNT) | ||||
|         || flags.contains(MountFlags::MS_BIND) | ||||
|         || flags.contains(MountFlags::MS_SHARED) | ||||
|         || flags.contains(MountFlags::MS_PRIVATE) | ||||
|         || flags.contains(MountFlags::MS_SLAVE) | ||||
|         || flags.contains(MountFlags::MS_UNBINDABLE) | ||||
|         || flags.contains(MountFlags::MS_MOVE) | ||||
|     { | ||||
|         return_errno!(EINVAL, "Only support to create a new mount"); | ||||
|     } | ||||
| 
 | ||||
|     let (mount_configs, user_key) = match options { | ||||
|         MountOptions::UnionFS(unionfs_options) => { | ||||
|             let mc = { | ||||
|                 let image_mc = ConfigMount { | ||||
|                     type_: ConfigMountFsType::TYPE_SEFS, | ||||
|                     target: target.clone(), | ||||
|                     source: Some(unionfs_options.lower_dir.clone()), | ||||
|                     options: Default::default(), | ||||
|                 }; | ||||
|                 let container_mc = ConfigMount { | ||||
|                     type_: ConfigMountFsType::TYPE_SEFS, | ||||
|                     target: target.clone(), | ||||
|                     source: Some(unionfs_options.upper_dir.clone()), | ||||
|                     options: Default::default(), | ||||
|                 }; | ||||
| 
 | ||||
|                 ConfigMount { | ||||
|                     type_: ConfigMountFsType::TYPE_UNIONFS, | ||||
|                     target, | ||||
|                     source: None, | ||||
|                     options: ConfigMountOptions { | ||||
|                         layers: Some(vec![image_mc, container_mc]), | ||||
|                         ..Default::default() | ||||
|                     }, | ||||
|                 } | ||||
|             }; | ||||
|             (vec![mc], unionfs_options.key) | ||||
|         } | ||||
|         MountOptions::SEFS(sefs_options) => { | ||||
|             let mc = ConfigMount { | ||||
|                 type_: ConfigMountFsType::TYPE_SEFS, | ||||
|                 target, | ||||
|                 source: Some(sefs_options.dir.clone()), | ||||
|                 options: ConfigMountOptions { | ||||
|                     mac: sefs_options.mac, | ||||
|                     ..Default::default() | ||||
|                 }, | ||||
|             }; | ||||
|             (vec![mc], sefs_options.key) | ||||
|         } | ||||
|         MountOptions::HostFS(dir) => { | ||||
|             let mc = ConfigMount { | ||||
|                 type_: ConfigMountFsType::TYPE_HOSTFS, | ||||
|                 target, | ||||
|                 source: Some(dir.clone()), | ||||
|                 options: Default::default(), | ||||
|             }; | ||||
|             (vec![mc], None) | ||||
|         } | ||||
|         MountOptions::RamFS => { | ||||
|             let mc = ConfigMount { | ||||
|                 type_: ConfigMountFsType::TYPE_RAMFS, | ||||
|                 target, | ||||
|                 source: None, | ||||
|                 options: Default::default(), | ||||
|             }; | ||||
|             (vec![mc], None) | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let mut root_inode = ROOT_INODE.write().unwrap(); | ||||
|     // Should we sync the fs before mount?
 | ||||
|     root_inode.fs().sync()?; | ||||
|     let follow_symlink = !flags.contains(MountFlags::MS_NOSYMFOLLOW); | ||||
|     mount_nonroot_fs_according_to(&*root_inode, &mount_configs, &user_key, follow_symlink)?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn do_umount(target: &str, flags: UmountFlags) -> Result<()> { | ||||
|     debug!("umount: target: {}, flags: {:?}", target, flags); | ||||
| 
 | ||||
|     let target = if target == "/" { | ||||
|         return_errno!(EPERM, "cannot umount rootfs"); | ||||
|     } else if target.len() > 0 && target.as_bytes()[0] == b'/' { | ||||
|         target.to_owned() | ||||
|     } else { | ||||
|         let thread = current!(); | ||||
|         let fs = thread.fs().read().unwrap(); | ||||
|         fs.convert_to_abs_path(target) | ||||
|     }; | ||||
| 
 | ||||
|     let mut root_inode = ROOT_INODE.write().unwrap(); | ||||
|     // Should we sync the fs before umount?
 | ||||
|     root_inode.fs().sync()?; | ||||
|     let follow_symlink = !flags.contains(UmountFlags::UMOUNT_NOFOLLOW); | ||||
|     umount_nonroot_fs(&*root_inode, &target, follow_symlink)?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| bitflags! { | ||||
|     pub struct MountFlags: u32 { | ||||
|         const MS_RDONLY = 1; | ||||
|         const MS_NOSUID = 2; | ||||
|         const MS_NODEV = 4; | ||||
|         const MS_NOEXEC = 8; | ||||
|         const MS_SYNCHRONOUS = 16; | ||||
|         const MS_REMOUNT = 32; | ||||
|         const MS_MANDLOCK = 64; | ||||
|         const MS_DIRSYNC = 128; | ||||
|         const MS_NOSYMFOLLOW = 256; | ||||
|         const MS_NOATIME = 1024; | ||||
|         const MS_NODIRATIME = 2048; | ||||
|         const MS_BIND = 4096; | ||||
|         const MS_MOVE = 8192; | ||||
|         const MS_REC = 16384; | ||||
|         const MS_SILENT = 32768; | ||||
|         const MS_POSIXACL = 1 << 16; | ||||
|         const MS_UNBINDABLE = 1 << 17; | ||||
|         const MS_PRIVATE = 1 << 18; | ||||
|         const MS_SLAVE = 1 << 19; | ||||
|         const MS_SHARED = 1 << 20; | ||||
|         const MS_RELATIME = 1 << 21; | ||||
|         const MS_KERNMOUNT = 1 << 22; | ||||
|         const MS_I_VERSION = 1 << 23; | ||||
|         const MS_STRICTATIME = 1 << 24; | ||||
|         const MS_LAZYTIME = 1 << 25; | ||||
|         const MS_SUBMOUNT = 1 << 26; | ||||
|         const MS_NOREMOTELOCK = 1 << 27; | ||||
|         const MS_NOSEC = 1 << 28; | ||||
|         const MS_BORN = 1 << 29; | ||||
|         const MS_ACTIVE = 1 << 30; | ||||
|         const MS_NOUSER = 1 << 31; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum MountOptions { | ||||
|     UnionFS(UnionFSMountOptions), | ||||
|     SEFS(SEFSMountOptions), | ||||
|     HostFS(PathBuf), | ||||
|     RamFS, | ||||
| } | ||||
| 
 | ||||
| impl MountOptions { | ||||
|     pub fn from_fs_type_and_options(type_: &ConfigMountFsType, options: *const i8) -> Result<Self> { | ||||
|         Ok(match type_ { | ||||
|             ConfigMountFsType::TYPE_SEFS => { | ||||
|                 let sefs_mount_options = { | ||||
|                     let options = from_user::clone_cstring_safely(options)? | ||||
|                         .to_string_lossy() | ||||
|                         .into_owned(); | ||||
|                     SEFSMountOptions::from_input(options.as_str())? | ||||
|                 }; | ||||
|                 Self::SEFS(sefs_mount_options) | ||||
|             } | ||||
|             ConfigMountFsType::TYPE_UNIONFS => { | ||||
|                 let unionfs_mount_options = { | ||||
|                     let options = from_user::clone_cstring_safely(options)? | ||||
|                         .to_string_lossy() | ||||
|                         .into_owned(); | ||||
|                     UnionFSMountOptions::from_input(options.as_str())? | ||||
|                 }; | ||||
|                 Self::UnionFS(unionfs_mount_options) | ||||
|             } | ||||
|             ConfigMountFsType::TYPE_HOSTFS => { | ||||
|                 let options = from_user::clone_cstring_safely(options)? | ||||
|                     .to_string_lossy() | ||||
|                     .into_owned(); | ||||
|                 let dir = { | ||||
|                     let options: Vec<&str> = options.split(",").collect(); | ||||
|                     let dir = options | ||||
|                         .iter() | ||||
|                         .find_map(|s| s.strip_prefix("dir=")) | ||||
|                         .ok_or_else(|| errno!(EINVAL, "no dir options"))?; | ||||
|                     PathBuf::from(dir) | ||||
|                 }; | ||||
|                 Self::HostFS(dir) | ||||
|             } | ||||
|             ConfigMountFsType::TYPE_RAMFS => Self::RamFS, | ||||
|             _ => { | ||||
|                 return_errno!(EINVAL, "unsupported fs type"); | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct UnionFSMountOptions { | ||||
|     lower_dir: PathBuf, | ||||
|     upper_dir: PathBuf, | ||||
|     key: Option<sgx_key_128bit_t>, | ||||
| } | ||||
| 
 | ||||
| impl UnionFSMountOptions { | ||||
|     pub fn from_input(input: &str) -> Result<Self> { | ||||
|         let options: Vec<&str> = input.split(",").collect(); | ||||
| 
 | ||||
|         let lower_dir = options | ||||
|             .iter() | ||||
|             .find_map(|s| s.strip_prefix("lowerdir=")) | ||||
|             .ok_or_else(|| errno!(EINVAL, "no lowerdir options"))?; | ||||
|         let upper_dir = options | ||||
|             .iter() | ||||
|             .find_map(|s| s.strip_prefix("upperdir=")) | ||||
|             .ok_or_else(|| errno!(EINVAL, "no upperdir options"))?; | ||||
|         let key = match options.iter().find_map(|s| s.strip_prefix("key=")) { | ||||
|             Some(key_str) => Some(parse_key(key_str)?), | ||||
|             None => None, | ||||
|         }; | ||||
| 
 | ||||
|         Ok(Self { | ||||
|             lower_dir: PathBuf::from(lower_dir), | ||||
|             upper_dir: PathBuf::from(upper_dir), | ||||
|             key, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct SEFSMountOptions { | ||||
|     dir: PathBuf, | ||||
|     key: Option<sgx_key_128bit_t>, | ||||
|     mac: Option<sgx_aes_gcm_128bit_tag_t>, | ||||
| } | ||||
| 
 | ||||
| impl SEFSMountOptions { | ||||
|     pub fn from_input(input: &str) -> Result<Self> { | ||||
|         let options: Vec<&str> = input.split(",").collect(); | ||||
| 
 | ||||
|         let dir = options | ||||
|             .iter() | ||||
|             .find_map(|s| s.strip_prefix("dir=")) | ||||
|             .ok_or_else(|| errno!(EINVAL, "no dir options"))?; | ||||
|         let key = match options.iter().find_map(|s| s.strip_prefix("key=")) { | ||||
|             Some(key_str) => Some(parse_key(key_str)?), | ||||
|             None => None, | ||||
|         }; | ||||
|         let mac = match options.iter().find_map(|s| s.strip_prefix("mac=")) { | ||||
|             Some(mac_str) => Some(parse_mac(mac_str)?), | ||||
|             None => None, | ||||
|         }; | ||||
| 
 | ||||
|         Ok(Self { | ||||
|             dir: PathBuf::from(dir), | ||||
|             key, | ||||
|             mac, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bitflags! { | ||||
|     pub struct UmountFlags: u32 { | ||||
|         const MNT_FORCE = 1; | ||||
|         const MNT_DETACH = 2; | ||||
|         const MNT_EXPIRE = 4; | ||||
|         const UMOUNT_NOFOLLOW = 8; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl UmountFlags { | ||||
|     pub fn from_u32(raw: u32) -> Result<Self> { | ||||
|         let flags = Self::from_bits(raw).ok_or_else(|| errno!(EINVAL, "invalid flags"))?; | ||||
|         if flags.contains(Self::MNT_EXPIRE) | ||||
|             && (flags.contains(Self::MNT_FORCE) || flags.contains(Self::MNT_DETACH)) | ||||
|         { | ||||
|             return_errno!(EINVAL, "MNT_EXPIRE with either MNT_DETACH or MNT_FORCE"); | ||||
|         } | ||||
|         Ok(flags) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -161,9 +161,6 @@ impl FsView { | ||||
| 
 | ||||
|     /// Lookup INode from the cwd of the process, dereference symlink
 | ||||
|     pub fn lookup_inode(&self, path: &str) -> Result<Arc<dyn INode>> { | ||||
|         // Linux uses 40 as the upper limit for resolving symbolic links,
 | ||||
|         // so Occlum use it as a reasonable value
 | ||||
|         const MAX_SYMLINKS: usize = 40; | ||||
|         debug!("lookup_inode: cwd: {:?}, path: {:?}", self.cwd(), path); | ||||
|         if path.len() > 0 && path.as_bytes()[0] == b'/' { | ||||
|             // absolute path
 | ||||
|  | ||||
| @ -67,3 +67,7 @@ fn split_path(path: &str) -> (&str, &str) { | ||||
|     } | ||||
|     (dir_path, file_name) | ||||
| } | ||||
| 
 | ||||
| // Linux uses 40 as the upper limit for resolving symbolic links,
 | ||||
| // so Occlum use it as a reasonable value
 | ||||
| pub const MAX_SYMLINKS: usize = 40; | ||||
|  | ||||
| @ -22,7 +22,7 @@ lazy_static! { | ||||
|                 let rootfs = open_root_fs_according_to(mount_config, &None)?; | ||||
|                 rootfs.root_inode() | ||||
|             }; | ||||
|             mount_nonroot_fs_according_to(&root_inode, mount_config, &None)?; | ||||
|             mount_nonroot_fs_according_to(&root_inode, mount_config, &None, true)?; | ||||
|             Ok(root_inode) | ||||
|         } | ||||
| 
 | ||||
| @ -74,10 +74,28 @@ pub fn open_root_fs_according_to( | ||||
|     Ok(root_mountable_unionfs) | ||||
| } | ||||
| 
 | ||||
| pub fn umount_nonroot_fs( | ||||
|     root: &Arc<dyn INode>, | ||||
|     abs_path: &str, | ||||
|     follow_symlink: bool, | ||||
| ) -> Result<()> { | ||||
|     let mount_dir = if follow_symlink { | ||||
|         root.lookup_follow(abs_path, MAX_SYMLINKS)? | ||||
|     } else { | ||||
|         let (dir_path, file_name) = split_path(abs_path); | ||||
|         root.lookup_follow(dir_path, MAX_SYMLINKS)? | ||||
|             .lookup(file_name)? | ||||
|     }; | ||||
| 
 | ||||
|     mount_dir.downcast_ref::<MNode>().unwrap().umount()?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| pub fn mount_nonroot_fs_according_to( | ||||
|     root: &Arc<dyn INode>, | ||||
|     mount_configs: &Vec<ConfigMount>, | ||||
|     user_key: &Option<sgx_key_128bit_t>, | ||||
|     follow_symlink: bool, | ||||
| ) -> Result<()> { | ||||
|     for mc in mount_configs { | ||||
|         if mc.target == Path::new("/") { | ||||
| @ -92,7 +110,7 @@ pub fn mount_nonroot_fs_according_to( | ||||
|         match mc.type_ { | ||||
|             TYPE_SEFS => { | ||||
|                 let sefs = open_or_create_sefs_according_to(&mc, user_key)?; | ||||
|                 mount_fs_at(sefs, root, &mc.target)?; | ||||
|                 mount_fs_at(sefs, root, &mc.target, follow_symlink)?; | ||||
|             } | ||||
|             TYPE_HOSTFS => { | ||||
|                 if mc.source.is_none() { | ||||
| @ -101,22 +119,44 @@ pub fn mount_nonroot_fs_according_to( | ||||
|                 let source_path = mc.source.as_ref().unwrap(); | ||||
| 
 | ||||
|                 let hostfs = HostFS::new(source_path); | ||||
|                 mount_fs_at(hostfs, root, &mc.target)?; | ||||
|                 mount_fs_at(hostfs, root, &mc.target, follow_symlink)?; | ||||
|             } | ||||
|             TYPE_RAMFS => { | ||||
|                 let ramfs = RamFS::new(); | ||||
|                 mount_fs_at(ramfs, root, &mc.target)?; | ||||
|                 mount_fs_at(ramfs, root, &mc.target, follow_symlink)?; | ||||
|             } | ||||
|             TYPE_DEVFS => { | ||||
|                 let devfs = dev_fs::init_devfs()?; | ||||
|                 mount_fs_at(devfs, root, &mc.target)?; | ||||
|                 mount_fs_at(devfs, root, &mc.target, follow_symlink)?; | ||||
|             } | ||||
|             TYPE_PROCFS => { | ||||
|                 let procfs = ProcFS::new(); | ||||
|                 mount_fs_at(procfs, root, &mc.target)?; | ||||
|                 mount_fs_at(procfs, root, &mc.target, follow_symlink)?; | ||||
|             } | ||||
|             TYPE_UNIONFS => { | ||||
|                 return_errno!(EINVAL, "Cannot mount UnionFS at non-root path"); | ||||
|                 let layer_mcs = mc | ||||
|                     .options | ||||
|                     .layers | ||||
|                     .as_ref() | ||||
|                     .ok_or_else(|| errno!(EINVAL, "Invalid layers for unionfs"))?; | ||||
|                 let image_fs_mc = layer_mcs | ||||
|                     .get(0) | ||||
|                     .ok_or_else(|| errno!(EINVAL, "Invalid image layer"))?; | ||||
|                 let container_fs_mc = layer_mcs | ||||
|                     .get(1) | ||||
|                     .ok_or_else(|| errno!(EINVAL, "Invalid container layer"))?; | ||||
|                 let unionfs = match (&image_fs_mc.type_, &container_fs_mc.type_) { | ||||
|                     (TYPE_SEFS, TYPE_SEFS) => { | ||||
|                         let image_sefs = open_or_create_sefs_according_to(image_fs_mc, user_key)?; | ||||
|                         let container_sefs = | ||||
|                             open_or_create_sefs_according_to(container_fs_mc, user_key)?; | ||||
|                         UnionFS::new(vec![container_sefs, image_sefs])? | ||||
|                     } | ||||
|                     (_, _) => { | ||||
|                         return_errno!(EINVAL, "Unsupported fs type inside unionfs"); | ||||
|                     } | ||||
|                 }; | ||||
|                 mount_fs_at(unionfs, root, &mc.target, follow_symlink)?; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -126,22 +166,21 @@ pub fn mount_nonroot_fs_according_to( | ||||
| pub fn mount_fs_at( | ||||
|     fs: Arc<dyn FileSystem>, | ||||
|     parent_inode: &Arc<dyn INode>, | ||||
|     abs_path: &Path, | ||||
|     path: &Path, | ||||
|     follow_symlink: bool, | ||||
| ) -> Result<()> { | ||||
|     let mut mount_dir = parent_inode.find(".")?; | ||||
|     // The first component of abs_path is the RootDir, skip it.
 | ||||
|     for dirname in abs_path.iter().skip(1) { | ||||
|         mount_dir = match mount_dir.find(dirname.to_str().unwrap()) { | ||||
|             Ok(existing_dir) => { | ||||
|                 if existing_dir.metadata()?.type_ != FileType::Dir { | ||||
|                     return_errno!(EIO, "not a directory"); | ||||
|                 } | ||||
|                 existing_dir | ||||
|             } | ||||
|             Err(_) => return_errno!(ENOENT, "Mount point does not exist"), | ||||
|         }; | ||||
|     } | ||||
|     mount_dir.downcast_ref::<MNode>().unwrap().mount(fs); | ||||
|     let path = path | ||||
|         .to_str() | ||||
|         .ok_or_else(|| errno!(EINVAL, "invalid path"))?; | ||||
|     let mount_dir = if follow_symlink { | ||||
|         parent_inode.lookup_follow(path, MAX_SYMLINKS)? | ||||
|     } else { | ||||
|         let (dir_path, file_name) = split_path(path); | ||||
|         parent_inode | ||||
|             .lookup_follow(dir_path, MAX_SYMLINKS)? | ||||
|             .lookup(file_name)? | ||||
|     }; | ||||
|     mount_dir.downcast_ref::<MNode>().unwrap().mount(fs)?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -5,9 +5,11 @@ use super::file_ops::{ | ||||
|     FsPath, LinkFlags, StatFlags, UnlinkFlags, AT_FDCWD, | ||||
| }; | ||||
| use super::fs_ops; | ||||
| use super::fs_ops::{MountFlags, MountOptions, UmountFlags}; | ||||
| use super::time::{clockid_t, itimerspec_t, ClockID}; | ||||
| use super::timer_file::{TimerCreationFlags, TimerSetFlags}; | ||||
| use super::*; | ||||
| use config::ConfigMountFsType; | ||||
| use util::mem_util::from_user; | ||||
| 
 | ||||
| #[allow(non_camel_case_types)] | ||||
| @ -653,3 +655,41 @@ pub fn do_statfs(path: *const i8, statfs_buf: *mut Statfs) -> Result<isize> { | ||||
|     } | ||||
|     Ok(0) | ||||
| } | ||||
| 
 | ||||
| pub fn do_mount( | ||||
|     source: *const i8, | ||||
|     target: *const i8, | ||||
|     fs_type: *const i8, | ||||
|     flags: u32, | ||||
|     options: *const i8, | ||||
| ) -> Result<isize> { | ||||
|     let source = from_user::clone_cstring_safely(source)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     let target = from_user::clone_cstring_safely(target)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     let flags = MountFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?; | ||||
|     let mount_options = { | ||||
|         let fs_type = { | ||||
|             let fs_type = from_user::clone_cstring_safely(fs_type)? | ||||
|                 .to_string_lossy() | ||||
|                 .into_owned(); | ||||
|             ConfigMountFsType::from_input(fs_type.as_str())? | ||||
|         }; | ||||
|         MountOptions::from_fs_type_and_options(&fs_type, options)? | ||||
|     }; | ||||
| 
 | ||||
|     fs_ops::do_mount(&source, &target, flags, mount_options)?; | ||||
|     Ok(0) | ||||
| } | ||||
| 
 | ||||
| pub fn do_umount(target: *const i8, flags: u32) -> Result<isize> { | ||||
|     let target = from_user::clone_cstring_safely(target)? | ||||
|         .to_string_lossy() | ||||
|         .into_owned(); | ||||
|     let flags = UmountFlags::from_u32(flags)?; | ||||
| 
 | ||||
|     fs_ops::do_umount(&target, flags)?; | ||||
|     Ok(0) | ||||
| } | ||||
|  | ||||
| @ -26,12 +26,12 @@ use crate::fs::{ | ||||
|     do_eventfd, do_eventfd2, do_faccessat, do_fallocate, do_fchdir, do_fchmod, do_fchmodat, | ||||
|     do_fchown, do_fchownat, do_fcntl, do_fdatasync, do_fstat, do_fstatat, do_fstatfs, do_fsync, | ||||
|     do_ftruncate, do_getcwd, do_getdents, do_getdents64, do_ioctl, do_lchown, do_link, do_linkat, | ||||
|     do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount_rootfs, do_open, do_openat, do_pipe, | ||||
|     do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, do_readv, do_rename, | ||||
|     do_renameat, do_rmdir, do_sendfile, do_stat, do_statfs, do_symlink, do_symlinkat, do_sync, | ||||
|     do_timerfd_create, do_timerfd_gettime, do_timerfd_settime, do_truncate, do_umask, do_unlink, | ||||
|     do_unlinkat, do_write, do_writev, iovec_t, AsTimer, File, FileDesc, FileRef, HostStdioFds, | ||||
|     Stat, Statfs, | ||||
|     do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount, do_mount_rootfs, do_open, do_openat, | ||||
|     do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, do_readv, | ||||
|     do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_statfs, do_symlink, do_symlinkat, | ||||
|     do_sync, do_timerfd_create, do_timerfd_gettime, do_timerfd_settime, do_truncate, do_umask, | ||||
|     do_umount, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, AsTimer, File, FileDesc, | ||||
|     FileRef, HostStdioFds, Stat, Statfs, | ||||
| }; | ||||
| use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; | ||||
| use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t, RandFlags}; | ||||
| @ -253,8 +253,8 @@ macro_rules! process_syscall_table_with_callback { | ||||
|             (Sync = 162) => do_sync(), | ||||
|             (Acct = 163) => handle_unsupported(), | ||||
|             (Settimeofday = 164) => handle_unsupported(), | ||||
|             (Mount = 165) => handle_unsupported(), | ||||
|             (Umount2 = 166) => handle_unsupported(), | ||||
|             (Mount = 165) => do_mount(source: *const i8, target: *const i8, fs_type: *const i8, flags: u32, options: *const i8), | ||||
|             (Umount2 = 166) => do_umount(target: *const i8, flags: u32), | ||||
|             (Swapon = 167) => handle_unsupported(), | ||||
|             (Swapoff = 168) => handle_unsupported(), | ||||
|             (Reboot = 169) => handle_unsupported(), | ||||
|  | ||||
							
								
								
									
										12
									
								
								test/mount/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										12
									
								
								test/mount/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| DEPS_FILE := mnt_test | ||||
| include ../test_common.mk | ||||
| 
 | ||||
| EXTRA_C_FLAGS := | ||||
| EXTRA_LINK_FLAGS := | ||||
| BIN_ARGS := | ||||
| 
 | ||||
| mnt_test: | ||||
| 	@mkdir -p $(BUILD_DIR)/test/$@/mnt_sefs | ||||
| 	@mkdir -p $(BUILD_DIR)/test/$@/mnt_unionfs/upper | ||||
| 	@mkdir -p $(BUILD_DIR)/test/$@/mnt_unionfs/lower | ||||
| 	@mkdir -p $(BUILD_DIR)/test/$@/mnt_hostfs | ||||
							
								
								
									
										234
									
								
								test/mount/main.c
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										234
									
								
								test/mount/main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,234 @@ | ||||
| #include <sys/stat.h> | ||||
| #include <sys/mount.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include "test_fs.h" | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Helper function
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static int remove_file(const char *file_path) { | ||||
|     int ret; | ||||
|     ret = unlink(file_path); | ||||
|     if (ret < 0) { | ||||
|         THROW_ERROR("failed to unlink the created file"); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int write_read_file(const char *file_path) { | ||||
|     char *write_str = "Hello World\n"; | ||||
|     int fd; | ||||
| 
 | ||||
|     fd = open(file_path, O_RDWR | O_CREAT | O_TRUNC, 00666); | ||||
|     if (fd < 0) { | ||||
|         THROW_ERROR("failed to open a file to write"); | ||||
|     } | ||||
|     if (write(fd, write_str, strlen(write_str)) <= 0) { | ||||
|         THROW_ERROR("failed to write"); | ||||
|     } | ||||
|     close(fd); | ||||
| 
 | ||||
|     if (fs_check_file_content(file_path, write_str) < 0) { | ||||
|         THROW_ERROR("failed to check file content"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int create_dir(const char *dir) { | ||||
|     struct stat stat_buf; | ||||
|     mode_t mode = 00775; | ||||
| 
 | ||||
|     if (stat(dir, &stat_buf) == 0) { | ||||
|         if (!S_ISDIR(stat_buf.st_mode)) { | ||||
|             if (remove_file(dir) < 0) { | ||||
|                 THROW_ERROR("failed to remove: %s", dir); | ||||
|             } | ||||
|             if (mkdir(dir, mode) < 0) { | ||||
|                 THROW_ERROR("failed to mkdir: %s", dir); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         if (mkdir(dir, mode) < 0) { | ||||
|             THROW_ERROR("failed to mkdir: %s", dir); | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int check_file_no_exists(const char *file_path) { | ||||
|     struct stat stat_buf; | ||||
| 
 | ||||
|     int ret = stat(file_path, &stat_buf); | ||||
|     if (!(ret < 0 && errno == ENOENT)) { | ||||
|         THROW_ERROR("stat on \"%s\" should return ENOENT", file_path); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test cases for mount
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static int __test_mount_sefs(const char *mnt_dir) { | ||||
|     if (create_dir(mnt_dir) < 0) { | ||||
|         THROW_ERROR("failed to create sefs mnt dir"); | ||||
|     } | ||||
| 
 | ||||
|     if (mount("sefs", mnt_dir, "sefs", 0, "dir=./mnt_test/mnt_sefs") < 0) { | ||||
|         THROW_ERROR("failed to mount sefs"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int __test_mount_unionfs(const char *mnt_dir) { | ||||
|     if (create_dir(mnt_dir) < 0) { | ||||
|         THROW_ERROR("failed to create unionfs mnt dir"); | ||||
|     } | ||||
| 
 | ||||
|     if (mount("unionfs", mnt_dir, "unionfs", 0, | ||||
|               "lowerdir=./mnt_test/mnt_unionfs/lower,upperdir=./mnt_test/mnt_unionfs/upper") < 0) { | ||||
|         THROW_ERROR("failed to mount unionfs"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int __test_mount_hostfs(const char *mnt_dir) { | ||||
|     if (create_dir(mnt_dir) < 0) { | ||||
|         THROW_ERROR("failed to create hostfs mnt dir"); | ||||
|     } | ||||
| 
 | ||||
|     if (mount("hostfs", mnt_dir, "hostfs", 0, "dir=./mnt_test/mnt_hostfs") < 0) { | ||||
|         THROW_ERROR("failed to mount hostfs"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int __test_mount_ramfs(const char *mnt_dir) { | ||||
|     if (create_dir(mnt_dir) < 0) { | ||||
|         THROW_ERROR("failed to create ramfs mnt dir"); | ||||
|     } | ||||
| 
 | ||||
|     if (mount("ramfs", mnt_dir, "ramfs", 0, NULL) < 0) { | ||||
|         THROW_ERROR("failed to mount ramfs"); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| typedef int(*test_mount_func_t)(const char *); | ||||
| 
 | ||||
| static int test_mount_framework(test_mount_func_t fn, const char *dir, bool mount) { | ||||
|     if (fn(dir) < 0) { | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     char file_path[PATH_MAX] = { 0 }; | ||||
|     snprintf(file_path, sizeof(file_path), "%s/test_write_read.txt", dir); | ||||
| 
 | ||||
|     if (mount) { | ||||
|         if (write_read_file(file_path) < 0) { | ||||
|             THROW_ERROR("failed to RW files on mounted fs"); | ||||
|         } | ||||
|     } else { | ||||
|         if (check_file_no_exists(file_path) < 0) { | ||||
|             THROW_ERROR("failed to check file exists after umount"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int test_mount_sefs() { | ||||
|     const char *mnt_dir = "/mnt_sefs"; | ||||
|     return test_mount_framework(__test_mount_sefs, mnt_dir, true); | ||||
| } | ||||
| 
 | ||||
| static int test_mount_unionfs() { | ||||
|     const char *mnt_dir = "/mnt_unionfs"; | ||||
|     return test_mount_framework(__test_mount_unionfs, mnt_dir, true); | ||||
| } | ||||
| 
 | ||||
| static int test_mount_hostfs() { | ||||
|     const char *mnt_dir = "/mnt_hostfs"; | ||||
|     return test_mount_framework(__test_mount_hostfs, mnt_dir, true); | ||||
| } | ||||
| 
 | ||||
| static int test_mount_ramfs() { | ||||
|     const char *mnt_dir = "/mnt_ramfs"; | ||||
|     return test_mount_framework(__test_mount_ramfs, mnt_dir, true); | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test cases for umount
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static int __test_umount_fs(const char *target) { | ||||
|     int flags = MNT_EXPIRE | MNT_DETACH; | ||||
|     int ret = umount2(target, flags); | ||||
|     if (!(ret < 0 && errno == EINVAL)) { | ||||
|         THROW_ERROR("failed to check invalid flags"); | ||||
|     } | ||||
| 
 | ||||
|     char subdir[PATH_MAX] = { 0 }; | ||||
|     snprintf(subdir, sizeof(subdir), "%s/subdir", target); | ||||
|     if (create_dir(subdir) < 0) { | ||||
|         THROW_ERROR("failed to create dir: %s", subdir); | ||||
|     } | ||||
|     ret = umount(subdir); | ||||
|     if (!(ret < 0 && errno == EINVAL)) { | ||||
|         THROW_ERROR("failed to check umount non-mountpoint"); | ||||
|     } | ||||
| 
 | ||||
|     if (umount(target) < 0) { | ||||
|         THROW_ERROR("failed to umount fs on: %s", target); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static int test_umount_sefs() { | ||||
|     const char *target = "/mnt_sefs"; | ||||
|     return test_mount_framework(__test_umount_fs, target, false); | ||||
| } | ||||
| 
 | ||||
| static int test_umount_unionfs() { | ||||
|     const char *target = "/mnt_unionfs"; | ||||
|     return test_mount_framework(__test_umount_fs, target, false); | ||||
| } | ||||
| 
 | ||||
| static int test_umount_hostfs() { | ||||
|     const char *target = "/mnt_hostfs"; | ||||
|     return test_mount_framework(__test_umount_fs, target, false); | ||||
| } | ||||
| 
 | ||||
| static int test_umount_ramfs() { | ||||
|     const char *target = "/mnt_ramfs"; | ||||
|     return test_mount_framework(__test_umount_fs, target, false); | ||||
| } | ||||
| 
 | ||||
| // ============================================================================
 | ||||
| // Test suite main
 | ||||
| // ============================================================================
 | ||||
| 
 | ||||
| static test_case_t test_cases[] = { | ||||
|     // TODO: enable it if SEFS is thread-safe
 | ||||
|     //TEST_CASE(test_mount_sefs),
 | ||||
|     //TEST_CASE(test_umount_sefs),
 | ||||
|     TEST_CASE(test_mount_unionfs), | ||||
|     TEST_CASE(test_umount_unionfs), | ||||
|     TEST_CASE(test_mount_hostfs), | ||||
|     TEST_CASE(test_umount_hostfs), | ||||
|     TEST_CASE(test_mount_ramfs), | ||||
|     TEST_CASE(test_umount_ramfs), | ||||
| }; | ||||
| 
 | ||||
| int main(int argc, const char *argv[]) { | ||||
|     return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user