adbdav — điện thoại Android thành ổ đĩa trong Finder, khỏi cần kext
🌿 buddingupdated 2026-10-17#macos#android#adb#webdav#go#hieu-nang
Mọi con số dưới đây đều đo trên máy thật, và cái benchmark sinh ra chúng nằm ngay trong repo.

Tóm tắt
- Là gì:
adbdavdựng/sdcardcủa điện thoại thành WebDAV trên loopback rồi đưa cái URL đó cho/sbin/mount_webdav— thứ macOS sẵn có. Điện thoại hiện ra như ổ đĩa thường: Finder,ls,rsync, app nào cũng dùng được. - Vì sao có nó: macOS không có MTP, còn macFUSE là một kernel extension.
- Cái giá: không kext, không khởi động lại, không cài app vào điện thoại, không đi qua mạng. Mặc định chỉ đọc.
- Mã nguồn: olragon/adbdav (MIT, viết bằng Go).
Finder ──webdav──> 127.0.0.1 (adbdav) ──adb──> máy /sdcard
Vấn đề
Cắm điện thoại Android vào Mac, và chẳng có đường chính thức nào để xem file trong đó.
macOS không có MTP. Không hỗ trợ sẵn, còn Android File Transfer của Google thì Google khai tử rồi. Trên ROM máy mình, bật chế độ USB sang MTP thì vài giây sau hệ thống tự gạt về. Chẳng có gì để sửa — đường đó đóng, thế thôi.
macFUSE thì chạy, nhưng nó là kext. Trên Apple Silicon, nạp được nó nghĩa là phải vào Recovery rồi hạ xuống Reduced Security. Máy mình đang chạy 26 container; "khởi động lại hai lần thôi mà" chẳng nhẹ nhàng chút nào, mà trên máy do công ty quản lý thì có khi còn chẳng được phép.
Mấy app đám mây thì đòi cài app vào điện thoại, đòi tài khoản, rồi đẩy file vòng qua server của người khác — chỉ để chuyển một file giữa hai thiết bị nằm cùng một mặt bàn.
Trong khi đó adb nằm ngay đấy, đã cấp quyền rồi, đang tải byte qua lại ngon lành.
Cách giải
macOS có sẵn một client WebDAV trong hệ thống. Nó chạy ở userspace. Không cần extension, không cần khởi động lại. Vậy thì: nói WebDAV trên loopback, dịch sang adb, rồi để macOS tự mount.
adbdav mount # mount vào ~/Android, chỉ đọc; Ctrl-C là gỡ

Mặc định là chỉ đọc, và chặn ở hai lớp: server từ chối method, còn ổ đĩa thì mount với -o ro nên nhân hệ điều hành từ chối trước. Chính lớp thứ hai làm Finder mờ hẳn nút sửa đi, thay vì để bạn kéo thả nửa chừng rồi mới báo lỗi.
Nó tốn bao nhiêu
Chạy lại bằng ./bench/bench.sh — chỉ đọc, mỗi phép 5 lần, lấy trung vị.
| Thao tác | Đo được | |---|---| | Liệt kê thư mục 76 mục, ngay trên máy | 0.142s | | ↳ PROPFIND Depth: 1, nguội (server mới khởi động) | 1.253s | | ↳ PROPFIND Depth: 1, nóng | 0.001s | | ↳ PROPFIND Depth: 1, -cache 0 | 18.070s | | Đọc 77 MB qua ổ đã mount | 6.600s (11.6 MB/s) | | ↳ cũng file đó, chạy adb exec-out cat | 11.8 MB/s |
Hai điều rút ra.
Lớp WebDAV không phải chỗ tốn; adb mới là. Đọc nguyên file chạy đúng bằng tốc độ adb exec-out cat. Trong cầu nối này chẳng còn gì đáng gọt nữa.
Cái làm điện thoại có cảm giác chậm là độ trễ, không phải băng thông. Một lượt adb tốn hàng trăm mili-giây, nên thiết kế nào gọi một lượt cho mỗi file là biến một thư mục bình thường thành nửa phút đứng hình. Dòng 18 giây chính là nó: vẫn code ấy, tắt hết tối ưu đi.
Ba lượt gọi đáng xoá
Cái nào cũng đo được, và cái nào giờ cũng có test giữ.
webdav.walkFS gọi Stat lại từng con sau khi Readdir. Thư mục 76 mục tốn thêm 76 lượt — PROPFIND mất 5.81s trong khi máy trả lời đúng câu hỏi đó trong 0.06s. Giờ mỗi lần liệt kê sẽ nạp sẵn cache stat cho từng con.
webdav trả lời getcontenttype bằng cách mở file đọc 512 byte mỗi khi không nhận ra đuôi file. 60 file không đuôi tốn 3.51s mỗi lần PROPFIND. Cài cái hook ContentType của webdav vào: còn 0.19s, và không đọc byte nào từ máy.
Spotlight với Quick Look thì mỗi đứa đòi đọc sạch cả điện thoại. macOS dò ở gốc ổ đĩa ba file .metadata_never_index, .ql_disablethumbnails và .ql_disablecache. Trả về cả ba dưới dạng file rỗng chỉ tồn tại trong server — không hề có trên máy — thì thư mục ảnh từ chỗ "Finder treo" xuống còn đúng không lượt GET nguyên file nào.
Hai thứ ngốn của mình cả ngày
adb exec-out luôn thoát với mã 0, lệnh trên máy làm gì mặc kệ, mà còn trộn luôn stderr của máy vào stdout. adb shell thì có trả mã thật, nhưng nó cấp một pty và pty làm hỏng dữ liệu nhị phân. Thế nên mọi lệnh đều phải tự mang mã trạng thái về trong luồng dữ liệu, còn lệnh đọc nhị phân thì gạt stderr đi. Làm sai chỗ này là một dòng báo lỗi của máy được phục vụ cho bạn dưới dạng nội dung file, và mọi lần ghi hỏng đều lặng thinh.
/sdcard là symlink, mà find thì không đi vào một symlink truyền làm tham số. Thiếu find -H là -mindepth 1 không khớp gì cả. Triệu chứng rất lạ: ổ mount lên bình thường, gõ đủ đường dẫn thì file nào cũng đọc được, mà thư mục thì rỗng không.
Còn một con bug nữa do chính cái benchmark lôi ra. -cache 0 ghi trong tài liệu là tắt cache, nhưng session.New lại lẳng lặng thay TTL chưa đặt bằng mặc định 2 giây — nên nó chưa từng tắt. Mình bắt được chỉ vì lần chạy đầu báo PROPFIND 0.9 mili-giây, trong khi một lượt đi về tới máy mất 1.4 giây. Một benchmark báo ra con số bất khả thi là đang báo một con bug, nằm ngay trong chính nó — và đáng dừng lại tìm xem bug nằm ở đâu. Nửa còn lại của bài học: phải kiểm mã HTTP trước khi bấm giờ bất cứ thứ gì, không thì một cú từ chối kết nối sẽ được đọc thành tốc độ dưới mili-giây.
Giới hạn
- Tốc độ là tốc độ của
adb— ở đây ~11.7 MB/s. Đủ cho ảnh và video; đừng mang ra làm ổ để biên dịch. - Client WebDAV của macOS chép nguyên file về máy khi bạn mở, nên video vài GB là phải chờ hết.
- Metadata cache mặc định 5 giây, vì Finder bắn PROPFIND thành từng chùm. Sửa gì bên điện thoại thì chờ ngần ấy mới thấy.
- Upload không giữ
mtime; quyền truy cập là bịa ra cho hợp lệ. - Hai lần, giữa lúc benchmark vắt kiệt
adbsong song, macOS tự gỡ ổ đĩa. adbdav không hề gỡ, màwebdavfscũng chẳng ghi log gì. Mình chưa tái hiện được có chủ đích, nên ghi vào mục giới hạn chứ không dám nói đã sửa.
Mã nguồn
github.com/olragon/adbdav — giấy phép MIT. make check chạy được cả bộ test mà không cần cắm điện thoại.