high_level.rs 5.4 KB

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