high_level.rs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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.
  16. ///
  17. /// ## Params
  18. ///
  19. /// * `root` - The inode id of the root directory for search.
  20. /// * `path` - The path of the object to be opened.
  21. ///
  22. /// ## Return
  23. ///
  24. /// `Ok(inode)` - Inode id of the object
  25. pub fn generic_lookup(&self, root: InodeId, path: &str) -> Result<InodeId> {
  26. // Search from the given parent inode
  27. let mut cur = root;
  28. let search_path = Self::split_path(path);
  29. // Search recursively
  30. for path in search_path.iter() {
  31. cur = self.lookup(cur, path)?;
  32. }
  33. Ok(cur)
  34. }
  35. /// Open a file in the filesystem. Return error if the file does not exist.
  36. ///
  37. /// ## Params
  38. ///
  39. /// * `root` - The inode id of the root directory for search.
  40. /// * `path` - The path of the object to be opened.
  41. /// * `flags` - The open flags. Creation (O_CREAT, O_EXCL, O_NOCTTY) flags
  42. /// will be ignored.
  43. ///
  44. /// ## Return
  45. ///
  46. /// `Ok(fh)` - File handler
  47. pub fn generic_open(&self, root: InodeId, path: &str, flags: OpenFlags) -> Result<FileHandler> {
  48. let inode_id = self.generic_lookup(root, path)?;
  49. let inode = self.read_inode(inode_id);
  50. // Check file type
  51. if !inode.inode.is_file() {
  52. return_error!(ErrCode::EISDIR, "File {} is not a regular file", path);
  53. }
  54. Ok(FileHandler::new(inode.id, flags, inode.inode.size()))
  55. }
  56. /// Create an object in the filesystem. Return error if the object already exists.
  57. ///
  58. /// This function will perform recursive-creation i.e. if the parent
  59. /// directory does not exist, it will be created as well.
  60. ///
  61. /// ## Params
  62. ///
  63. /// * `root` - The inode id of the starting directory for search.
  64. /// * `path` - The path of the object to create.
  65. /// * `mode` - file mode and type to create
  66. ///
  67. /// ## Return
  68. ///
  69. /// `Ok(inode)` - Inode id of the created object
  70. pub fn generic_create(&self, root: InodeId, path: &str, mode: InodeMode) -> Result<InodeId> {
  71. // Search from the given parent inode
  72. let mut cur = self.read_inode(root);
  73. let search_path = Self::split_path(path);
  74. // Search recursively
  75. for (i, path) in search_path.iter().enumerate() {
  76. if !cur.inode.is_dir() {
  77. return_error!(ErrCode::ENOTDIR, "Parent directory is not a directory");
  78. }
  79. match self.dir_find_entry(&cur, &path) {
  80. Ok(de) => {
  81. // If the object exists, check the type
  82. cur = self.read_inode(de.inode());
  83. }
  84. Err(e) => {
  85. if e.code() != ErrCode::ENOENT {
  86. return Err(e);
  87. }
  88. // If the object does not exist, create it
  89. let mut child = if i == search_path.len() - 1 {
  90. // Create the file
  91. self.create_inode(mode)?
  92. } else {
  93. // Create the directory
  94. self.create_inode(InodeMode::DIRECTORY | InodeMode::ALL_RWX)?
  95. };
  96. self.link_inode(&mut cur, &mut child, path)?;
  97. cur = child;
  98. }
  99. }
  100. }
  101. Ok(cur.id)
  102. }
  103. /// Remove an object from the filesystem. Return error if the object is a
  104. /// directory and is not empty.
  105. ///
  106. /// ## Params
  107. ///
  108. /// * `root` - The inode id of the starting directory for search.
  109. /// * `path` - The path of the object to remove.
  110. pub fn generic_remove(&self, root: InodeId, path: &str) -> Result<()> {
  111. // Get the parent directory path and the file name
  112. let mut search_path = Self::split_path(path);
  113. let file_name = &search_path.split_off(search_path.len() - 1)[0];
  114. let parent_path = search_path.join("/");
  115. // Get the parent directory inode
  116. let parent_id = self.generic_lookup(root, &parent_path)?;
  117. // Get the child inode
  118. let child_id = self.generic_lookup(parent_id, &file_name)?;
  119. let mut parent = self.read_inode(parent_id);
  120. let mut child = self.read_inode(child_id);
  121. if child.inode.is_dir() {
  122. // Check if the directory is empty
  123. if self.dir_get_all_entries(&child).len() > 2 {
  124. return_error!(ErrCode::ENOTEMPTY, "Directory {} not empty", path);
  125. }
  126. }
  127. // Unlink the file
  128. self.unlink_inode(&mut parent, &mut child, file_name)
  129. }
  130. /// A helper function to split a path by '/'
  131. fn split_path(path: &str) -> Vec<String> {
  132. let _ = path.trim_start_matches("/");
  133. if path.is_empty() {
  134. return vec![]; // root
  135. }
  136. path.split("/").map(|s| s.to_string()).collect()
  137. }
  138. }