1use std::collections::HashMap;
2use std::ffi::OsStr;
3use std::io;
4use std::io::{Seek, SeekFrom};
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::sync::{Arc, Mutex};
7use std::time::{Duration, SystemTime, UNIX_EPOCH};
8
9use bytes::Bytes;
10use fuser::{
11 BsdFileFlags, Errno, FileAttr, FileHandle, FileType, Filesystem, FopenFlags, Generation,
12 INodeNo, KernelConfig, LockOwner, MountOption, OpenFlags, RenameFlags, ReplyAttr, ReplyCreate,
13 ReplyData, ReplyDirectory, ReplyEmpty, ReplyEntry, ReplyOpen, ReplyStatfs, ReplyWrite, Request,
14 TimeOrNow, WriteFlags,
15};
16use log::{debug, error, warn};
17use mtp_rs::mtp::{DeviceEvent, MtpDevice};
18use mtp_rs::{NewObjectInfo, ObjectHandle, Storage};
19
20use crate::buffer::WriteBuffer;
21use crate::inode::{InodeEntry, InodeKind, InodeTable, FUSE_ROOT_INODE};
22use crate::sparse_cache::SparseCache;
23
24const TTL: Duration = Duration::from_secs(1);
25
26fn mtp_datetime_to_system_time(dt: &mtp_rs::DateTime) -> SystemTime {
27 fn days_from_civil(y: i64, m: i64, d: i64) -> i64 {
28 let y = if m <= 2 { y - 1 } else { y };
29 let era = if y >= 0 { y } else { y - 399 } / 400;
30 let yoe = (y - era * 400) as u64;
31 let m_adj = if m > 2 { m - 3 } else { m + 9 } as u64;
32 let doy = (153 * m_adj + 2) / 5 + d as u64 - 1;
33 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
34 era * 146097 + doe as i64 - 719468
35 }
36
37 let days = days_from_civil(dt.year as i64, dt.month as i64, dt.day as i64);
38 let secs = days * 86400 + dt.hour as i64 * 3600 + dt.minute as i64 * 60 + dt.second as i64;
39 if secs >= 0 {
40 UNIX_EPOCH + Duration::from_secs(secs as u64)
41 } else {
42 UNIX_EPOCH
43 }
44}
45
46fn inode_to_file_attr(entry: &InodeEntry) -> FileAttr {
47 let uid = unsafe { libc::getuid() };
48 let gid = unsafe { libc::getgid() };
49 FileAttr {
50 ino: INodeNo(entry.inode),
51 size: entry.size,
52 blocks: entry.size.div_ceil(512),
53 atime: entry.atime,
54 mtime: entry.mtime,
55 ctime: entry.mtime,
56 crtime: entry.mtime,
57 kind: if entry.is_dir() {
58 FileType::Directory
59 } else {
60 FileType::RegularFile
61 },
62 perm: if entry.is_dir() { 0o755 } else { 0o644 },
63 nlink: if entry.is_dir() { 2 } else { 1 },
64 uid,
65 gid,
66 rdev: 0,
67 blksize: 4096,
68 flags: 0,
69 }
70}
71
72fn bytes_stream(
74 data: Vec<u8>,
75) -> futures::stream::Iter<std::vec::IntoIter<Result<Bytes, io::Error>>> {
76 let chunks = if data.is_empty() {
77 vec![Ok(Bytes::new())]
78 } else {
79 vec![Ok(Bytes::from(data))]
80 };
81 futures::stream::iter(chunks)
82}
83
84fn file_stream(
86 mut file: std::fs::File,
87) -> futures::stream::Iter<std::vec::IntoIter<Result<Bytes, io::Error>>> {
88 use std::io::Read as _;
89 let mut chunks = Vec::new();
90 loop {
91 let mut buf = vec![0u8; 65536];
92 match file.read(&mut buf) {
93 Ok(0) => break,
94 Ok(n) => {
95 buf.truncate(n);
96 chunks.push(Ok(Bytes::from(buf)));
97 }
98 Err(e) => {
99 chunks.push(Err(e));
100 break;
101 }
102 }
103 }
104 futures::stream::iter(chunks)
105}
106
107struct Inner {
109 storages: Vec<Storage>,
110 inodes: InodeTable,
111 write_buf: WriteBuffer,
112 read_cache: HashMap<u64, SparseCache>,
113 dirs_loaded: HashMap<u64, bool>,
114 fh_to_inode: HashMap<u64, u64>,
115}
116
117pub struct MtpFs {
119 rt: tokio::runtime::Handle,
120 device: Mutex<MtpDevice>,
121 event_device: MtpDevice,
123 inner: Arc<Mutex<Inner>>,
124 next_fh: AtomicU64,
125 read_only: bool,
126 fetch_counter: Arc<AtomicU64>,
129}
130
131impl MtpFs {
132 pub fn new(device: MtpDevice, read_only: bool, rt: tokio::runtime::Handle) -> Self {
133 let event_device = device.clone();
134 Self {
135 rt,
136 device: Mutex::new(device),
137 event_device,
138 inner: Arc::new(Mutex::new(Inner {
139 storages: Vec::new(),
140 inodes: InodeTable::new(),
141 write_buf: WriteBuffer::new(),
142 read_cache: HashMap::new(),
143 dirs_loaded: HashMap::new(),
144 fh_to_inode: HashMap::new(),
145 })),
146 next_fh: AtomicU64::new(1),
147 read_only,
148 fetch_counter: Arc::new(AtomicU64::new(0)),
149 }
150 }
151
152 #[allow(dead_code)] pub fn fetch_counter(&self) -> Arc<AtomicU64> {
158 Arc::clone(&self.fetch_counter)
159 }
160
161 fn alloc_fh(&self) -> u64 {
162 self.next_fh.fetch_add(1, Ordering::Relaxed)
163 }
164
165 fn find_storage_index(inner: &Inner, inode: u64) -> Option<usize> {
167 let mut current = inode;
168 loop {
169 let entry = inner.inodes.get(current)?;
170 if let InodeKind::Storage { storage_id } = &entry.kind {
171 return inner
172 .storages
173 .iter()
174 .position(|s: &Storage| s.id() == *storage_id);
175 }
176 if current == entry.parent {
177 return None;
178 }
179 current = entry.parent;
180 }
181 }
182
183 fn mtp_parent_handle(inner: &Inner, inode: u64) -> Option<Option<ObjectHandle>> {
185 let entry = inner.inodes.get(inode)?;
186 match &entry.kind {
187 InodeKind::Storage { .. } => Some(None),
188 InodeKind::Directory { handle } => Some(Some(*handle)),
189 _ => None,
190 }
191 }
192
193 fn load_dir(&self, inner: &mut Inner, parent_inode: u64) {
195 if inner.dirs_loaded.get(&parent_inode) == Some(&true) {
196 return;
197 }
198
199 if parent_inode == FUSE_ROOT_INODE {
200 inner.dirs_loaded.insert(parent_inode, true);
201 return;
202 }
203
204 let mtp_parent = match Self::mtp_parent_handle(inner, parent_inode) {
205 Some(p) => p,
206 None => return,
207 };
208
209 let storage_idx = match Self::find_storage_index(inner, parent_inode) {
210 Some(i) => i,
211 None => return,
212 };
213
214 let objects = match self
215 .rt
216 .block_on(inner.storages[storage_idx].list_objects(mtp_parent))
217 {
218 Ok(objs) => objs,
219 Err(e) => {
220 error!("Failed to list MTP objects: {e}");
221 return;
222 }
223 };
224
225 inner.inodes.clear_children(parent_inode);
226
227 for obj in objects {
228 let mtime = obj
229 .modified
230 .as_ref()
231 .map(mtp_datetime_to_system_time)
232 .unwrap_or(UNIX_EPOCH);
233 let is_folder = obj.is_folder();
234 inner.inodes.add_object(
235 parent_inode,
236 obj.handle,
237 obj.filename,
238 is_folder,
239 obj.size,
240 mtime,
241 );
242 }
243
244 inner.dirs_loaded.insert(parent_inode, true);
245 }
246
247 fn flush_to_mtp(&self, inner: &mut Inner, fh: u64) {
253 let buf = match inner.write_buf.close(fh) {
254 Some(b) => b,
255 None => return,
256 };
257
258 if !buf.is_dirty() {
259 return;
260 }
261
262 let inode = buf.inode;
263 let mut file = buf.into_file();
264 if let Err(e) = file.seek(SeekFrom::Start(0)) {
265 error!("Flush: failed to rewind temp file: {e}");
266 return;
267 }
268 let file_len = file.seek(SeekFrom::End(0)).unwrap_or(0);
269 if let Err(e) = file.seek(SeekFrom::Start(0)) {
270 error!("Flush: failed to rewind temp file: {e}");
271 return;
272 }
273 let entry = match inner.inodes.get(inode) {
274 Some(e) => e.clone(),
275 None => {
276 error!("Flush: inode {inode} not found");
277 return;
278 }
279 };
280
281 let handle = match &entry.kind {
282 InodeKind::File { handle } => *handle,
283 _ => {
284 error!("Flush: inode {inode} is not a file");
285 return;
286 }
287 };
288
289 let storage_idx = match Self::find_storage_index(inner, inode) {
290 Some(i) => i,
291 None => {
292 error!("Flush: no storage for inode {inode}");
293 return;
294 }
295 };
296
297 let parent_handle = inner.inodes.get(entry.parent).and_then(|p| match &p.kind {
298 InodeKind::Storage { .. } => None,
299 InodeKind::Directory { handle } => Some(*handle),
300 _ => None,
301 });
302
303 let supports_rename = self.device.lock().unwrap().supports_rename();
304
305 if supports_rename {
306 self.flush_safe(
307 inner,
308 inode,
309 handle,
310 storage_idx,
311 parent_handle,
312 &entry,
313 file_len,
314 file,
315 );
316 } else {
317 warn!(
318 "Flush: device does not support rename, using delete-then-upload \
319 (data loss possible if upload fails)"
320 );
321 self.flush_unsafe(
322 inner,
323 inode,
324 handle,
325 storage_idx,
326 parent_handle,
327 &entry,
328 file_len,
329 file,
330 );
331 }
332 }
333
334 #[allow(clippy::too_many_arguments)]
336 fn flush_safe(
337 &self,
338 inner: &mut Inner,
339 inode: u64,
340 old_handle: ObjectHandle,
341 storage_idx: usize,
342 parent_handle: Option<ObjectHandle>,
343 entry: &InodeEntry,
344 size: u64,
345 file: std::fs::File,
346 ) {
347 let storage = &inner.storages[storage_idx];
348 let temp_name = format!(".~tmp~{}", entry.name);
349
350 let info = NewObjectInfo::file(&temp_name, size);
352 let stream = file_stream(file);
353 let new_handle = match self
354 .rt
355 .block_on(storage.upload(parent_handle, info, stream))
356 {
357 Ok(h) => h,
358 Err(e) => {
359 error!("Flush: upload failed (original file untouched): {e}");
360 return;
361 }
362 };
363
364 if let Err(e) = self.rt.block_on(storage.delete(old_handle)) {
366 error!("Flush: failed to delete old object (new data saved as '{temp_name}'): {e}");
367 if let Some(e) = inner.inodes.get_mut(inode) {
368 e.kind = InodeKind::File { handle: new_handle };
369 e.name = temp_name;
370 e.size = size;
371 e.mtime = SystemTime::now();
372 }
373 return;
374 }
375
376 if let Err(e) = self.rt.block_on(storage.rename(new_handle, &entry.name)) {
378 warn!(
379 "Flush: rename from '{temp_name}' to '{}' failed: {e}",
380 entry.name
381 );
382 if let Some(e) = inner.inodes.get_mut(inode) {
383 e.kind = InodeKind::File { handle: new_handle };
384 e.name = temp_name;
385 e.size = size;
386 e.mtime = SystemTime::now();
387 }
388 return;
389 }
390
391 if let Some(e) = inner.inodes.get_mut(inode) {
392 e.kind = InodeKind::File { handle: new_handle };
393 e.size = size;
394 e.mtime = SystemTime::now();
395 }
396 }
397
398 #[allow(clippy::too_many_arguments)]
400 fn flush_unsafe(
401 &self,
402 inner: &mut Inner,
403 inode: u64,
404 old_handle: ObjectHandle,
405 storage_idx: usize,
406 parent_handle: Option<ObjectHandle>,
407 entry: &InodeEntry,
408 size: u64,
409 file: std::fs::File,
410 ) {
411 let storage = &inner.storages[storage_idx];
412
413 if let Err(e) = self.rt.block_on(storage.delete(old_handle)) {
414 error!("Flush: failed to delete old object: {e}");
415 return;
416 }
417
418 let info = NewObjectInfo::file(&entry.name, size);
419 let stream = file_stream(file);
420
421 match self
422 .rt
423 .block_on(storage.upload(parent_handle, info, stream))
424 {
425 Ok(new_handle) => {
426 if let Some(e) = inner.inodes.get_mut(inode) {
427 e.kind = InodeKind::File { handle: new_handle };
428 e.size = size;
429 e.mtime = SystemTime::now();
430 }
431 }
432 Err(e) => {
433 error!("Flush: upload failed after delete (data lost): {e}");
434 }
435 }
436 }
437
438 pub fn mount_options(&self) -> Vec<MountOption> {
439 let mut opts = vec![
440 MountOption::FSName("mtp-mount".to_string()),
441 MountOption::Subtype("mtp".to_string()),
442 MountOption::DefaultPermissions,
443 MountOption::NoDev,
444 MountOption::NoSuid,
445 ];
446 if self.read_only {
447 opts.push(MountOption::RO);
448 } else {
449 opts.push(MountOption::RW);
450 }
451 opts
452 }
453
454 async fn event_loop(device: MtpDevice, inner: Arc<Mutex<Inner>>) {
457 loop {
458 match tokio::time::timeout(Duration::from_millis(200), device.next_event()).await {
459 Ok(Ok(event)) => {
460 Self::handle_event(&inner, &event);
461 }
462 Ok(Err(mtp_rs::Error::Disconnected)) => {
463 debug!("Event loop: device disconnected");
464 break;
465 }
466 Ok(Err(mtp_rs::Error::Timeout)) => continue,
467 Ok(Err(e)) => {
468 warn!("Event loop error: {e}");
469 break;
470 }
471 Err(_) => continue, }
473 }
474 }
475
476 fn handle_event(inner: &Mutex<Inner>, event: &DeviceEvent) {
478 match event {
479 DeviceEvent::ObjectAdded { handle } => {
480 debug!("Event: object added {:?}", handle);
481 let mut inner = inner.lock().unwrap();
482 if let Some(parent_ino) = inner.inodes.find_parent_by_handle(*handle) {
486 inner.dirs_loaded.remove(&parent_ino);
487 } else {
488 Self::invalidate_all_dirs(&mut inner);
489 }
490 }
491 DeviceEvent::ObjectRemoved { handle } => {
492 debug!("Event: object removed {:?}", handle);
493 let mut inner = inner.lock().unwrap();
494 if let Some(parent_ino) = inner.inodes.find_parent_by_handle(*handle) {
495 inner.dirs_loaded.remove(&parent_ino);
496 } else {
497 Self::invalidate_all_dirs(&mut inner);
498 }
499 }
500 DeviceEvent::ObjectInfoChanged { handle } => {
501 debug!("Event: object info changed {:?}", handle);
502 let mut inner = inner.lock().unwrap();
503 if let Some(parent_ino) = inner.inodes.find_parent_by_handle(*handle) {
505 inner.dirs_loaded.remove(&parent_ino);
506 }
507 let fhs_to_clear: Vec<u64> = inner
509 .fh_to_inode
510 .iter()
511 .filter_map(|(&fh, &ino)| {
512 inner.inodes.get(ino).and_then(|e| match &e.kind {
513 InodeKind::File { handle: h } if *h == *handle => Some(fh),
514 _ => None,
515 })
516 })
517 .collect();
518 for fh in fhs_to_clear {
519 inner.read_cache.remove(&fh);
520 }
521 }
522 DeviceEvent::StoreAdded { .. }
523 | DeviceEvent::StoreRemoved { .. }
524 | DeviceEvent::StorageInfoChanged { .. } => {
525 debug!("Event: storage change {:?}", event);
526 let mut inner = inner.lock().unwrap();
528 Self::invalidate_all_dirs(&mut inner);
529 }
530 _ => {
531 debug!("Event: unhandled {:?}", event);
532 }
533 }
534 }
535
536 fn invalidate_all_dirs(inner: &mut Inner) {
538 inner.dirs_loaded.retain(|&k, _| k == FUSE_ROOT_INODE);
539 }
540}
541
542impl Filesystem for MtpFs {
543 fn init(&mut self, _req: &Request, _config: &mut KernelConfig) -> io::Result<()> {
544 let storages = self
545 .rt
546 .block_on(self.device.lock().unwrap().storages())
547 .map_err(|e: mtp_rs::Error| io::Error::other(e.to_string()))?;
548
549 let mut inner = self.inner.lock().unwrap();
550 for storage in &storages {
551 let storage: &Storage = storage;
552 let name = if storage.info().description.is_empty() {
553 format!("Storage_{}", storage.id().0)
554 } else {
555 storage.info().description.clone()
556 };
557 inner.inodes.add_storage(storage.id(), name);
558 }
559 inner.dirs_loaded.insert(FUSE_ROOT_INODE, true);
560 inner.storages = storages;
561 drop(inner);
562
563 let event_device = self.event_device.clone();
566 let event_inner = Arc::clone(&self.inner);
567 self.rt.spawn(async move {
568 Self::event_loop(event_device, event_inner).await;
569 });
570
571 debug!(
572 "MtpFs initialized with {} storages + event monitor",
573 self.inner.lock().unwrap().storages.len()
574 );
575 Ok(())
576 }
577
578 fn lookup(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEntry) {
579 let parent_ino = parent.0;
580 let name_str = match name.to_str() {
581 Some(s) => s,
582 None => {
583 reply.error(Errno::ENOENT);
584 return;
585 }
586 };
587
588 let mut inner = self.inner.lock().unwrap();
589 self.load_dir(&mut inner, parent_ino);
590
591 match inner.inodes.lookup(parent_ino, name_str) {
592 Some(ino) => {
593 let entry = inner.inodes.get(ino).unwrap();
594 let attr = inode_to_file_attr(entry);
595 reply.entry(&TTL, &attr, Generation(0));
596 }
597 None => {
598 reply.error(Errno::ENOENT);
599 }
600 }
601 }
602
603 fn getattr(&self, _req: &Request, ino: INodeNo, _fh: Option<FileHandle>, reply: ReplyAttr) {
604 let inner = self.inner.lock().unwrap();
605 match inner.inodes.get(ino.0) {
606 Some(entry) => {
607 let mut attr = inode_to_file_attr(entry);
608 for (&fh, &inode) in &inner.fh_to_inode {
609 if inode == ino.0 {
610 if let Some(size) = inner.write_buf.size(fh) {
611 attr.size = size;
612 attr.blocks = size.div_ceil(512);
613 }
614 break;
615 }
616 }
617 reply.attr(&TTL, &attr);
618 }
619 None => {
620 reply.error(Errno::ENOENT);
621 }
622 }
623 }
624
625 fn readdir(
626 &self,
627 _req: &Request,
628 ino: INodeNo,
629 _fh: FileHandle,
630 offset: u64,
631 mut reply: ReplyDirectory,
632 ) {
633 let ino_val = ino.0;
634
635 let mut inner = self.inner.lock().unwrap();
636 self.load_dir(&mut inner, ino_val);
637
638 let parent_ino = inner
639 .inodes
640 .get(ino_val)
641 .map(|e| e.parent)
642 .unwrap_or(FUSE_ROOT_INODE);
643
644 let mut entries: Vec<(u64, INodeNo, FileType, String)> = vec![
645 (1, INodeNo(ino_val), FileType::Directory, ".".to_string()),
646 (
647 2,
648 INodeNo(parent_ino),
649 FileType::Directory,
650 "..".to_string(),
651 ),
652 ];
653
654 let children = inner.inodes.children(ino_val);
655 for (i, child_ino) in children.iter().enumerate() {
656 if let Some(child) = inner.inodes.get(*child_ino) {
657 let kind = if child.is_dir() {
658 FileType::Directory
659 } else {
660 FileType::RegularFile
661 };
662 entries.push((i as u64 + 3, INodeNo(*child_ino), kind, child.name.clone()));
663 }
664 }
665
666 for (i, (off, ino, kind, name)) in entries.iter().enumerate() {
667 if i as u64 >= offset && reply.add(*ino, *off, *kind, name) {
668 break;
669 }
670 }
671 reply.ok();
672 }
673
674 fn open(&self, _req: &Request, ino: INodeNo, _flags: OpenFlags, reply: ReplyOpen) {
675 let mut inner = self.inner.lock().unwrap();
676 match inner.inodes.get(ino.0) {
677 Some(entry) if !entry.is_dir() => {
678 let fh = self.alloc_fh();
679 inner.fh_to_inode.insert(fh, ino.0);
680 reply.opened(FileHandle(fh), FopenFlags::empty());
681 }
682 Some(_) => {
683 reply.error(Errno::EISDIR);
684 }
685 None => {
686 reply.error(Errno::ENOENT);
687 }
688 }
689 }
690
691 fn read(
692 &self,
693 _req: &Request,
694 ino: INodeNo,
695 fh: FileHandle,
696 offset: u64,
697 size: u32,
698 _flags: OpenFlags,
699 _lock_owner: Option<LockOwner>,
700 reply: ReplyData,
701 ) {
702 let fh_val = fh.0;
703 let mut inner = self.inner.lock().unwrap();
704
705 if inner.write_buf.is_open(fh_val) {
707 match inner.write_buf.read(fh_val, offset as i64, size) {
708 Ok(data) => reply.data(&data),
709 Err(e) => {
710 error!("Read from write buffer failed: {e}");
711 reply.error(Errno::EIO);
712 }
713 }
714 return;
715 }
716
717 let entry = match inner.inodes.get(ino.0) {
719 Some(e) => e.clone(),
720 None => {
721 reply.error(Errno::ENOENT);
722 return;
723 }
724 };
725
726 let handle = match &entry.kind {
727 InodeKind::File { handle } => *handle,
728 _ => {
729 reply.error(Errno::EISDIR);
730 return;
731 }
732 };
733
734 let storage_idx = match Self::find_storage_index(&inner, ino.0) {
735 Some(i) => i,
736 None => {
737 reply.error(Errno::EIO);
738 return;
739 }
740 };
741
742 use std::collections::hash_map::Entry;
744 if let Entry::Vacant(slot) = inner.read_cache.entry(fh_val) {
745 let cache = match SparseCache::new(entry.size) {
746 Ok(c) => c,
747 Err(e) => {
748 error!("Failed to create sparse cache: {e}");
749 reply.error(Errno::EIO);
750 return;
751 }
752 };
753 slot.insert(cache);
754 }
755
756 let missing = {
758 let cache = inner.read_cache.get(&fh_val).unwrap();
759 cache.missing_ranges(offset, size as u64)
760 };
761
762 const CHUNK: u64 = 1024 * 1024;
766 for range in missing {
767 let mut cursor = range.start;
768 while cursor < range.end {
769 let chunk_size = (range.end - cursor).min(CHUNK) as u32;
770 self.fetch_counter.fetch_add(1, Ordering::Relaxed);
771 let bytes = match self.rt.block_on(
772 inner.storages[storage_idx].download_partial_64(handle, cursor, chunk_size),
773 ) {
774 Ok(b) => b,
775 Err(e) => {
776 error!("MTP download_partial_64 failed at offset {cursor}: {e}");
777 reply.error(Errno::EIO);
778 return;
779 }
780 };
781 let bytes_len = bytes.len() as u64;
782 let cache = inner.read_cache.get_mut(&fh_val).unwrap();
783 if let Err(e) = cache.write_at(cursor, &bytes) {
784 error!("Sparse cache write failed: {e}");
785 reply.error(Errno::EIO);
786 return;
787 }
788 if bytes_len == 0 {
791 break;
792 }
793 cursor += bytes_len;
794 }
795 }
796
797 let cache = inner.read_cache.get_mut(&fh_val).unwrap();
799 match cache.read_at(offset, size as u64) {
800 Ok(buf) => reply.data(&buf),
801 Err(e) => {
802 error!("Sparse cache read failed: {e}");
803 reply.error(Errno::EIO);
804 }
805 }
806 }
807
808 fn release(
809 &self,
810 _req: &Request,
811 _ino: INodeNo,
812 fh: FileHandle,
813 _flags: OpenFlags,
814 _lock_owner: Option<LockOwner>,
815 _flush: bool,
816 reply: ReplyEmpty,
817 ) {
818 let fh_val = fh.0;
819 let mut inner = self.inner.lock().unwrap();
820
821 if inner.write_buf.is_open(fh_val) {
822 self.flush_to_mtp(&mut inner, fh_val);
823 }
824
825 inner.read_cache.remove(&fh_val);
826 inner.fh_to_inode.remove(&fh_val);
827 reply.ok();
828 }
829
830 fn write(
831 &self,
832 _req: &Request,
833 ino: INodeNo,
834 fh: FileHandle,
835 offset: u64,
836 data: &[u8],
837 _write_flags: WriteFlags,
838 _flags: OpenFlags,
839 _lock_owner: Option<LockOwner>,
840 reply: ReplyWrite,
841 ) {
842 if self.read_only {
843 reply.error(Errno::EROFS);
844 return;
845 }
846
847 let fh_val = fh.0;
848 let mut inner = self.inner.lock().unwrap();
849
850 if !inner.write_buf.is_open(fh_val) {
851 let original_size = inner.inodes.get(ino.0).map(|e| e.size).unwrap_or(0);
852 if let Err(e) = inner.write_buf.open(fh_val, ino.0, original_size) {
853 error!("Failed to open write buffer: {e}");
854 reply.error(Errno::EIO);
855 return;
856 }
857 }
858
859 match inner.write_buf.write(fh_val, offset as i64, data) {
860 Ok(written) => reply.written(written),
861 Err(e) => {
862 error!("Write failed: {e}");
863 reply.error(Errno::EIO);
864 }
865 }
866 }
867
868 fn create(
869 &self,
870 _req: &Request,
871 parent: INodeNo,
872 name: &OsStr,
873 _mode: u32,
874 _umask: u32,
875 _flags: i32,
876 reply: ReplyCreate,
877 ) {
878 if self.read_only {
879 reply.error(Errno::EROFS);
880 return;
881 }
882
883 let name_str = match name.to_str() {
884 Some(s) => s,
885 None => {
886 reply.error(Errno::EINVAL);
887 return;
888 }
889 };
890
891 let parent_ino = parent.0;
892 let mut inner = self.inner.lock().unwrap();
893
894 let storage_idx = match Self::find_storage_index(&inner, parent_ino) {
895 Some(i) => i,
896 None => {
897 reply.error(Errno::EIO);
898 return;
899 }
900 };
901
902 let mtp_parent = match Self::mtp_parent_handle(&inner, parent_ino) {
903 Some(p) => p,
904 None => {
905 reply.error(Errno::ENOTDIR);
906 return;
907 }
908 };
909
910 let info = NewObjectInfo::file(name_str, 0);
911 let stream = bytes_stream(Vec::new());
912
913 let handle = match self
914 .rt
915 .block_on(inner.storages[storage_idx].upload(mtp_parent, info, stream))
916 {
917 Ok(h) => h,
918 Err(e) => {
919 error!("MTP create failed: {e}");
920 reply.error(Errno::EIO);
921 return;
922 }
923 };
924
925 let now = SystemTime::now();
926 let ino = inner
927 .inodes
928 .add_object(parent_ino, handle, name_str.to_string(), false, 0, now);
929
930 let fh = self.alloc_fh();
931 inner.fh_to_inode.insert(fh, ino);
932 if let Err(e) = inner.write_buf.open(fh, ino, 0) {
933 error!("Failed to open write buffer: {e}");
934 reply.error(Errno::EIO);
935 return;
936 }
937
938 let entry = inner.inodes.get(ino).unwrap();
939 let attr = inode_to_file_attr(entry);
940 reply.created(
941 &TTL,
942 &attr,
943 Generation(0),
944 FileHandle(fh),
945 FopenFlags::empty(),
946 );
947 }
948
949 fn mkdir(
950 &self,
951 _req: &Request,
952 parent: INodeNo,
953 name: &OsStr,
954 _mode: u32,
955 _umask: u32,
956 reply: ReplyEntry,
957 ) {
958 if self.read_only {
959 reply.error(Errno::EROFS);
960 return;
961 }
962
963 let name_str = match name.to_str() {
964 Some(s) => s,
965 None => {
966 reply.error(Errno::EINVAL);
967 return;
968 }
969 };
970
971 let parent_ino = parent.0;
972 let mut inner = self.inner.lock().unwrap();
973
974 let storage_idx = match Self::find_storage_index(&inner, parent_ino) {
975 Some(i) => i,
976 None => {
977 reply.error(Errno::EIO);
978 return;
979 }
980 };
981
982 let mtp_parent = match Self::mtp_parent_handle(&inner, parent_ino) {
983 Some(p) => p,
984 None => {
985 reply.error(Errno::ENOTDIR);
986 return;
987 }
988 };
989
990 let handle = match self
991 .rt
992 .block_on(inner.storages[storage_idx].create_folder(mtp_parent, name_str))
993 {
994 Ok(h) => h,
995 Err(e) => {
996 error!("MTP mkdir failed: {e}");
997 reply.error(Errno::EIO);
998 return;
999 }
1000 };
1001
1002 let now = SystemTime::now();
1003 let ino = inner
1004 .inodes
1005 .add_object(parent_ino, handle, name_str.to_string(), true, 0, now);
1006
1007 let entry = inner.inodes.get(ino).unwrap();
1008 let attr = inode_to_file_attr(entry);
1009 reply.entry(&TTL, &attr, Generation(0));
1010 }
1011
1012 fn unlink(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) {
1013 if self.read_only {
1014 reply.error(Errno::EROFS);
1015 return;
1016 }
1017
1018 let name_str = match name.to_str() {
1019 Some(s) => s,
1020 None => {
1021 reply.error(Errno::ENOENT);
1022 return;
1023 }
1024 };
1025
1026 let parent_ino = parent.0;
1027 let mut inner = self.inner.lock().unwrap();
1028
1029 let child_ino = match inner.inodes.lookup(parent_ino, name_str) {
1030 Some(i) => i,
1031 None => {
1032 reply.error(Errno::ENOENT);
1033 return;
1034 }
1035 };
1036
1037 let handle = match inner.inodes.get(child_ino).and_then(|e| match &e.kind {
1038 InodeKind::File { handle } => Some(*handle),
1039 _ => None,
1040 }) {
1041 Some(h) => h,
1042 None => {
1043 reply.error(Errno::EISDIR);
1044 return;
1045 }
1046 };
1047
1048 let storage_idx = match Self::find_storage_index(&inner, child_ino) {
1049 Some(i) => i,
1050 None => {
1051 reply.error(Errno::EIO);
1052 return;
1053 }
1054 };
1055
1056 if let Err(e) = self.rt.block_on(inner.storages[storage_idx].delete(handle)) {
1057 error!("MTP delete failed: {e}");
1058 reply.error(Errno::EIO);
1059 return;
1060 }
1061
1062 inner.inodes.remove(child_ino);
1063 reply.ok();
1064 }
1065
1066 fn rmdir(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) {
1067 if self.read_only {
1068 reply.error(Errno::EROFS);
1069 return;
1070 }
1071
1072 let name_str = match name.to_str() {
1073 Some(s) => s,
1074 None => {
1075 reply.error(Errno::ENOENT);
1076 return;
1077 }
1078 };
1079
1080 let parent_ino = parent.0;
1081 let mut inner = self.inner.lock().unwrap();
1082
1083 let child_ino = match inner.inodes.lookup(parent_ino, name_str) {
1084 Some(i) => i,
1085 None => {
1086 reply.error(Errno::ENOENT);
1087 return;
1088 }
1089 };
1090
1091 let handle = match inner.inodes.get(child_ino).and_then(|e| match &e.kind {
1092 InodeKind::Directory { handle } => Some(*handle),
1093 _ => None,
1094 }) {
1095 Some(h) => h,
1096 None => {
1097 reply.error(Errno::ENOTDIR);
1098 return;
1099 }
1100 };
1101
1102 let storage_idx = match Self::find_storage_index(&inner, child_ino) {
1103 Some(i) => i,
1104 None => {
1105 reply.error(Errno::EIO);
1106 return;
1107 }
1108 };
1109
1110 if let Err(e) = self.rt.block_on(inner.storages[storage_idx].delete(handle)) {
1111 error!("MTP rmdir failed: {e}");
1112 reply.error(Errno::EIO);
1113 return;
1114 }
1115
1116 inner.inodes.remove(child_ino);
1117 reply.ok();
1118 }
1119
1120 fn rename(
1121 &self,
1122 _req: &Request,
1123 parent: INodeNo,
1124 name: &OsStr,
1125 newparent: INodeNo,
1126 newname: &OsStr,
1127 _flags: RenameFlags,
1128 reply: ReplyEmpty,
1129 ) {
1130 if self.read_only {
1131 reply.error(Errno::EROFS);
1132 return;
1133 }
1134
1135 let name_str = match name.to_str() {
1136 Some(s) => s,
1137 None => {
1138 reply.error(Errno::ENOENT);
1139 return;
1140 }
1141 };
1142 let newname_str = match newname.to_str() {
1143 Some(s) => s,
1144 None => {
1145 reply.error(Errno::EINVAL);
1146 return;
1147 }
1148 };
1149
1150 let parent_ino = parent.0;
1151 let newparent_ino = newparent.0;
1152 let mut inner = self.inner.lock().unwrap();
1153
1154 let child_ino = match inner.inodes.lookup(parent_ino, name_str) {
1155 Some(i) => i,
1156 None => {
1157 reply.error(Errno::ENOENT);
1158 return;
1159 }
1160 };
1161
1162 let handle = match inner.inodes.get(child_ino).and_then(|e| match &e.kind {
1163 InodeKind::File { handle } | InodeKind::Directory { handle } => Some(*handle),
1164 _ => None,
1165 }) {
1166 Some(h) => h,
1167 None => {
1168 reply.error(Errno::EINVAL);
1169 return;
1170 }
1171 };
1172
1173 let storage_idx = match Self::find_storage_index(&inner, child_ino) {
1174 Some(i) => i,
1175 None => {
1176 reply.error(Errno::EIO);
1177 return;
1178 }
1179 };
1180
1181 if name_str != newname_str {
1182 if let Err(e) = self
1183 .rt
1184 .block_on(inner.storages[storage_idx].rename(handle, newname_str))
1185 {
1186 error!("MTP rename failed: {e}");
1187 reply.error(Errno::EIO);
1188 return;
1189 }
1190 }
1191
1192 if parent_ino != newparent_ino {
1193 let new_mtp_parent = match Self::mtp_parent_handle(&inner, newparent_ino) {
1194 Some(Some(h)) => h,
1195 Some(None) => ObjectHandle::ROOT,
1196 None => {
1197 reply.error(Errno::ENOTDIR);
1198 return;
1199 }
1200 };
1201
1202 if let Err(e) = self.rt.block_on(inner.storages[storage_idx].move_object(
1203 handle,
1204 new_mtp_parent,
1205 None,
1206 )) {
1207 error!("MTP move failed: {e}");
1208 reply.error(Errno::EIO);
1209 return;
1210 }
1211 }
1212
1213 inner
1214 .inodes
1215 .rename(child_ino, newparent_ino, newname_str.to_string());
1216 reply.ok();
1217 }
1218
1219 fn setattr(
1220 &self,
1221 _req: &Request,
1222 ino: INodeNo,
1223 _mode: Option<u32>,
1224 _uid: Option<u32>,
1225 _gid: Option<u32>,
1226 size: Option<u64>,
1227 _atime: Option<TimeOrNow>,
1228 _mtime: Option<TimeOrNow>,
1229 _ctime: Option<SystemTime>,
1230 fh: Option<FileHandle>,
1231 _crtime: Option<SystemTime>,
1232 _chgtime: Option<SystemTime>,
1233 _bkuptime: Option<SystemTime>,
1234 _flags: Option<BsdFileFlags>,
1235 reply: ReplyAttr,
1236 ) {
1237 if let Some(new_size) = size {
1238 if self.read_only {
1239 reply.error(Errno::EROFS);
1240 return;
1241 }
1242
1243 if let Some(fh) = fh {
1244 let fh_val = fh.0;
1245 let mut inner = self.inner.lock().unwrap();
1246
1247 if !inner.write_buf.is_open(fh_val) {
1248 let original_size = inner.inodes.get(ino.0).map(|e| e.size).unwrap_or(0);
1249 if let Err(e) = inner.write_buf.open(fh_val, ino.0, original_size) {
1250 error!("Failed to open write buffer: {e}");
1251 reply.error(Errno::EIO);
1252 return;
1253 }
1254 }
1255
1256 if new_size == 0 {
1257 inner.write_buf.close(fh_val);
1258 if let Err(e) = inner.write_buf.open(fh_val, ino.0, 0) {
1259 error!("Failed to open write buffer: {e}");
1260 reply.error(Errno::EIO);
1261 return;
1262 }
1263 }
1264 }
1265 }
1266
1267 let inner = self.inner.lock().unwrap();
1268 match inner.inodes.get(ino.0) {
1269 Some(entry) => {
1270 let mut attr = inode_to_file_attr(entry);
1271 if let Some(new_size) = size {
1272 attr.size = new_size;
1273 attr.blocks = new_size.div_ceil(512);
1274 }
1275 reply.attr(&TTL, &attr);
1276 }
1277 None => {
1278 reply.error(Errno::ENOENT);
1279 }
1280 }
1281 }
1282
1283 fn statfs(&self, _req: &Request, _ino: INodeNo, reply: ReplyStatfs) {
1284 let inner = self.inner.lock().unwrap();
1285 let block_size: u64 = 4096;
1286
1287 let mut total_bytes: u64 = 0;
1288 let mut free_bytes: u64 = 0;
1289 for storage in &inner.storages {
1290 total_bytes = total_bytes.saturating_add(storage.info().max_capacity);
1291 free_bytes = free_bytes.saturating_add(storage.info().free_space_bytes);
1292 }
1293
1294 let blocks = total_bytes / block_size;
1295 let bfree = free_bytes / block_size;
1296
1297 reply.statfs(blocks, bfree, bfree, 0, 0, block_size as u32, 255, 0);
1298 }
1299
1300 fn opendir(&self, _req: &Request, ino: INodeNo, _flags: OpenFlags, reply: ReplyOpen) {
1301 let mut inner = self.inner.lock().unwrap();
1302 match inner.inodes.get(ino.0) {
1303 Some(entry) if entry.is_dir() => {
1304 let fh = self.alloc_fh();
1305 inner.dirs_loaded.remove(&ino.0);
1306 reply.opened(FileHandle(fh), FopenFlags::empty());
1307 }
1308 Some(_) => {
1309 reply.error(Errno::ENOTDIR);
1310 }
1311 None => {
1312 reply.error(Errno::ENOENT);
1313 }
1314 }
1315 }
1316
1317 fn releasedir(
1318 &self,
1319 _req: &Request,
1320 _ino: INodeNo,
1321 _fh: FileHandle,
1322 _flags: OpenFlags,
1323 reply: ReplyEmpty,
1324 ) {
1325 reply.ok();
1326 }
1327}