Skip to main content

mtp_mount/
fs.rs

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