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
71fn 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
83fn 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
106struct 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
116pub struct MtpFs {
118 rt: tokio::runtime::Handle,
119 device: Mutex<MtpDevice>,
120 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 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 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 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 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 #[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 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 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 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 #[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 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, }
459 }
460 }
461
462 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 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 if let Some(parent_ino) = inner.inodes.find_parent_by_handle(*handle) {
491 inner.dirs_loaded.remove(&parent_ino);
492 }
493 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 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 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 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)] 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 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 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}