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::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::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 inner: Mutex<Inner>,
121 next_fh: AtomicU64,
122 read_only: bool,
123}
124
125impl MtpFs {
126 pub fn new(device: MtpDevice, read_only: bool, rt: tokio::runtime::Handle) -> Self {
127 Self {
128 rt,
129 device: Mutex::new(device),
130 inner: Mutex::new(Inner {
131 storages: Vec::new(),
132 inodes: InodeTable::new(),
133 write_buf: WriteBuffer::new(),
134 read_cache: HashMap::new(),
135 dirs_loaded: HashMap::new(),
136 fh_to_inode: HashMap::new(),
137 }),
138 next_fh: AtomicU64::new(1),
139 read_only,
140 }
141 }
142
143 fn alloc_fh(&self) -> u64 {
144 self.next_fh.fetch_add(1, Ordering::Relaxed)
145 }
146
147 fn find_storage_index(inner: &Inner, inode: u64) -> Option<usize> {
149 let mut current = inode;
150 loop {
151 let entry = inner.inodes.get(current)?;
152 if let InodeKind::Storage { storage_id } = &entry.kind {
153 return inner.storages.iter().position(|s| s.id() == *storage_id);
154 }
155 if current == entry.parent {
156 return None;
157 }
158 current = entry.parent;
159 }
160 }
161
162 fn mtp_parent_handle(inner: &Inner, inode: u64) -> Option<Option<ObjectHandle>> {
164 let entry = inner.inodes.get(inode)?;
165 match &entry.kind {
166 InodeKind::Storage { .. } => Some(None),
167 InodeKind::Directory { handle } => Some(Some(*handle)),
168 _ => None,
169 }
170 }
171
172 fn load_dir(&self, inner: &mut Inner, parent_inode: u64) {
174 if inner.dirs_loaded.get(&parent_inode) == Some(&true) {
175 return;
176 }
177
178 if parent_inode == FUSE_ROOT_INODE {
179 inner.dirs_loaded.insert(parent_inode, true);
180 return;
181 }
182
183 let mtp_parent = match Self::mtp_parent_handle(inner, parent_inode) {
184 Some(p) => p,
185 None => return,
186 };
187
188 let storage_idx = match Self::find_storage_index(inner, parent_inode) {
189 Some(i) => i,
190 None => return,
191 };
192
193 let objects = match self
194 .rt
195 .block_on(inner.storages[storage_idx].list_objects(mtp_parent))
196 {
197 Ok(objs) => objs,
198 Err(e) => {
199 error!("Failed to list MTP objects: {e}");
200 return;
201 }
202 };
203
204 inner.inodes.clear_children(parent_inode);
205
206 for obj in objects {
207 let mtime = obj
208 .modified
209 .as_ref()
210 .map(mtp_datetime_to_system_time)
211 .unwrap_or(UNIX_EPOCH);
212 let is_folder = obj.is_folder();
213 inner.inodes.add_object(
214 parent_inode,
215 obj.handle,
216 obj.filename,
217 is_folder,
218 obj.size,
219 mtime,
220 );
221 }
222
223 inner.dirs_loaded.insert(parent_inode, true);
224 }
225
226 fn flush_to_mtp(&self, inner: &mut Inner, fh: u64) {
232 let buf = match inner.write_buf.close(fh) {
233 Some(b) => b,
234 None => return,
235 };
236
237 if !buf.is_dirty() {
238 return;
239 }
240
241 let inode = buf.inode;
242 let mut file = buf.into_file();
243 if let Err(e) = file.seek(SeekFrom::Start(0)) {
244 error!("Flush: failed to rewind temp file: {e}");
245 return;
246 }
247 let file_len = file.seek(SeekFrom::End(0)).unwrap_or(0);
248 if let Err(e) = file.seek(SeekFrom::Start(0)) {
249 error!("Flush: failed to rewind temp file: {e}");
250 return;
251 }
252 let entry = match inner.inodes.get(inode) {
253 Some(e) => e.clone(),
254 None => {
255 error!("Flush: inode {inode} not found");
256 return;
257 }
258 };
259
260 let handle = match &entry.kind {
261 InodeKind::File { handle } => *handle,
262 _ => {
263 error!("Flush: inode {inode} is not a file");
264 return;
265 }
266 };
267
268 let storage_idx = match Self::find_storage_index(inner, inode) {
269 Some(i) => i,
270 None => {
271 error!("Flush: no storage for inode {inode}");
272 return;
273 }
274 };
275
276 let parent_handle = inner.inodes.get(entry.parent).and_then(|p| match &p.kind {
277 InodeKind::Storage { .. } => None,
278 InodeKind::Directory { handle } => Some(*handle),
279 _ => None,
280 });
281
282 let supports_rename = self.device.lock().unwrap().supports_rename();
283
284 if supports_rename {
285 self.flush_safe(
286 inner,
287 inode,
288 handle,
289 storage_idx,
290 parent_handle,
291 &entry,
292 file_len,
293 file,
294 );
295 } else {
296 warn!(
297 "Flush: device does not support rename, using delete-then-upload \
298 (data loss possible if upload fails)"
299 );
300 self.flush_unsafe(
301 inner,
302 inode,
303 handle,
304 storage_idx,
305 parent_handle,
306 &entry,
307 file_len,
308 file,
309 );
310 }
311 }
312
313 #[allow(clippy::too_many_arguments)]
315 fn flush_safe(
316 &self,
317 inner: &mut Inner,
318 inode: u64,
319 old_handle: ObjectHandle,
320 storage_idx: usize,
321 parent_handle: Option<ObjectHandle>,
322 entry: &InodeEntry,
323 size: u64,
324 file: std::fs::File,
325 ) {
326 let storage = &inner.storages[storage_idx];
327 let temp_name = format!(".~tmp~{}", entry.name);
328
329 let info = NewObjectInfo::file(&temp_name, size);
331 let stream = file_stream(file);
332 let new_handle = match self
333 .rt
334 .block_on(storage.upload(parent_handle, info, stream))
335 {
336 Ok(h) => h,
337 Err(e) => {
338 error!("Flush: upload failed (original file untouched): {e}");
339 return;
340 }
341 };
342
343 if let Err(e) = self.rt.block_on(storage.delete(old_handle)) {
345 error!("Flush: failed to delete old object (new data saved as '{temp_name}'): {e}");
346 if let Some(e) = inner.inodes.get_mut(inode) {
347 e.kind = InodeKind::File { handle: new_handle };
348 e.name = temp_name;
349 e.size = size;
350 e.mtime = SystemTime::now();
351 }
352 return;
353 }
354
355 if let Err(e) = self.rt.block_on(storage.rename(new_handle, &entry.name)) {
357 warn!(
358 "Flush: rename from '{temp_name}' to '{}' failed: {e}",
359 entry.name
360 );
361 if let Some(e) = inner.inodes.get_mut(inode) {
362 e.kind = InodeKind::File { handle: new_handle };
363 e.name = temp_name;
364 e.size = size;
365 e.mtime = SystemTime::now();
366 }
367 return;
368 }
369
370 if let Some(e) = inner.inodes.get_mut(inode) {
371 e.kind = InodeKind::File { handle: new_handle };
372 e.size = size;
373 e.mtime = SystemTime::now();
374 }
375 }
376
377 #[allow(clippy::too_many_arguments)]
379 fn flush_unsafe(
380 &self,
381 inner: &mut Inner,
382 inode: u64,
383 old_handle: ObjectHandle,
384 storage_idx: usize,
385 parent_handle: Option<ObjectHandle>,
386 entry: &InodeEntry,
387 size: u64,
388 file: std::fs::File,
389 ) {
390 let storage = &inner.storages[storage_idx];
391
392 if let Err(e) = self.rt.block_on(storage.delete(old_handle)) {
393 error!("Flush: failed to delete old object: {e}");
394 return;
395 }
396
397 let info = NewObjectInfo::file(&entry.name, size);
398 let stream = file_stream(file);
399
400 match self
401 .rt
402 .block_on(storage.upload(parent_handle, info, stream))
403 {
404 Ok(new_handle) => {
405 if let Some(e) = inner.inodes.get_mut(inode) {
406 e.kind = InodeKind::File { handle: new_handle };
407 e.size = size;
408 e.mtime = SystemTime::now();
409 }
410 }
411 Err(e) => {
412 error!("Flush: upload failed after delete (data lost): {e}");
413 }
414 }
415 }
416
417 pub fn mount_options(&self) -> Vec<MountOption> {
418 let mut opts = vec![
419 MountOption::FSName("mtp-mount".to_string()),
420 MountOption::Subtype("mtp".to_string()),
421 MountOption::DefaultPermissions,
422 MountOption::NoDev,
423 MountOption::NoSuid,
424 ];
425 if self.read_only {
426 opts.push(MountOption::RO);
427 } else {
428 opts.push(MountOption::RW);
429 }
430 opts
431 }
432}
433
434impl Filesystem for MtpFs {
435 fn init(&mut self, _req: &Request, _config: &mut KernelConfig) -> io::Result<()> {
436 let storages = self
437 .rt
438 .block_on(self.device.lock().unwrap().storages())
439 .map_err(|e| io::Error::other(e.to_string()))?;
440
441 let mut inner = self.inner.lock().unwrap();
442 for storage in &storages {
443 let name = if storage.info().description.is_empty() {
444 format!("Storage_{}", storage.id().0)
445 } else {
446 storage.info().description.clone()
447 };
448 inner.inodes.add_storage(storage.id(), name);
449 }
450 inner.dirs_loaded.insert(FUSE_ROOT_INODE, true);
451 inner.storages = storages;
452
453 debug!("MtpFs initialized with {} storages", inner.storages.len());
454 Ok(())
455 }
456
457 fn lookup(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEntry) {
458 let parent_ino = parent.0;
459 let name_str = match name.to_str() {
460 Some(s) => s,
461 None => {
462 reply.error(Errno::ENOENT);
463 return;
464 }
465 };
466
467 let mut inner = self.inner.lock().unwrap();
468 self.load_dir(&mut inner, parent_ino);
469
470 match inner.inodes.lookup(parent_ino, name_str) {
471 Some(ino) => {
472 let entry = inner.inodes.get(ino).unwrap();
473 let attr = inode_to_file_attr(entry);
474 reply.entry(&TTL, &attr, Generation(0));
475 }
476 None => {
477 reply.error(Errno::ENOENT);
478 }
479 }
480 }
481
482 fn getattr(&self, _req: &Request, ino: INodeNo, _fh: Option<FileHandle>, reply: ReplyAttr) {
483 let inner = self.inner.lock().unwrap();
484 match inner.inodes.get(ino.0) {
485 Some(entry) => {
486 let mut attr = inode_to_file_attr(entry);
487 for (&fh, &inode) in &inner.fh_to_inode {
488 if inode == ino.0 {
489 if let Some(size) = inner.write_buf.size(fh) {
490 attr.size = size;
491 attr.blocks = size.div_ceil(512);
492 }
493 break;
494 }
495 }
496 reply.attr(&TTL, &attr);
497 }
498 None => {
499 reply.error(Errno::ENOENT);
500 }
501 }
502 }
503
504 fn readdir(
505 &self,
506 _req: &Request,
507 ino: INodeNo,
508 _fh: FileHandle,
509 offset: u64,
510 mut reply: ReplyDirectory,
511 ) {
512 let ino_val = ino.0;
513
514 let mut inner = self.inner.lock().unwrap();
515 self.load_dir(&mut inner, ino_val);
516
517 let parent_ino = inner
518 .inodes
519 .get(ino_val)
520 .map(|e| e.parent)
521 .unwrap_or(FUSE_ROOT_INODE);
522
523 let mut entries: Vec<(u64, INodeNo, FileType, String)> = vec![
524 (1, INodeNo(ino_val), FileType::Directory, ".".to_string()),
525 (
526 2,
527 INodeNo(parent_ino),
528 FileType::Directory,
529 "..".to_string(),
530 ),
531 ];
532
533 let children = inner.inodes.children(ino_val);
534 for (i, child_ino) in children.iter().enumerate() {
535 if let Some(child) = inner.inodes.get(*child_ino) {
536 let kind = if child.is_dir() {
537 FileType::Directory
538 } else {
539 FileType::RegularFile
540 };
541 entries.push((i as u64 + 3, INodeNo(*child_ino), kind, child.name.clone()));
542 }
543 }
544
545 for (i, (off, ino, kind, name)) in entries.iter().enumerate() {
546 if i as u64 >= offset && reply.add(*ino, *off, *kind, name) {
547 break;
548 }
549 }
550 reply.ok();
551 }
552
553 fn open(&self, _req: &Request, ino: INodeNo, _flags: OpenFlags, reply: ReplyOpen) {
554 let mut inner = self.inner.lock().unwrap();
555 match inner.inodes.get(ino.0) {
556 Some(entry) if !entry.is_dir() => {
557 let fh = self.alloc_fh();
558 inner.fh_to_inode.insert(fh, ino.0);
559 reply.opened(FileHandle(fh), FopenFlags::empty());
560 }
561 Some(_) => {
562 reply.error(Errno::EISDIR);
563 }
564 None => {
565 reply.error(Errno::ENOENT);
566 }
567 }
568 }
569
570 #[allow(clippy::map_entry)] fn read(
572 &self,
573 _req: &Request,
574 ino: INodeNo,
575 fh: FileHandle,
576 offset: u64,
577 size: u32,
578 _flags: OpenFlags,
579 _lock_owner: Option<LockOwner>,
580 reply: ReplyData,
581 ) {
582 let fh_val = fh.0;
583 let mut inner = self.inner.lock().unwrap();
584
585 if inner.write_buf.is_open(fh_val) {
587 match inner.write_buf.read(fh_val, offset as i64, size) {
588 Ok(data) => reply.data(&data),
589 Err(e) => {
590 error!("Read from write buffer failed: {e}");
591 reply.error(Errno::EIO);
592 }
593 }
594 return;
595 }
596
597 if !inner.read_cache.contains_key(&fh_val) {
599 let entry = match inner.inodes.get(ino.0) {
600 Some(e) => e,
601 None => {
602 reply.error(Errno::ENOENT);
603 return;
604 }
605 };
606
607 let handle = match &entry.kind {
608 InodeKind::File { handle } => *handle,
609 _ => {
610 reply.error(Errno::EISDIR);
611 return;
612 }
613 };
614
615 let storage_idx = match Self::find_storage_index(&inner, ino.0) {
616 Some(i) => i,
617 None => {
618 reply.error(Errno::EIO);
619 return;
620 }
621 };
622
623 let mut download = match self
624 .rt
625 .block_on(inner.storages[storage_idx].download_stream(handle))
626 {
627 Ok(d) => d,
628 Err(e) => {
629 error!("MTP download_stream failed: {e}");
630 reply.error(Errno::EIO);
631 return;
632 }
633 };
634
635 let mut file = match tempfile::tempfile() {
636 Ok(f) => f,
637 Err(e) => {
638 error!("Failed to create temp file: {e}");
639 reply.error(Errno::EIO);
640 return;
641 }
642 };
643
644 let write_ok = self.rt.block_on(async {
645 while let Some(chunk) = download.next_chunk().await {
646 match chunk {
647 Ok(bytes) => {
648 if let Err(e) = file.write_all(&bytes) {
649 error!("Failed to write to temp file: {e}");
650 return false;
651 }
652 }
653 Err(e) => {
654 error!("MTP download chunk failed: {e}");
655 return false;
656 }
657 }
658 }
659 true
660 });
661
662 if !write_ok {
663 reply.error(Errno::EIO);
664 return;
665 }
666
667 inner.read_cache.insert(fh_val, file);
668 }
669
670 let file = inner.read_cache.get_mut(&fh_val).unwrap();
671 let file_size = file.seek(SeekFrom::End(0)).unwrap_or(0);
672 if offset >= file_size {
673 reply.data(&[]);
674 } else {
675 let read_len = (size as u64).min(file_size - offset) as usize;
676 let mut buf = vec![0u8; read_len];
677 if let Err(e) = file.seek(SeekFrom::Start(offset)) {
678 error!("Seek failed: {e}");
679 reply.error(Errno::EIO);
680 return;
681 }
682 match file.read_exact(&mut buf) {
683 Ok(()) => reply.data(&buf),
684 Err(e) => {
685 error!("Read from temp file failed: {e}");
686 reply.error(Errno::EIO);
687 }
688 }
689 }
690 }
691
692 fn release(
693 &self,
694 _req: &Request,
695 _ino: INodeNo,
696 fh: FileHandle,
697 _flags: OpenFlags,
698 _lock_owner: Option<LockOwner>,
699 _flush: bool,
700 reply: ReplyEmpty,
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) {
706 self.flush_to_mtp(&mut inner, fh_val);
707 }
708
709 inner.read_cache.remove(&fh_val);
710 inner.fh_to_inode.remove(&fh_val);
711 reply.ok();
712 }
713
714 fn write(
715 &self,
716 _req: &Request,
717 ino: INodeNo,
718 fh: FileHandle,
719 offset: u64,
720 data: &[u8],
721 _write_flags: WriteFlags,
722 _flags: OpenFlags,
723 _lock_owner: Option<LockOwner>,
724 reply: ReplyWrite,
725 ) {
726 if self.read_only {
727 reply.error(Errno::EROFS);
728 return;
729 }
730
731 let fh_val = fh.0;
732 let mut inner = self.inner.lock().unwrap();
733
734 if !inner.write_buf.is_open(fh_val) {
735 let original_size = inner.inodes.get(ino.0).map(|e| e.size).unwrap_or(0);
736 if let Err(e) = inner.write_buf.open(fh_val, ino.0, original_size) {
737 error!("Failed to open write buffer: {e}");
738 reply.error(Errno::EIO);
739 return;
740 }
741 }
742
743 match inner.write_buf.write(fh_val, offset as i64, data) {
744 Ok(written) => reply.written(written),
745 Err(e) => {
746 error!("Write failed: {e}");
747 reply.error(Errno::EIO);
748 }
749 }
750 }
751
752 fn create(
753 &self,
754 _req: &Request,
755 parent: INodeNo,
756 name: &OsStr,
757 _mode: u32,
758 _umask: u32,
759 _flags: i32,
760 reply: ReplyCreate,
761 ) {
762 if self.read_only {
763 reply.error(Errno::EROFS);
764 return;
765 }
766
767 let name_str = match name.to_str() {
768 Some(s) => s,
769 None => {
770 reply.error(Errno::EINVAL);
771 return;
772 }
773 };
774
775 let parent_ino = parent.0;
776 let mut inner = self.inner.lock().unwrap();
777
778 let storage_idx = match Self::find_storage_index(&inner, parent_ino) {
779 Some(i) => i,
780 None => {
781 reply.error(Errno::EIO);
782 return;
783 }
784 };
785
786 let mtp_parent = match Self::mtp_parent_handle(&inner, parent_ino) {
787 Some(p) => p,
788 None => {
789 reply.error(Errno::ENOTDIR);
790 return;
791 }
792 };
793
794 let info = NewObjectInfo::file(name_str, 0);
795 let stream = bytes_stream(Vec::new());
796
797 let handle = match self
798 .rt
799 .block_on(inner.storages[storage_idx].upload(mtp_parent, info, stream))
800 {
801 Ok(h) => h,
802 Err(e) => {
803 error!("MTP create failed: {e}");
804 reply.error(Errno::EIO);
805 return;
806 }
807 };
808
809 let now = SystemTime::now();
810 let ino = inner
811 .inodes
812 .add_object(parent_ino, handle, name_str.to_string(), false, 0, now);
813
814 let fh = self.alloc_fh();
815 inner.fh_to_inode.insert(fh, ino);
816 if let Err(e) = inner.write_buf.open(fh, ino, 0) {
817 error!("Failed to open write buffer: {e}");
818 reply.error(Errno::EIO);
819 return;
820 }
821
822 let entry = inner.inodes.get(ino).unwrap();
823 let attr = inode_to_file_attr(entry);
824 reply.created(
825 &TTL,
826 &attr,
827 Generation(0),
828 FileHandle(fh),
829 FopenFlags::empty(),
830 );
831 }
832
833 fn mkdir(
834 &self,
835 _req: &Request,
836 parent: INodeNo,
837 name: &OsStr,
838 _mode: u32,
839 _umask: u32,
840 reply: ReplyEntry,
841 ) {
842 if self.read_only {
843 reply.error(Errno::EROFS);
844 return;
845 }
846
847 let name_str = match name.to_str() {
848 Some(s) => s,
849 None => {
850 reply.error(Errno::EINVAL);
851 return;
852 }
853 };
854
855 let parent_ino = parent.0;
856 let mut inner = self.inner.lock().unwrap();
857
858 let storage_idx = match Self::find_storage_index(&inner, parent_ino) {
859 Some(i) => i,
860 None => {
861 reply.error(Errno::EIO);
862 return;
863 }
864 };
865
866 let mtp_parent = match Self::mtp_parent_handle(&inner, parent_ino) {
867 Some(p) => p,
868 None => {
869 reply.error(Errno::ENOTDIR);
870 return;
871 }
872 };
873
874 let handle = match self
875 .rt
876 .block_on(inner.storages[storage_idx].create_folder(mtp_parent, name_str))
877 {
878 Ok(h) => h,
879 Err(e) => {
880 error!("MTP mkdir failed: {e}");
881 reply.error(Errno::EIO);
882 return;
883 }
884 };
885
886 let now = SystemTime::now();
887 let ino = inner
888 .inodes
889 .add_object(parent_ino, handle, name_str.to_string(), true, 0, now);
890
891 let entry = inner.inodes.get(ino).unwrap();
892 let attr = inode_to_file_attr(entry);
893 reply.entry(&TTL, &attr, Generation(0));
894 }
895
896 fn unlink(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) {
897 if self.read_only {
898 reply.error(Errno::EROFS);
899 return;
900 }
901
902 let name_str = match name.to_str() {
903 Some(s) => s,
904 None => {
905 reply.error(Errno::ENOENT);
906 return;
907 }
908 };
909
910 let parent_ino = parent.0;
911 let mut inner = self.inner.lock().unwrap();
912
913 let child_ino = match inner.inodes.lookup(parent_ino, name_str) {
914 Some(i) => i,
915 None => {
916 reply.error(Errno::ENOENT);
917 return;
918 }
919 };
920
921 let handle = match inner.inodes.get(child_ino).and_then(|e| match &e.kind {
922 InodeKind::File { handle } => Some(*handle),
923 _ => None,
924 }) {
925 Some(h) => h,
926 None => {
927 reply.error(Errno::EISDIR);
928 return;
929 }
930 };
931
932 let storage_idx = match Self::find_storage_index(&inner, child_ino) {
933 Some(i) => i,
934 None => {
935 reply.error(Errno::EIO);
936 return;
937 }
938 };
939
940 if let Err(e) = self.rt.block_on(inner.storages[storage_idx].delete(handle)) {
941 error!("MTP delete failed: {e}");
942 reply.error(Errno::EIO);
943 return;
944 }
945
946 inner.inodes.remove(child_ino);
947 reply.ok();
948 }
949
950 fn rmdir(&self, _req: &Request, parent: INodeNo, name: &OsStr, reply: ReplyEmpty) {
951 if self.read_only {
952 reply.error(Errno::EROFS);
953 return;
954 }
955
956 let name_str = match name.to_str() {
957 Some(s) => s,
958 None => {
959 reply.error(Errno::ENOENT);
960 return;
961 }
962 };
963
964 let parent_ino = parent.0;
965 let mut inner = self.inner.lock().unwrap();
966
967 let child_ino = match inner.inodes.lookup(parent_ino, name_str) {
968 Some(i) => i,
969 None => {
970 reply.error(Errno::ENOENT);
971 return;
972 }
973 };
974
975 let handle = match inner.inodes.get(child_ino).and_then(|e| match &e.kind {
976 InodeKind::Directory { handle } => Some(*handle),
977 _ => None,
978 }) {
979 Some(h) => h,
980 None => {
981 reply.error(Errno::ENOTDIR);
982 return;
983 }
984 };
985
986 let storage_idx = match Self::find_storage_index(&inner, child_ino) {
987 Some(i) => i,
988 None => {
989 reply.error(Errno::EIO);
990 return;
991 }
992 };
993
994 if let Err(e) = self.rt.block_on(inner.storages[storage_idx].delete(handle)) {
995 error!("MTP rmdir failed: {e}");
996 reply.error(Errno::EIO);
997 return;
998 }
999
1000 inner.inodes.remove(child_ino);
1001 reply.ok();
1002 }
1003
1004 fn rename(
1005 &self,
1006 _req: &Request,
1007 parent: INodeNo,
1008 name: &OsStr,
1009 newparent: INodeNo,
1010 newname: &OsStr,
1011 _flags: RenameFlags,
1012 reply: ReplyEmpty,
1013 ) {
1014 if self.read_only {
1015 reply.error(Errno::EROFS);
1016 return;
1017 }
1018
1019 let name_str = match name.to_str() {
1020 Some(s) => s,
1021 None => {
1022 reply.error(Errno::ENOENT);
1023 return;
1024 }
1025 };
1026 let newname_str = match newname.to_str() {
1027 Some(s) => s,
1028 None => {
1029 reply.error(Errno::EINVAL);
1030 return;
1031 }
1032 };
1033
1034 let parent_ino = parent.0;
1035 let newparent_ino = newparent.0;
1036 let mut inner = self.inner.lock().unwrap();
1037
1038 let child_ino = match inner.inodes.lookup(parent_ino, name_str) {
1039 Some(i) => i,
1040 None => {
1041 reply.error(Errno::ENOENT);
1042 return;
1043 }
1044 };
1045
1046 let handle = match inner.inodes.get(child_ino).and_then(|e| match &e.kind {
1047 InodeKind::File { handle } | InodeKind::Directory { handle } => Some(*handle),
1048 _ => None,
1049 }) {
1050 Some(h) => h,
1051 None => {
1052 reply.error(Errno::EINVAL);
1053 return;
1054 }
1055 };
1056
1057 let storage_idx = match Self::find_storage_index(&inner, child_ino) {
1058 Some(i) => i,
1059 None => {
1060 reply.error(Errno::EIO);
1061 return;
1062 }
1063 };
1064
1065 if name_str != newname_str {
1066 if let Err(e) = self
1067 .rt
1068 .block_on(inner.storages[storage_idx].rename(handle, newname_str))
1069 {
1070 error!("MTP rename failed: {e}");
1071 reply.error(Errno::EIO);
1072 return;
1073 }
1074 }
1075
1076 if parent_ino != newparent_ino {
1077 let new_mtp_parent = match Self::mtp_parent_handle(&inner, newparent_ino) {
1078 Some(Some(h)) => h,
1079 Some(None) => ObjectHandle::ROOT,
1080 None => {
1081 reply.error(Errno::ENOTDIR);
1082 return;
1083 }
1084 };
1085
1086 if let Err(e) = self.rt.block_on(inner.storages[storage_idx].move_object(
1087 handle,
1088 new_mtp_parent,
1089 None,
1090 )) {
1091 error!("MTP move failed: {e}");
1092 reply.error(Errno::EIO);
1093 return;
1094 }
1095 }
1096
1097 inner
1098 .inodes
1099 .rename(child_ino, newparent_ino, newname_str.to_string());
1100 reply.ok();
1101 }
1102
1103 fn setattr(
1104 &self,
1105 _req: &Request,
1106 ino: INodeNo,
1107 _mode: Option<u32>,
1108 _uid: Option<u32>,
1109 _gid: Option<u32>,
1110 size: Option<u64>,
1111 _atime: Option<TimeOrNow>,
1112 _mtime: Option<TimeOrNow>,
1113 _ctime: Option<SystemTime>,
1114 fh: Option<FileHandle>,
1115 _crtime: Option<SystemTime>,
1116 _chgtime: Option<SystemTime>,
1117 _bkuptime: Option<SystemTime>,
1118 _flags: Option<BsdFileFlags>,
1119 reply: ReplyAttr,
1120 ) {
1121 if let Some(new_size) = size {
1122 if self.read_only {
1123 reply.error(Errno::EROFS);
1124 return;
1125 }
1126
1127 if let Some(fh) = fh {
1128 let fh_val = fh.0;
1129 let mut inner = self.inner.lock().unwrap();
1130
1131 if !inner.write_buf.is_open(fh_val) {
1132 let original_size = inner.inodes.get(ino.0).map(|e| e.size).unwrap_or(0);
1133 if let Err(e) = inner.write_buf.open(fh_val, ino.0, original_size) {
1134 error!("Failed to open write buffer: {e}");
1135 reply.error(Errno::EIO);
1136 return;
1137 }
1138 }
1139
1140 if new_size == 0 {
1141 inner.write_buf.close(fh_val);
1142 if let Err(e) = inner.write_buf.open(fh_val, ino.0, 0) {
1143 error!("Failed to open write buffer: {e}");
1144 reply.error(Errno::EIO);
1145 return;
1146 }
1147 }
1148 }
1149 }
1150
1151 let inner = self.inner.lock().unwrap();
1152 match inner.inodes.get(ino.0) {
1153 Some(entry) => {
1154 let mut attr = inode_to_file_attr(entry);
1155 if let Some(new_size) = size {
1156 attr.size = new_size;
1157 attr.blocks = new_size.div_ceil(512);
1158 }
1159 reply.attr(&TTL, &attr);
1160 }
1161 None => {
1162 reply.error(Errno::ENOENT);
1163 }
1164 }
1165 }
1166
1167 fn statfs(&self, _req: &Request, _ino: INodeNo, reply: ReplyStatfs) {
1168 reply.statfs(0, 0, 0, 0, 0, 4096, 255, 0);
1169 }
1170
1171 fn opendir(&self, _req: &Request, ino: INodeNo, _flags: OpenFlags, reply: ReplyOpen) {
1172 let mut inner = self.inner.lock().unwrap();
1173 match inner.inodes.get(ino.0) {
1174 Some(entry) if entry.is_dir() => {
1175 let fh = self.alloc_fh();
1176 inner.dirs_loaded.remove(&ino.0);
1177 reply.opened(FileHandle(fh), FopenFlags::empty());
1178 }
1179 Some(_) => {
1180 reply.error(Errno::ENOTDIR);
1181 }
1182 None => {
1183 reply.error(Errno::ENOENT);
1184 }
1185 }
1186 }
1187
1188 fn releasedir(
1189 &self,
1190 _req: &Request,
1191 _ino: INodeNo,
1192 _fh: FileHandle,
1193 _flags: OpenFlags,
1194 reply: ReplyEmpty,
1195 ) {
1196 reply.ok();
1197 }
1198}