From 51118228f978f8dde956016f4cf3f169d9e21f20 Mon Sep 17 00:00:00 2001
From: Chuyan Zhang <me@zcy.moe>
Date: Mon, 27 Nov 2023 02:24:52 -0800
Subject: Implemented read

---
 src/filesystem/trait_impl.rs | 176 +++++++++++++++++++++++++++++++++----------
 1 file changed, 135 insertions(+), 41 deletions(-)

(limited to 'src/filesystem')

diff --git a/src/filesystem/trait_impl.rs b/src/filesystem/trait_impl.rs
index 5353f6e..2a49eeb 100644
--- a/src/filesystem/trait_impl.rs
+++ b/src/filesystem/trait_impl.rs
@@ -8,14 +8,12 @@ use fuser::{
     Filesystem, KernelConfig, ReplyAttr, ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry,
     ReplyLseek, ReplyOpen, ReplyWrite, Request, TimeOrNow,
 };
-use libc::{
-    c_int, EACCES, EBADF, EEXIST, EINVAL, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR,
-    ENOTEMPTY, EPERM, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY, R_OK, S_ISGID, S_ISUID, S_IXGRP,
-    S_IXOTH, S_IXUSR, W_OK,
-};
+use libc::{c_int, EACCES, EBADF, EEXIST, EINVAL, EIO, EISDIR, ENAMETOOLONG, ENOENT, ENOSPC, ENOSYS, ENOTDIR, ENOTEMPTY, EPERM, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY, R_OK, S_ISGID, S_ISUID, S_IXGRP, S_IXOTH, S_IXUSR, W_OK};
 use log::debug;
 use std::ffi::OsStr;
 use std::time::SystemTime;
+use crate::block_device::BLOCK_SIZE;
+use crate::disk::block::DataBlock;
 
 impl AyaFS {}
 
@@ -537,22 +535,116 @@ impl Filesystem for AyaFS {
         }
     }
 
-    fn open(&mut self, _req: &Request<'_>, _ino: u64, _flags: i32, reply: ReplyOpen) {
-        todo!()
+    fn open(&mut self, req: &Request<'_>, ino: u64, flags: i32, reply: ReplyOpen) {
+        debug!("open(ino: {:#x?}, flags: {:#x?})", ino, flags);
+        let (access_mask, read, write) = match flags & O_ACCMODE {
+            O_RDONLY => {
+                // Behavior is undefined, but most filesystems return EACCES
+                if flags & libc::O_TRUNC != 0 {
+                    reply.error(libc::EACCES);
+                    return;
+                }
+                (R_OK, true, false)
+            }
+            O_WRONLY => (W_OK, false, true),
+            O_RDWR => (R_OK | W_OK, true, true),
+            _ => {
+                // flag 非法
+                reply.error(EINVAL);
+                return;
+            }
+        };
+
+        match self.get_inode(ino as usize) {
+            Some(inode) => {
+                if !check_access(
+                    req.uid(),
+                    req.gid(),
+                    inode.uid,
+                    inode.gid,
+                    inode.mode,
+                    access_mask,
+                ) {
+                    reply.error(EACCES);
+                    return;
+                }
+                let fd = self.allocate_file_descriptor(ino as usize, read, write);
+                reply.opened(fd, 0);
+            }
+            None => {
+                reply.error(ENOENT);
+            }
+        }
     }
 
+    // read [offset, offset + size)
+    // - EOF < offset + size -> return EOF - offset
+    // - EOF > offset + size -> return size
     fn read(
         &mut self,
-        _req: &Request<'_>,
+        req: &Request<'_>,
         ino: u64,
-        _fh: u64,
+        fh: u64,
         offset: i64,
-        _size: u32,
+        size: u32,
         _flags: i32,
-        _lock_owner: Option<u64>,
+        _lock_owner: Option<u64>, // 用不到!
         reply: ReplyData,
     ) {
-        todo!()
+        assert_eq!(self.file_handle_map.get(&fh).unwrap().0, ino as usize);
+        if let Some(inode) = self.get_inode(ino as usize) {
+            if inode.is_dir() {
+                reply.error(EISDIR);
+                return;
+            }
+            if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) {
+                reply.error(EACCES);
+                return;
+            }
+            debug!("reading inode {:#x} (offset {} size {})", ino, offset, size);
+
+            if offset as u32 >= inode.size {
+                // offset 在 EOF 后面, 直接返回一个 0 长度的空 buffer
+                reply.data(&Vec::new());
+                return;
+            }
+
+            // let read_length = size.min(inode.size.saturating_sub(offset as u32)) as usize;
+            // 这和下面那个是等同的但是不利于让人看懂……
+            let read_length = if offset as u32 + size <= inode.size {
+                size // 没有越过 EOF, 读取 size 个 byte
+            } else {
+                inode.size - offset as u32 // 越过了 EOF, 读取 inode.size - offset 个 byte
+            } as usize;
+
+            let mut read_buffer = vec![0u8; read_length];
+            let mut read_ptr = 0usize;
+
+            let inode = inode.clone();
+
+            while read_ptr < read_length {
+                let current_point = offset as usize + read_ptr;
+                let current_block_index = current_point / BLOCK_SIZE;
+                let current_offset = current_point % BLOCK_SIZE;
+
+                let read_length_within_block = if BLOCK_SIZE - current_offset < read_length - read_ptr {
+                    BLOCK_SIZE - current_offset // 可以读到 block 最后
+                } else {
+                    read_length - read_ptr // 读到中途会停下来
+                };
+
+                if let Some(block) = self.access_block::<DataBlock>(&inode, current_block_index) {
+                    (&mut read_buffer[read_ptr .. read_ptr + read_length_within_block])
+                        .copy_from_slice(&block.block.0[current_offset .. current_offset + read_length_within_block]);
+                    read_ptr += read_length_within_block;
+                } else {
+                    reply.error(EIO);
+                    return;
+                }
+            }
+
+            reply.data(&read_buffer);
+        }
     }
 
     fn write(
@@ -635,38 +727,40 @@ impl Filesystem for AyaFS {
     ) {
         assert_eq!(self.file_handle_map.get(&fh).unwrap().0, ino as usize);
         if let Some(inode) = self.get_inode(ino as usize) {
-            if inode.is_dir() {
-                if check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) {
-                    debug!("reading dir entries from inode {:#x} with offset {}", ino, offset);
-                    let inode = inode.clone();
-
-                    self.load_direntry_map(ino as usize, &inode).unwrap();
-                    for (entry_index, (name, dir_entry)) in self
-                        .dir_entry_map
-                        .get(&(ino as usize))
-                        .unwrap()
-                        .iter()
-                        .enumerate()
-                        .skip(offset as usize)
-                    {
-                        debug!("  entry {} from inode {:#x} with name {:?}", entry_index, dir_entry.inode, name);
-                        if reply.add(
-                            dir_entry.inode as u64,
-                            entry_index as i64 + 1,
-                            to_filetype(dir_entry.file_type).expect("not 0x0!"),
-                            name
-                        ) {
-                            break;
-                        }
-                    }
+            if !inode.is_dir() {
+                reply.error(ENOTDIR);
+                return;
+            }
 
-                    reply.ok();
-                } else {
-                    reply.error(EACCES);
+            if !check_access(req.uid(), req.gid(), inode.uid, inode.gid, inode.mode, R_OK) {
+                reply.error(EACCES);
+                return;
+            }
+
+            debug!("reading dir entries from inode {:#x} with offset {}", ino, offset);
+            let inode = inode.clone();
+
+            self.load_direntry_map(ino as usize, &inode).unwrap();
+            for (entry_index, (name, dir_entry)) in self
+                .dir_entry_map
+                .get(&(ino as usize))
+                .unwrap()
+                .iter()
+                .enumerate()
+                .skip(offset as usize)
+            {
+                debug!("  entry {} from inode {:#x} with name {:?}", entry_index, dir_entry.inode, name);
+                if reply.add(
+                    dir_entry.inode as u64,
+                    entry_index as i64 + 1,
+                    to_filetype(dir_entry.file_type).expect("not 0x0!"),
+                    name
+                ) {
+                    break;
                 }
-            } else {
-                reply.error(ENOTDIR);
             }
+
+            reply.ok();
         } else {
             reply.error(ENOENT);
         }
-- 
cgit v1.2.3-70-g09d2