1use fuser::{FileType, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, ReplyWrite};
4use libc::ENOENT;
5use tracing::{info, warn};
6
7use super::{RagFs, TTL, reply_file_bytes};
8use crate::inode::{
9 FIRST_REAL_INO, HISTORY_FILE_INO, RAGFS_DIR_INO, SAFETY_DIR_INO, TRASH_DIR_INO, UNDO_FILE_INO,
10};
11
12impl RagFs {
13 pub(crate) fn lookup_safety_dir(&mut self, name_str: &str, reply: ReplyEntry) {
14 match name_str {
15 ".trash" => {
16 let attr = self.make_attr(TRASH_DIR_INO, FileType::Directory, 0);
17 reply.entry(&TTL, &attr, 0);
18 }
19 ".history" => {
20 let content = self.safety_manager.get_history_json(Some(100));
21 let attr = self.make_attr(
22 HISTORY_FILE_INO,
23 FileType::RegularFile,
24 content.len() as u64,
25 );
26 let mut cache = self.runtime.block_on(self.content_cache.write());
27 cache.insert(HISTORY_FILE_INO, content);
28 reply.entry(&TTL, &attr, 0);
29 }
30 ".undo" => {
31 let attr = self.make_attr(UNDO_FILE_INO, FileType::RegularFile, 0);
32 reply.entry(&TTL, &attr, 0);
33 }
34 _ => reply.error(ENOENT),
35 }
36 }
37
38 pub(crate) fn getattr_safety(&mut self, ino: u64, reply: ReplyAttr) {
39 match ino {
40 SAFETY_DIR_INO | TRASH_DIR_INO => {
41 let attr = self.make_attr(ino, FileType::Directory, 0);
42 reply.attr(&TTL, &attr);
43 }
44 HISTORY_FILE_INO => {
45 let content = self.safety_manager.get_history_json(Some(100));
46 let size = content.len() as u64;
47 let mut cache = self.runtime.block_on(self.content_cache.write());
48 cache.insert(HISTORY_FILE_INO, content);
49 let attr = self.make_attr(ino, FileType::RegularFile, size);
50 reply.attr(&TTL, &attr);
51 }
52 UNDO_FILE_INO => {
53 let attr = self.make_attr(ino, FileType::RegularFile, 0);
54 reply.attr(&TTL, &attr);
55 }
56 _ => reply.error(ENOENT),
57 }
58 }
59
60 pub(crate) fn read_safety(&mut self, ino: u64, offset: i64, size: u32, reply: ReplyData) {
61 match ino {
62 HISTORY_FILE_INO => {
63 let content = self.safety_manager.get_history_json(Some(100));
64 reply_file_bytes(reply, &content, offset, size);
65 }
66 UNDO_FILE_INO => {
67 reply.data(&[]);
68 }
69 _ => reply.error(ENOENT),
70 }
71 }
72
73 pub(crate) fn readdir_safety(&mut self, ino: u64, offset: i64, mut reply: ReplyDirectory) {
74 match ino {
75 SAFETY_DIR_INO => {
76 let entries = [
77 (SAFETY_DIR_INO, FileType::Directory, "."),
78 (RAGFS_DIR_INO, FileType::Directory, ".."),
79 (TRASH_DIR_INO, FileType::Directory, ".trash"),
80 (HISTORY_FILE_INO, FileType::RegularFile, ".history"),
81 (UNDO_FILE_INO, FileType::RegularFile, ".undo"),
82 ];
83
84 for (i, (ino, kind, name)) in entries.iter().enumerate().skip(offset as usize) {
85 if reply.add(*ino, (i + 1) as i64, *kind, name) {
86 break;
87 }
88 }
89 reply.ok();
90 }
91 TRASH_DIR_INO => {
92 let mut entries: Vec<(u64, FileType, String)> = vec![
93 (TRASH_DIR_INO, FileType::Directory, ".".to_string()),
94 (SAFETY_DIR_INO, FileType::Directory, "..".to_string()),
95 ];
96
97 let trash = self.runtime.block_on(self.safety_manager.list_trash());
98 for entry in trash {
99 let entry_ino =
100 FIRST_REAL_INO + 500_000 + (entry.id.as_u128() % 100_000) as u64;
101 entries.push((entry_ino, FileType::RegularFile, entry.id.to_string()));
102 }
103
104 for (i, (ino, kind, name)) in entries.iter().enumerate().skip(offset as usize) {
105 if reply.add(*ino, (i + 1) as i64, *kind, name) {
106 break;
107 }
108 }
109 reply.ok();
110 }
111 _ => reply.error(ENOENT),
112 }
113 }
114
115 pub(crate) fn write_safety(&mut self, _ino: u64, data: &[u8], reply: ReplyWrite) {
116 let data_str = String::from_utf8_lossy(data).trim().to_string();
117 if let Ok(operation_id) = uuid::Uuid::parse_str(&data_str) {
118 let safety_manager = self.safety_manager.clone();
119 let result = self
120 .runtime
121 .block_on(async move { safety_manager.undo(operation_id).await });
122 match result {
123 Ok(msg) => info!("Undo successful: {}", msg),
124 Err(e) => warn!("Undo failed: {}", e),
125 }
126 } else {
127 warn!("Invalid operation ID for undo: {}", data_str);
128 }
129 reply.written(data.len() as u32);
130 }
131}