high_level.rs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. //! High-level operations of Ext4 filesystem.
  2. //!
  3. //! This module provides path-based operations. An object can be
  4. //! located in the filesystem by its relative or absolute path.
  5. //!
  6. //! Some operations such as `read`, `write`, `setattr` do not involve
  7. //! file location. They are implemented in the `low_level` module.
  8. //! High-level and low-level operations can be used together to
  9. //! implement more complex operations.
  10. use super::Ext4;
  11. use crate::ext4_defs::*;
  12. use crate::prelude::*;
  13. use crate::return_error;
  14. impl Ext4 {
  15. /// Look up an object in the filesystem recursively.
  16. ///
  17. /// # Params
  18. ///
  19. /// * `root` - The inode id of the root directory for search.
  20. /// * `path` - The relative path of the object to be opened.
  21. ///
  22. /// # Return
  23. ///
  24. /// `Ok(inode)` - Inode id of the object
  25. ///
  26. /// # Error
  27. ///
  28. /// * `ENOTDIR` - Any parent along `path` is not a directory.
  29. /// * `ENOENT` - The object does not exist.
  30. pub fn generic_lookup(&self, root: InodeId, path: &str) -> Result<InodeId> {
  31. trace!("generic_lookup({}, {})", root, path);
  32. // Search from the given parent inode
  33. let mut cur = root;
  34. let search_path = Self::split_path(path);
  35. // Search recursively
  36. for path in search_path.iter() {
  37. cur = self.lookup(cur, path)?;
  38. }
  39. Ok(cur)
  40. }
  41. /// (DEPRECATED) Open a file in the filesystem.
  42. ///
  43. /// # Params
  44. ///
  45. /// * `root` - The inode id of the root directory for search.
  46. /// * `path` - The path of the object to be opened.
  47. /// * `flags` - The open flags. Creation (O_CREAT, O_EXCL, O_NOCTTY) flags
  48. /// will be ignored.
  49. ///
  50. /// # Return
  51. ///
  52. /// `Ok(fh)` - File handler
  53. ///
  54. /// # Error
  55. ///
  56. /// * `ENOENT` - The file does not exist.
  57. /// * `EISDIR` - The file is a directory.
  58. #[deprecated]
  59. pub fn generic_open(&self, root: InodeId, path: &str, flags: OpenFlags) -> Result<FileHandler> {
  60. let inode_id = self.generic_lookup(root, path)?;
  61. let inode = self.read_inode(inode_id);
  62. // Check file type
  63. if !inode.inode.is_file() {
  64. return_error!(ErrCode::EISDIR, "File {} is not a regular file", path);
  65. }
  66. Ok(FileHandler::new(inode.id, flags, inode.inode.size()))
  67. }
  68. /// Create an object in the filesystem.
  69. ///
  70. /// This function will perform recursive-creation i.e. if the parent
  71. /// directory does not exist, it will be created as well.
  72. ///
  73. /// # Params
  74. ///
  75. /// * `root` - The inode id of the starting directory for search.
  76. /// * `path` - The relative path of the object to create.
  77. /// * `mode` - file mode and type to create
  78. ///
  79. /// # Return
  80. ///
  81. /// `Ok(inode)` - Inode id of the created object
  82. ///
  83. /// # Error
  84. ///
  85. /// * `ENOTDIR` - Any parent along `path` is not a directory.
  86. /// * `EEXIST` - The object already exists.
  87. pub fn generic_create(&self, root: InodeId, path: &str, mode: InodeMode) -> Result<InodeId> {
  88. // Search from the given parent inode
  89. let mut cur = self.read_inode(root);
  90. let search_path = Self::split_path(path);
  91. // Search recursively
  92. for (i, path) in search_path.iter().enumerate() {
  93. if !cur.inode.is_dir() {
  94. return_error!(ErrCode::ENOTDIR, "Parent {} is not a directory", cur.id);
  95. }
  96. match self.dir_find_entry(&cur, &path) {
  97. Ok(id) => {
  98. if i == search_path.len() - 1 {
  99. // Reach the object and it already exists
  100. return_error!(ErrCode::EEXIST, "Object {}/{} already exists", root, path);
  101. }
  102. cur = self.read_inode(id);
  103. }
  104. Err(e) => {
  105. if e.code() != ErrCode::ENOENT {
  106. return_error!(e.code(), "Unexpected error: {:?}", e);
  107. }
  108. let mut child = if i == search_path.len() - 1 {
  109. // Reach the object, create it
  110. self.create_inode(mode)?
  111. } else {
  112. // Create parent directory
  113. self.create_inode(InodeMode::DIRECTORY | InodeMode::ALL_RWX)?
  114. };
  115. self.link_inode(&mut cur, &mut child, path)?;
  116. cur = child;
  117. }
  118. }
  119. }
  120. Ok(cur.id)
  121. }
  122. /// Remove an object from the filesystem.
  123. ///
  124. /// # Params
  125. ///
  126. /// * `root` - The inode id of the starting directory for search.
  127. /// * `path` - The relative path of the object to remove.
  128. ///
  129. /// # Error
  130. ///
  131. /// * `ENOENT` - The object does not exist.
  132. /// * `ENOTEMPTY` - The object is a non-empty directory.
  133. pub fn generic_remove(&self, root: InodeId, path: &str) -> Result<()> {
  134. // Get the parent directory path and the file name
  135. let mut search_path = Self::split_path(path);
  136. let file_name = &search_path.split_off(search_path.len() - 1)[0];
  137. let parent_path = search_path.join("/");
  138. // Get the parent directory inode
  139. let parent_id = self.generic_lookup(root, &parent_path)?;
  140. // Get the child inode
  141. let child_id = self.lookup(parent_id, &file_name)?;
  142. let mut parent = self.read_inode(parent_id);
  143. let mut child = self.read_inode(child_id);
  144. // Check if child is a non-empty directory
  145. if child.inode.is_dir() && self.dir_list_entries(&child).len() > 2 {
  146. return_error!(ErrCode::ENOTEMPTY, "Directory {} not empty", path);
  147. }
  148. // Unlink the file
  149. self.unlink_inode(&mut parent, &mut child, file_name, true)
  150. }
  151. /// Move an object from one location to another.
  152. ///
  153. /// # Params
  154. ///
  155. /// * `root` - The inode id of the starting directory for search.
  156. /// * `src` - The relative path of the object to move.
  157. /// * `dst` - The relative path of the destination.
  158. ///
  159. /// # Error
  160. ///
  161. /// * `ENOTDIR` - Any parent in the path is not a directory.
  162. /// * `ENOENT` - The source object does not exist.
  163. /// * `EEXIST` - The destination object already exists.
  164. pub fn generic_rename(&self, root: InodeId, src: &str, dst: &str) -> Result<()> {
  165. // Parse the directories and file names
  166. let mut src_path = Self::split_path(src);
  167. let src_file_name = &src_path.split_off(src_path.len() - 1)[0];
  168. let src_parent_path = src_path.join("/");
  169. let mut dst_path = Self::split_path(dst);
  170. let dst_file_name = &dst_path.split_off(dst_path.len() - 1)[0];
  171. let dst_parent_path = dst_path.join("/");
  172. // Get source and des inodes
  173. let src_parent_id = self.generic_lookup(root, &src_parent_path)?;
  174. let dst_parent_id = self.generic_lookup(root, &dst_parent_path)?;
  175. // Move the file
  176. self.rename(src_parent_id, &src_file_name, dst_parent_id, &dst_file_name)
  177. }
  178. /// A helper function to split a path by '/'
  179. fn split_path(path: &str) -> Vec<String> {
  180. let path = path.trim_start_matches("/");
  181. if path.is_empty() {
  182. return vec![]; // root
  183. }
  184. path.split("/").map(|s| s.to_string()).collect()
  185. }
  186. }