atproto repo as vfs
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 186 lines 5.1 kB view raw
1use easy_fuser::{prelude::*, templates::DefaultFuseHandler}; 2use tokio::runtime::Handle; 3 4use std::{ 5 ffi::{OsStr, OsString}, 6 io::{Cursor, Read, Seek, SeekFrom}, 7 path::PathBuf, 8 sync::Arc, 9 time::UNIX_EPOCH, 10}; 11 12use crate::{AtpFS, FileType}; 13 14pub struct AtpFuse { 15 pub fs: Arc<AtpFS>, 16 pub inner: DefaultFuseHandler, 17 pub runtime: Handle, 18} 19 20impl AtpFuse { 21 fn path_to_str(&self, path: &std::path::Path) -> String { 22 // Strip leading '/' for VFS compatibility 23 path.to_str() 24 .unwrap_or("") 25 .trim_start_matches('/') 26 .to_string() 27 } 28 29 fn fileattr_for_root(&self) -> FileAttribute { 30 FileAttribute { 31 size: 0, 32 blocks: 0, 33 atime: UNIX_EPOCH, 34 mtime: UNIX_EPOCH, 35 ctime: UNIX_EPOCH, 36 crtime: UNIX_EPOCH, 37 kind: FileKind::Directory, 38 perm: 0o755, 39 nlink: 2, 40 uid: 1000, 41 gid: 1000, 42 rdev: 0, 43 flags: 0, 44 blksize: 512, 45 ttl: None, 46 generation: None, 47 } 48 } 49 50 fn vfs_metadata_attr(&self, vfs_path: &str) -> FuseResult<FileAttribute> { 51 let meta = self 52 .runtime 53 .block_on(self.fs.metadata(vfs_path)) 54 .map_err(|_| ErrorKind::FileNotFound.to_error("Not found"))?; 55 56 let (kind, perm, nlink) = match meta.file_type { 57 FileType::Directory => (FileKind::Directory, 0o755, 2), 58 FileType::File => (FileKind::RegularFile, 0o644, 1), 59 }; 60 61 Ok(FileAttribute { 62 size: meta.len, 63 blocks: (meta.len + 511) / 512, 64 atime: UNIX_EPOCH, 65 mtime: UNIX_EPOCH, 66 ctime: UNIX_EPOCH, 67 crtime: UNIX_EPOCH, 68 kind, 69 perm, 70 nlink, 71 uid: 1000, 72 gid: 1000, 73 rdev: 0, 74 flags: 0, 75 blksize: 512, 76 ttl: None, 77 generation: None, 78 }) 79 } 80} 81 82impl FuseHandler<PathBuf> for AtpFuse { 83 fn get_inner(&self) -> &dyn FuseHandler<PathBuf> { 84 &self.inner 85 } 86 87 fn lookup( 88 &self, 89 _req: &RequestInfo, 90 parent_id: PathBuf, 91 name: &OsStr, 92 ) -> FuseResult<FileAttribute> { 93 let path = parent_id.join(name); 94 let vfs_path = self.path_to_str(&path); 95 self.vfs_metadata_attr(&vfs_path) 96 } 97 98 fn getattr( 99 &self, 100 _req: &RequestInfo, 101 file_id: PathBuf, 102 _fh: Option<BorrowedFileHandle>, 103 ) -> FuseResult<FileAttribute> { 104 // Root handling 105 if file_id.as_os_str().is_empty() || file_id == PathBuf::from("/") { 106 return Ok(self.fileattr_for_root()); 107 } 108 109 let vfs_path = self.path_to_str(&file_id); 110 self.vfs_metadata_attr(&vfs_path) 111 } 112 113 fn readdir( 114 &self, 115 _req: &RequestInfo, 116 file_id: PathBuf, 117 _fh: BorrowedFileHandle, 118 ) -> FuseResult<Vec<(OsString, FileKind)>> { 119 let vfs_path = self.path_to_str(&file_id); 120 121 let stream = self 122 .runtime 123 .block_on(self.fs.read_dir(&vfs_path)) 124 .map_err(|_| ErrorKind::InputOutputError.to_error("Read dir failed"))?; 125 126 let mut entries = vec![ 127 (OsString::from("."), FileKind::Directory), 128 (OsString::from(".."), FileKind::Directory), 129 ]; 130 131 for name in stream { 132 let kind = if name.ends_with(".json") { 133 FileKind::RegularFile 134 } else { 135 FileKind::Directory 136 }; 137 entries.push((OsString::from(name), kind)); 138 } 139 140 Ok(entries) 141 } 142 143 fn read( 144 &self, 145 _req: &RequestInfo, 146 file_id: PathBuf, 147 _fh: BorrowedFileHandle, 148 seek: SeekFrom, 149 size: u32, 150 _flags: FUSEOpenFlags, 151 _lock_owner: Option<u64>, 152 ) -> FuseResult<Vec<u8>> { 153 // Only support absolute start seeks for now. 154 let pos = match seek { 155 SeekFrom::Start(p) => p, 156 SeekFrom::Current(_) | SeekFrom::End(_) => { 157 return Err(ErrorKind::InputOutputError.to_error("Unsupported seek")) 158 } 159 }; 160 161 if size == 0 { 162 return Ok(Vec::new()); 163 } 164 165 let vfs_path = self.path_to_str(&file_id); 166 let data = self 167 .runtime 168 .block_on(self.fs.open_file(&vfs_path)) 169 .map_err(|_| ErrorKind::FileNotFound.to_error("File not found"))?; 170 let mut reader = Cursor::new(data.as_slice()); 171 172 // Seek to the requested position. 173 reader 174 .seek(SeekFrom::Start(pos)) 175 .map_err(|_| ErrorKind::InputOutputError.to_error("Seek failed"))?; 176 177 // Read up to `size` bytes into the buffer. 178 let mut buf = vec![0u8; size as usize]; 179 let n = reader 180 .read(&mut buf) 181 .map_err(|_| ErrorKind::InputOutputError.to_error("Read failed"))?; 182 183 buf.truncate(n); 184 Ok(buf) 185 } 186}