123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- //! Low-level operations of Ext4 filesystem.
- //!
- //! These interfaces are designed and arranged coresponding to FUSE low-level ops.
- //! Ref: https://libfuse.github.io/doxygen/structfuse__lowlevel__ops.html
- use super::Ext4;
- use crate::constants::*;
- use crate::ext4_defs::*;
- use crate::prelude::*;
- use crate::return_error;
- use core::cmp::min;
- impl Ext4 {
- /// Read an indoe
- ///
- /// # Params
- ///
- /// * `id` - inode id
- ///
- /// # Return
- ///
- /// An inode reference, combing id and the inode itself
- pub fn inode(&self, id: InodeId) -> InodeRef {
- self.read_inode(id)
- }
- /// Create and open a file. This function will not check the existence
- /// of the file. Call `lookup` to check beforehand.
- ///
- /// # Params
- ///
- /// * `parent` - parent directory inode id
- /// * `name` - file name
- /// * `mode` - file type and mode with which to create the new file
- /// * `flags` - open flags
- ///
- /// # Return
- ///
- /// `Ok(inode)` - Inode id of the new file
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` is not a directory
- /// * `ENOSPC` - No space left on device
- pub fn create(&mut self, parent: InodeId, name: &str, mode: InodeMode) -> Result<InodeId> {
- let mut parent = self.read_inode(parent);
- // Can only create a file in a directory
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- // Create child inode and link it to parent directory
- let mut child = self.create_inode(mode)?;
- self.link_inode(&mut parent, &mut child, name)?;
- // Create file handler
- Ok(child.id)
- }
- /// Read data from a file. This function will read exactly `buf.len()`
- /// bytes unless the end of the file is reached.
- ///
- /// # Params
- ///
- /// * `file` - the file handler, acquired by `open` or `create`
- /// * `offset` - offset to read from
- /// * `buf` - the buffer to store the data
- ///
- /// # Return
- ///
- /// `Ok(usize)` - the actual number of bytes read
- ///
- /// # Error
- ///
- /// * `EISDIR` - `file` is not a regular file
- ///
- /// TODO: handle EOF
- pub fn read(&mut self, file: InodeId, offset: usize, buf: &mut [u8]) -> Result<usize> {
- // Get the inode of the file
- let mut inode_ref = self.read_inode(file);
- if !inode_ref.inode.is_file() {
- return_error!(ErrCode::EISDIR, "Inode {} is not a file", file);
- }
- let read_size = buf.len();
- // Read no bytes
- if read_size == 0 {
- return Ok(0);
- }
- // Get file size
- let fsize = inode_ref.inode.size();
- // Calc the actual size to read
- let size_to_read = min(read_size, fsize as usize - offset);
- // Calc the start block of reading
- let start_iblock = (offset / BLOCK_SIZE) as LBlockId;
- // Calc the length that is not aligned to the block size
- let misaligned = offset % BLOCK_SIZE;
- let mut cursor = 0;
- let mut iblock = start_iblock;
- // Read first block
- if misaligned > 0 {
- let read_len = min(BLOCK_SIZE - misaligned, size_to_read);
- let fblock = self
- .extent_get_pblock(&mut inode_ref, start_iblock)
- .unwrap();
- let block = self.read_block(fblock);
- // Copy data from block to the user buffer
- buf[cursor..cursor + read_len].copy_from_slice(block.read_offset(misaligned, read_len));
- cursor += read_len;
- iblock += 1;
- }
- // Continue with full block reads
- while cursor < size_to_read {
- let read_len = min(BLOCK_SIZE, size_to_read - cursor);
- let fblock = self.extent_get_pblock(&mut inode_ref, iblock).unwrap();
- let block = self.read_block(fblock);
- // Copy data from block to the user buffer
- buf[cursor..cursor + read_len].copy_from_slice(block.read_offset(0, read_len));
- cursor += read_len;
- iblock += 1;
- }
- Ok(cursor)
- }
- /// Write data to a file. This function will write exactly `data.len()` bytes.
- ///
- /// # Params
- ///
- /// * `file` - the file handler, acquired by `open` or `create`
- /// * `offset` - offset to write to
- /// * `data` - the data to write
- ///
- /// # Return
- ///
- /// `Ok(usize)` - the actual number of bytes written
- ///
- /// # Error
- ///
- /// * `EISDIR` - `file` is not a regular file
- /// `ENOSPC` - no space left on device
- ///
- /// TODO: handle EOF
- pub fn write(&mut self, file: InodeId, offset: usize, data: &[u8]) -> Result<usize> {
- // Get the inode of the file
- let mut inode_ref = self.read_inode(file);
- if !inode_ref.inode.is_file() {
- return_error!(ErrCode::EISDIR, "Inode {} is not a file", file);
- }
- let write_size = data.len();
- // Calc the start and end block of reading
- let start_iblock = (offset / BLOCK_SIZE) as LBlockId;
- let end_iblock = ((offset + write_size) / BLOCK_SIZE) as LBlockId;
- // Calc the block count of the file
- let block_count = (offset as usize + BLOCK_SIZE - 1) / BLOCK_SIZE;
- // Append enough block for writing
- let append_block_count = end_iblock + 1 - block_count as LBlockId;
- for _ in 0..append_block_count {
- self.inode_append_block(&mut inode_ref)?;
- }
- // Write data
- let mut cursor = 0;
- let mut iblock = start_iblock;
- while cursor < write_size {
- let write_len = min(BLOCK_SIZE, write_size - cursor);
- let fblock = self.extent_get_pblock(&mut inode_ref, iblock)?;
- let mut block = self.read_block(fblock);
- block.write_offset(
- (offset + cursor) % BLOCK_SIZE,
- &data[cursor..cursor + write_len],
- );
- self.write_block(&block);
- cursor += write_len;
- iblock += 1;
- }
- Ok(cursor)
- }
- /// Create a hard link. This function will not check name conflict.
- /// Call `lookup` to check beforehand.
- ///
- /// # Params
- ///
- /// * `child` - the inode of the file to link
- /// * `parent` - the inode of the directory to link to
- ///
- /// # Return
- ///
- /// `Ok(child)` - An inode reference to the child inode.
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` is not a directory
- /// * `ENOSPC` - no space left on device
- pub fn link(&mut self, child: InodeId, parent: InodeId, name: &str) -> Result<InodeRef> {
- let mut parent = self.read_inode(parent);
- // Can only link to a directory
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- let mut child = self.read_inode(child);
- self.link_inode(&mut parent, &mut child, name)?;
- Ok(child)
- }
- /// Unlink a file.
- ///
- /// # Params
- ///
- /// * `parent` - the inode of the directory to unlink from
- /// * `name` - the name of the file to unlink
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` is not a directory
- /// * `ENOENT` - `name` does not exist in `parent`
- pub fn unlink(&mut self, parent: InodeId, name: &str) -> Result<()> {
- let mut parent = self.read_inode(parent);
- // Can only unlink from a directory
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- let child_id = self.dir_find_entry(&parent, name)?.inode();
- let mut child = self.read_inode(child_id);
- self.unlink_inode(&mut parent, &mut child, name)
- }
- /// Move a file. This function will not check name conflict.
- /// Call `lookup` to check beforehand.
- ///
- /// # Params
- ///
- /// * `parent` - the inode of the directory to move from
- /// * `name` - the name of the file to move
- /// * `new_parent` - the inode of the directory to move to
- /// * `new_name` - the new name of the file
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` or `new_parent` is not a directory
- /// * `ENOENT` - `name` does not exist in `parent`
- /// * `ENOSPC` - no space left on device
- pub fn rename(
- &mut self,
- parent: InodeId,
- name: &str,
- new_parent: InodeId,
- new_name: &str,
- ) -> Result<()> {
- let mut parent = self.read_inode(parent);
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- let mut new_parent = self.read_inode(new_parent);
- if !new_parent.inode.is_dir() {
- return_error!(
- ErrCode::ENOTDIR,
- "Inode {} is not a directory",
- new_parent.id
- );
- }
- let child_id = self.dir_find_entry(&parent, name)?;
- let mut child = self.read_inode(child_id.inode());
- self.link_inode(&mut new_parent, &mut child, new_name)?;
- self.unlink_inode(&mut parent, &mut child, name)
- }
- /// Create a directory. This function will not check name conflict.
- /// Call `lookup` to check beforehand.
- ///
- /// # Params
- ///
- /// * `parent` - the inode of the directory to create in
- /// * `name` - the name of the directory to create
- /// * `mode` - the mode of the directory to create, type field will be ignored
- ///
- /// # Return
- ///
- /// `Ok(child)` - An inode reference to the new directory.
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` is not a directory
- /// * `ENOSPC` - no space left on device
- pub fn mkdir(&mut self, parent: InodeId, name: &str, mode: InodeMode) -> Result<InodeRef> {
- let mut parent = self.read_inode(parent);
- // Can only create a directory in a directory
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- // Create file/directory
- let mode = mode & InodeMode::PERM_MASK | InodeMode::DIRECTORY;
- let mut child = self.create_inode(mode)?;
- // Link the new inode
- self.link_inode(&mut parent, &mut child, name)?;
- Ok(child)
- }
- /// Look up a directory entry by name.
- ///
- /// # Params
- ///
- /// * `parent` - the inode of the directory to look in
- /// * `name` - the name of the entry to look for
- ///
- /// # Return
- ///
- /// `Ok(child)`: The inode id to which the directory entry points.
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` is not a directory
- /// * `ENOENT` - `name` does not exist in `parent`
- pub fn lookup(&mut self, parent: InodeId, name: &str) -> Result<InodeId> {
- let parent = self.read_inode(parent);
- // Can only lookup in a directory
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- self.dir_find_entry(&parent, name)
- .map(|entry| entry.inode())
- }
- /// List all directory entries in a directory.
- ///
- /// # Params
- ///
- /// * `inode` - the inode of the directory to list
- ///
- /// # Return
- ///
- /// `Ok(entries)` - A vector of directory entries in the directory.
- ///
- /// # Error
- ///
- /// `ENOTDIR` - `inode` is not a directory
- pub fn list(&self, inode: InodeId) -> Result<Vec<DirEntry>> {
- let inode_ref = self.read_inode(inode);
- // Can only list a directory
- if inode_ref.inode.file_type() != FileType::Directory {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", inode);
- }
- Ok(self.dir_get_all_entries(&inode_ref))
- }
- /// Remove an empty directory.
- ///
- /// # Params
- ///
- /// * `parent` - the parent directory where the directory is located
- /// * `name` - the name of the directory to remove
- ///
- /// # Error
- ///
- /// * `ENOTDIR` - `parent` or `child` is not a directory
- /// * `ENOENT` - `name` does not exist in `parent`
- /// * `ENOTEMPTY` - `child` is not empty
- pub fn rmdir(&mut self, parent: InodeId, name: &str) -> Result<()> {
- let mut parent = self.read_inode(parent);
- // Can only remove a directory in a directory
- if !parent.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", parent.id);
- }
- let mut child = self.read_inode(self.dir_find_entry(&parent, name)?.inode());
- // Child must be a directory
- if !child.inode.is_dir() {
- return_error!(ErrCode::ENOTDIR, "Inode {} is not a directory", child.id);
- }
- // Child must be empty
- if self.dir_get_all_entries(&child).len() > 2 {
- return_error!(ErrCode::ENOTEMPTY, "Directory {} is not empty", child.id);
- }
- // Remove directory entry
- self.unlink_inode(&mut parent, &mut child, name)
- }
- }
|