[go: up one dir, main page]

fuse/
lib.rs

1//! FUSE userspace library implementation
2//!
3//! This is an improved rewrite of the FUSE userspace library (lowlevel interface) to fully take
4//! advantage of Rust's architecture. The only thing we rely on in the real libfuse are mount
5//! and unmount calls which are needed to establish a fd to talk to the kernel driver.
6
7#![warn(missing_docs, bad_style, unused, unused_extern_crates, unused_import_braces, unused_qualifications, missing_debug_implementations)]
8
9extern crate libc;
10#[macro_use]
11extern crate log;
12extern crate time;
13extern crate thread_scoped;
14
15use std::convert::AsRef;
16use std::io;
17use std::ffi::OsStr;
18use std::path::Path;
19use libc::{c_int, ENOSYS};
20use time::Timespec;
21
22pub use kernel::FUSE_ROOT_ID;
23pub use kernel::consts;
24pub use reply::{Reply, ReplyEmpty, ReplyData, ReplyEntry, ReplyAttr, ReplyOpen};
25pub use reply::{ReplyWrite, ReplyStatfs, ReplyCreate, ReplyLock, ReplyBmap, ReplyDirectory};
26pub use reply::ReplyXattr;
27#[cfg(target_os = "macos")]
28pub use reply::ReplyXTimes;
29pub use request::Request;
30pub use session::{Session, BackgroundSession};
31
32mod argument;
33mod channel;
34mod kernel;
35mod libfuse;
36mod reply;
37mod request;
38mod session;
39
40/// File types
41#[derive(Clone, Copy, Debug, Hash, PartialEq)]
42pub enum FileType {
43    /// Named pipe (S_IFIFO)
44    NamedPipe,
45    /// Character device (S_IFCHR)
46    CharDevice,
47    /// Block device (S_IFBLK)
48    BlockDevice,
49    /// Directory (S_IFDIR)
50    Directory,
51    /// Regular file (S_IFREG)
52    RegularFile,
53    /// Symbolic link (S_IFLNK)
54    Symlink,
55    /// Unix domain socket (S_IFSOCK)
56    Socket,
57}
58
59/// File attributes
60#[derive(Clone, Copy, Debug)]
61pub struct FileAttr {
62    /// Inode number
63    pub ino: u64,
64    /// Size in bytes
65    pub size: u64,
66    /// Size in blocks
67    pub blocks: u64,
68    /// Time of last access
69    pub atime: Timespec,
70    /// Time of last modification
71    pub mtime: Timespec,
72    /// Time of last change
73    pub ctime: Timespec,
74    /// Time of creation (macOS only)
75    pub crtime: Timespec,
76    /// Kind of file (directory, file, pipe, etc)
77    pub kind: FileType,
78    /// Permissions
79    pub perm: u16,
80    /// Number of hard links
81    pub nlink: u32,
82    /// User id
83    pub uid: u32,
84    /// Group id
85    pub gid: u32,
86    /// Rdev
87    pub rdev: u32,
88    /// Flags (macOS only, see chflags(2))
89    pub flags: u32,
90}
91
92/// Filesystem trait.
93///
94/// This trait must be implemented to provide a userspace filesystem via FUSE.
95/// These methods correspond to fuse_lowlevel_ops in libfuse. Reasonable default
96/// implementations are provided here to get a mountable filesystem that does
97/// nothing.
98pub trait Filesystem {
99    /// Initialize filesystem.
100    /// Called before any other filesystem method.
101    fn init(&mut self, _req: &Request) -> Result<(), c_int> {
102        Ok(())
103    }
104
105    /// Clean up filesystem.
106    /// Called on filesystem exit.
107    fn destroy(&mut self, _req: &Request) {}
108
109    /// Look up a directory entry by name and get its attributes.
110    fn lookup(&mut self, _req: &Request, _parent: u64, _name: &OsStr, reply: ReplyEntry) {
111        reply.error(ENOSYS);
112    }
113
114    /// Forget about an inode.
115    /// The nlookup parameter indicates the number of lookups previously performed on
116    /// this inode. If the filesystem implements inode lifetimes, it is recommended that
117    /// inodes acquire a single reference on each lookup, and lose nlookup references on
118    /// each forget. The filesystem may ignore forget calls, if the inodes don't need to
119    /// have a limited lifetime. On unmount it is not guaranteed, that all referenced
120    /// inodes will receive a forget message.
121    fn forget(&mut self, _req: &Request, _ino: u64, _nlookup: u64) {}
122
123    /// Get file attributes.
124    fn getattr(&mut self, _req: &Request, _ino: u64, reply: ReplyAttr) {
125        reply.error(ENOSYS);
126    }
127
128    /// Set file attributes.
129    fn setattr(&mut self, _req: &Request, _ino: u64, _mode: Option<u32>, _uid: Option<u32>, _gid: Option<u32>, _size: Option<u64>, _atime: Option<Timespec>, _mtime: Option<Timespec>, _fh: Option<u64>, _crtime: Option<Timespec>, _chgtime: Option<Timespec>, _bkuptime: Option<Timespec>, _flags: Option<u32>, reply: ReplyAttr) {
130        reply.error(ENOSYS);
131    }
132
133    /// Read symbolic link.
134    fn readlink(&mut self, _req: &Request, _ino: u64, reply: ReplyData) {
135        reply.error(ENOSYS);
136    }
137
138    /// Create file node.
139    /// Create a regular file, character device, block device, fifo or socket node.
140    fn mknod(&mut self, _req: &Request, _parent: u64, _name: &OsStr, _mode: u32, _rdev: u32, reply: ReplyEntry) {
141        reply.error(ENOSYS);
142    }
143
144    /// Create a directory.
145    fn mkdir(&mut self, _req: &Request, _parent: u64, _name: &OsStr, _mode: u32, reply: ReplyEntry) {
146        reply.error(ENOSYS);
147    }
148
149    /// Remove a file.
150    fn unlink(&mut self, _req: &Request, _parent: u64, _name: &OsStr, reply: ReplyEmpty) {
151        reply.error(ENOSYS);
152    }
153
154    /// Remove a directory.
155    fn rmdir(&mut self, _req: &Request, _parent: u64, _name: &OsStr, reply: ReplyEmpty) {
156        reply.error(ENOSYS);
157    }
158
159    /// Create a symbolic link.
160    fn symlink(&mut self, _req: &Request, _parent: u64, _name: &OsStr, _link: &Path, reply: ReplyEntry) {
161        reply.error(ENOSYS);
162    }
163
164    /// Rename a file.
165    fn rename(&mut self, _req: &Request, _parent: u64, _name: &OsStr, _newparent: u64, _newname: &OsStr, reply: ReplyEmpty) {
166        reply.error(ENOSYS);
167    }
168
169    /// Create a hard link.
170    fn link(&mut self, _req: &Request, _ino: u64, _newparent: u64, _newname: &OsStr, reply: ReplyEntry) {
171        reply.error(ENOSYS);
172    }
173
174    /// Open a file.
175    /// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are
176    /// available in flags. Filesystem may store an arbitrary file handle (pointer, index,
177    /// etc) in fh, and use this in other all other file operations (read, write, flush,
178    /// release, fsync). Filesystem may also implement stateless file I/O and not store
179    /// anything in fh. There are also some flags (direct_io, keep_cache) which the
180    /// filesystem may set, to change the way the file is opened. See fuse_file_info
181    /// structure in <fuse_common.h> for more details.
182    fn open(&mut self, _req: &Request, _ino: u64, _flags: u32, reply: ReplyOpen) {
183        reply.opened(0, 0);
184    }
185
186    /// Read data.
187    /// Read should send exactly the number of bytes requested except on EOF or error,
188    /// otherwise the rest of the data will be substituted with zeroes. An exception to
189    /// this is when the file has been opened in 'direct_io' mode, in which case the
190    /// return value of the read system call will reflect the return value of this
191    /// operation. fh will contain the value set by the open method, or will be undefined
192    /// if the open method didn't set any value.
193    fn read(&mut self, _req: &Request, _ino: u64, _fh: u64, _offset: i64, _size: u32, reply: ReplyData) {
194        reply.error(ENOSYS);
195    }
196
197    /// Write data.
198    /// Write should return exactly the number of bytes requested except on error. An
199    /// exception to this is when the file has been opened in 'direct_io' mode, in
200    /// which case the return value of the write system call will reflect the return
201    /// value of this operation. fh will contain the value set by the open method, or
202    /// will be undefined if the open method didn't set any value.
203    fn write(&mut self, _req: &Request, _ino: u64, _fh: u64, _offset: i64, _data: &[u8], _flags: u32, reply: ReplyWrite) {
204        reply.error(ENOSYS);
205    }
206
207    /// Flush method.
208    /// This is called on each close() of the opened file. Since file descriptors can
209    /// be duplicated (dup, dup2, fork), for one open call there may be many flush
210    /// calls. Filesystems shouldn't assume that flush will always be called after some
211    /// writes, or that if will be called at all. fh will contain the value set by the
212    /// open method, or will be undefined if the open method didn't set any value.
213    /// NOTE: the name of the method is misleading, since (unlike fsync) the filesystem
214    /// is not forced to flush pending writes. One reason to flush data, is if the
215    /// filesystem wants to return write errors. If the filesystem supports file locking
216    /// operations (setlk, getlk) it should remove all locks belonging to 'lock_owner'.
217    fn flush(&mut self, _req: &Request, _ino: u64, _fh: u64, _lock_owner: u64, reply: ReplyEmpty) {
218        reply.error(ENOSYS);
219    }
220
221    /// Release an open file.
222    /// Release is called when there are no more references to an open file: all file
223    /// descriptors are closed and all memory mappings are unmapped. For every open
224    /// call there will be exactly one release call. The filesystem may reply with an
225    /// error, but error values are not returned to close() or munmap() which triggered
226    /// the release. fh will contain the value set by the open method, or will be undefined
227    /// if the open method didn't set any value. flags will contain the same flags as for
228    /// open.
229    fn release(&mut self, _req: &Request, _ino: u64, _fh: u64, _flags: u32, _lock_owner: u64, _flush: bool, reply: ReplyEmpty) {
230        reply.ok();
231    }
232
233    /// Synchronize file contents.
234    /// If the datasync parameter is non-zero, then only the user data should be flushed,
235    /// not the meta data.
236    fn fsync(&mut self, _req: &Request, _ino: u64, _fh: u64, _datasync: bool, reply: ReplyEmpty) {
237        reply.error(ENOSYS);
238    }
239
240    /// Open a directory.
241    /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh, and
242    /// use this in other all other directory stream operations (readdir, releasedir,
243    /// fsyncdir). Filesystem may also implement stateless directory I/O and not store
244    /// anything in fh, though that makes it impossible to implement standard conforming
245    /// directory stream operations in case the contents of the directory can change
246    /// between opendir and releasedir.
247    fn opendir(&mut self, _req: &Request, _ino: u64, _flags: u32, reply: ReplyOpen) {
248        reply.opened(0, 0);
249    }
250
251    /// Read directory.
252    /// Send a buffer filled using buffer.fill(), with size not exceeding the
253    /// requested size. Send an empty buffer on end of stream. fh will contain the
254    /// value set by the opendir method, or will be undefined if the opendir method
255    /// didn't set any value.
256    fn readdir(&mut self, _req: &Request, _ino: u64, _fh: u64, _offset: i64, reply: ReplyDirectory) {
257        reply.error(ENOSYS);
258    }
259
260    /// Release an open directory.
261    /// For every opendir call there will be exactly one releasedir call. fh will
262    /// contain the value set by the opendir method, or will be undefined if the
263    /// opendir method didn't set any value.
264    fn releasedir(&mut self, _req: &Request, _ino: u64, _fh: u64, _flags: u32, reply: ReplyEmpty) {
265        reply.ok();
266    }
267
268    /// Synchronize directory contents.
269    /// If the datasync parameter is set, then only the directory contents should
270    /// be flushed, not the meta data. fh will contain the value set by the opendir
271    /// method, or will be undefined if the opendir method didn't set any value.
272    fn fsyncdir (&mut self, _req: &Request, _ino: u64, _fh: u64, _datasync: bool, reply: ReplyEmpty) {
273        reply.error(ENOSYS);
274    }
275
276    /// Get file system statistics.
277    fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) {
278        reply.statfs(0, 0, 0, 0, 0, 512, 255, 0);
279    }
280
281    /// Set an extended attribute.
282    fn setxattr(&mut self, _req: &Request, _ino: u64, _name: &OsStr, _value: &[u8], _flags: u32, _position: u32, reply: ReplyEmpty) {
283        reply.error(ENOSYS);
284    }
285
286    /// Get an extended attribute.
287    /// If `size` is 0, the size of the value should be sent with `reply.size()`.
288    /// If `size` is not 0, and the value fits, send it with `reply.data()`, or
289    /// `reply.error(ERANGE)` if it doesn't.
290    fn getxattr(&mut self, _req: &Request, _ino: u64, _name: &OsStr, _size: u32, reply: ReplyXattr) {
291        reply.error(ENOSYS);
292    }
293
294    /// List extended attribute names.
295    /// If `size` is 0, the size of the value should be sent with `reply.size()`.
296    /// If `size` is not 0, and the value fits, send it with `reply.data()`, or
297    /// `reply.error(ERANGE)` if it doesn't.
298    fn listxattr(&mut self, _req: &Request, _ino: u64, _size: u32, reply: ReplyXattr) {
299        reply.error(ENOSYS);
300    }
301
302    /// Remove an extended attribute.
303    fn removexattr(&mut self, _req: &Request, _ino: u64, _name: &OsStr, reply: ReplyEmpty) {
304        reply.error(ENOSYS);
305    }
306
307    /// Check file access permissions.
308    /// This will be called for the access() system call. If the 'default_permissions'
309    /// mount option is given, this method is not called. This method is not called
310    /// under Linux kernel versions 2.4.x
311    fn access(&mut self, _req: &Request, _ino: u64, _mask: u32, reply: ReplyEmpty) {
312        reply.error(ENOSYS);
313    }
314
315    /// Create and open a file.
316    /// If the file does not exist, first create it with the specified mode, and then
317    /// open it. Open flags (with the exception of O_NOCTTY) are available in flags.
318    /// Filesystem may store an arbitrary file handle (pointer, index, etc) in fh,
319    /// and use this in other all other file operations (read, write, flush, release,
320    /// fsync). There are also some flags (direct_io, keep_cache) which the
321    /// filesystem may set, to change the way the file is opened. See fuse_file_info
322    /// structure in <fuse_common.h> for more details. If this method is not
323    /// implemented or under Linux kernel versions earlier than 2.6.15, the mknod()
324    /// and open() methods will be called instead.
325    fn create(&mut self, _req: &Request, _parent: u64, _name: &OsStr, _mode: u32, _flags: u32, reply: ReplyCreate) {
326        reply.error(ENOSYS);
327    }
328
329    /// Test for a POSIX file lock.
330    fn getlk(&mut self, _req: &Request, _ino: u64, _fh: u64, _lock_owner: u64, _start: u64, _end: u64, _typ: u32, _pid: u32, reply: ReplyLock) {
331        reply.error(ENOSYS);
332    }
333
334    /// Acquire, modify or release a POSIX file lock.
335    /// For POSIX threads (NPTL) there's a 1-1 relation between pid and owner, but
336    /// otherwise this is not always the case.  For checking lock ownership,
337    /// 'fi->owner' must be used. The l_pid field in 'struct flock' should only be
338    /// used to fill in this field in getlk(). Note: if the locking methods are not
339    /// implemented, the kernel will still allow file locking to work locally.
340    /// Hence these are only interesting for network filesystems and similar.
341    fn setlk(&mut self, _req: &Request, _ino: u64, _fh: u64, _lock_owner: u64, _start: u64, _end: u64, _typ: u32, _pid: u32, _sleep: bool, reply: ReplyEmpty) {
342        reply.error(ENOSYS);
343    }
344
345    /// Map block index within file to block index within device.
346    /// Note: This makes sense only for block device backed filesystems mounted
347    /// with the 'blkdev' option
348    fn bmap(&mut self, _req: &Request, _ino: u64, _blocksize: u32, _idx: u64, reply: ReplyBmap) {
349        reply.error(ENOSYS);
350    }
351
352    /// macOS only: Rename the volume. Set fuse_init_out.flags during init to
353    /// FUSE_VOL_RENAME to enable
354    #[cfg(target_os = "macos")]
355    fn setvolname(&mut self, _req: &Request, _name: &OsStr, reply: ReplyEmpty) {
356        reply.error(ENOSYS);
357    }
358
359    /// macOS only (undocumented)
360    #[cfg(target_os = "macos")]
361    fn exchange(&mut self, _req: &Request, _parent: u64, _name: &OsStr, _newparent: u64, _newname: &OsStr, _options: u64, reply: ReplyEmpty) {
362        reply.error(ENOSYS);
363    }
364
365    /// macOS only: Query extended times (bkuptime and crtime). Set fuse_init_out.flags
366    /// during init to FUSE_XTIMES to enable
367    #[cfg(target_os = "macos")]
368    fn getxtimes(&mut self, _req: &Request, _ino: u64, reply: ReplyXTimes) {
369        reply.error(ENOSYS);
370    }
371}
372
373/// Mount the given filesystem to the given mountpoint. This function will
374/// not return until the filesystem is unmounted.
375pub fn mount<FS: Filesystem, P: AsRef<Path>>(filesystem: FS, mountpoint: &P, options: &[&OsStr]) -> io::Result<()>{
376    Session::new(filesystem, mountpoint.as_ref(), options).and_then(|mut se| se.run())
377}
378
379/// Mount the given filesystem to the given mountpoint. This function spawns
380/// a background thread to handle filesystem operations while being mounted
381/// and therefore returns immediately. The returned handle should be stored
382/// to reference the mounted filesystem. If it's dropped, the filesystem will
383/// be unmounted.
384pub unsafe fn spawn_mount<'a, FS: Filesystem+Send+'a, P: AsRef<Path>>(filesystem: FS, mountpoint: &P, options: &[&OsStr]) -> io::Result<BackgroundSession<'a>> {
385    Session::new(filesystem, mountpoint.as_ref(), options).and_then(|se| se.spawn())
386}