diff --git a/README.md b/README.md
index c82b51e..33b6b51 100644
--- a/README.md
+++ b/README.md
@@ -1,143 +1,236 @@
-# remotefs-fuse
-
-
-
-
-
-~ A FUSE Driver for remotefs-rs ~
-
-Developed by @veeso
-Current version: WIP
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----
-
-## Get started
-
-Coming soon...
-
-## CLI Tool
-
-remotefs-fuse comes with a CLI tool **remotefs-fuse-cli** to mount remote file systems with FUSE.
-
-```sh
-cargo install remotefs-fuse-cli
-```
-
-### Features
-
-remotefs-fuse-cli can be built with the features below; each feature enables a different file transfer protocol
-
-- `aws-s3`
-- `ftp`
-- `kube`
-- `smb`: requires `libsmbclient` on MacOS and GNU/Linux systems
-- `ssh` (enables **both sftp and scp**); requires `libssh2` on MacOS and GNU/Linux systems
-- `webdav`
-
-All the features are enabled by default; so if you want to build it with only certain features, pass the `--no-default-features` option.
-
-### Usage
-
-```sh
-remotefs-fuse-cli --to /mnt/to --volume [protocol-options...]
-```
-
-where protocol options are
-
-- aws-s3
- - `--bucket `
- - `--region ` (optional)
- - `--endpoint ` (optional)
- - `--profile ` (optional)
- - `--access-key ` (optional)
- - `--security-token ` (optional)
- - `--new-path-style` use new path style
-- ftp
- - `--hostname `
- - `--port ` (default 21)
- - `--username ` (default: `anonymous`)
- - `--password ` (optional)
- - `--secure` specify it if you want to use FTPS
- - `--active` specify it if you want to use ACTIVE mode
-- kube
- - `--namespace ` (default: `default`)
- - `--cluster-url `
-- memory: runs a virtual file system in memory
-- smb
- - `--address `
- - `--port ` (default: `139`; Linux/Mac only)
- - `--share `
- - `--username ` (optional)
- - `--password ` (optional)
- - `--workgroup ` (optional; Linux/Mac only)
-- scp / sftp
- - `--hostname `
- - `--port ` (default `22`)
- - `--username `
- - `--password `
-- webdav
- - `--url `
- - `--username `
- - `--password `
-
-## Changelog ⏳
-
-View remotefs` changelog [HERE](CHANGELOG.md)
-
----
-
-## License 📃
-
-remotefs is licensed under the MIT license.
-
-You can read the entire license [HERE](LICENSE)
+# remotefs-fuse
+
+
+
+
+
+~ A FUSE Driver for remotefs-rs ~
+
+Developed by @veeso
+Current version: 0.1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+## Get started
+
+First of all you need to add **remotefs-fuse** to your project dependencies:
+
+```toml
+remotefs-fuse = "^0.1.0"
+```
+
+these features are supported:
+
+- `no-log`: disable logging. By default, this library will log via the `log` crate.
+
+## Example
+
+```rust,no_run,ignore
+use remotefs_fuse::Mount;
+
+let options = vec![
+ #[cfg(unix)]
+ remotefs_fuse::MountOption::AllowRoot,
+ #[cfg(unix)]
+ remotefs_fuse::MountOption::RW,
+ #[cfg(unix)]
+ remotefs_fuse::MountOption::Exec,
+ #[cfg(unix)]
+ remotefs_fuse::MountOption::Sync,
+ #[cfg(unix)]
+ remotefs_fuse::MountOption::FSName(volume),
+];
+
+let remote = MyRemoteFsImpl::new();
+let mount_path = std::path::PathBuf::from("/mnt/remote");
+let mut mount = Mount::mount(remote, &mount_path, &options).expect("Failed to mount");
+let mut umount = mount.unmounter();
+
+// setup signal handler
+ctrlc::set_handler(move || {
+ umount.umount().expect("Failed to unmount");
+})?;
+
+mount.run().expect("Failed to run filesystem event loop");
+```
+
+## Requirements
+
+- **Linux**: you need to have `fuse3` installed on your system.
+
+ Of course, you also need to have the `FUSE` kernel module installed.
+ To build `remotefs-fuse` on Linux, you need to have the `libfuse3` development package installed.
+
+ In Ubuntu, you can install it with:
+
+ ```sh
+ sudo apt-get install fuse3 libfuse3-dev
+ ```
+
+ In CentOS, you can install it with:
+
+ ```sh
+ sudo yum install fuse-devel
+ ```
+
+- **macOS**: you need to have the `macfuse` service installed on your system.
+
+ You can install it with:
+
+ ```sh
+ brew install macfuse
+ ```
+
+- **Windows**: you need to have the `dokany` service installed on your system.
+
+ You can install it from
+
+## CLI Tool
+
+remotefs-fuse comes with a CLI tool **remotefs-fuse-cli** to mount remote file systems with FUSE.
+
+```sh
+cargo install remotefs-fuse-cli
+```
+
+### Features
+
+remotefs-fuse-cli can be built with the features below; each feature enables a different file transfer protocol
+
+- `aws-s3`
+- `ftp`
+- `kube`
+- `smb`: requires `libsmbclient` on MacOS and GNU/Linux systems
+- `ssh` (enables **both sftp and scp**); requires `libssh2` on MacOS and GNU/Linux systems
+- `webdav`
+
+All the features are enabled by default; so if you want to build it with only certain features, pass the `--no-default-features` option.
+
+### Usage
+
+```sh
+remotefs-fuse-cli -o opt1 -o opt2=abc --to /mnt/to --volume [protocol-options...]
+```
+
+where protocol options are
+
+- aws-s3
+ - `--bucket `
+ - `--region ` (optional)
+ - `--endpoint ` (optional)
+ - `--profile ` (optional)
+ - `--access-key ` (optional)
+ - `--security-token ` (optional)
+ - `--new-path-style` use new path style
+- ftp
+ - `--hostname `
+ - `--port ` (default 21)
+ - `--username ` (default: `anonymous`)
+ - `--password ` (optional)
+ - `--secure` specify it if you want to use FTPS
+ - `--active` specify it if you want to use ACTIVE mode
+- kube
+ - `--namespace ` (default: `default`)
+ - `--cluster-url `
+- memory: runs a virtual file system in memory
+- smb
+ - `--address `
+ - `--port ` (default: `139`; Linux/Mac only)
+ - `--share `
+ - `--username ` (optional)
+ - `--password ` (optional)
+ - `--workgroup ` (optional; Linux/Mac only)
+- scp / sftp
+ - `--hostname `
+ - `--port ` (default `22`)
+ - `--username `
+ - `--password `
+- webdav
+ - `--url `
+ - `--username `
+ - `--password `
+
+Other options are:
+
+- `--uid `: specify the UID to overwrite when mounting the remote fs. See [UID and GID override](#uid-and-gid-override).
+- `--gid `: specify the GID to overwrite when mounting the remote fs. See [UID and GID override](#uid-and-gid-override).
+- `--default-mode `: set the default file mode to use when the remote fs doesn't support it.
+
+Mount options can be viewed in the docs at .
+
+## UID and GID override
+
+The possibility to override UID and GID is used because sometimes this scenario can happen:
+
+1. my UID is `1000`
+2. I'm mounting for instance a SFTP file system and the remote user I used to sign in has UID `1002`
+3. I'm unable to operate on the file system because UID `1000` can't operate to files owned by `1002`
+
+But of course this doesn't make sense: I signed in with user who owns those files, so I should be able to operate on them.
+That's why I've added `Uid` and `Gid` into the `MountOption` variant.
+
+Setting the `Uid` option to `1002` you'll be able to operate on the File system as it should.
+
+> ❗ This doesn't apply to Windows.
+
+## Changelog ⏳
+
+View remotefs-fuse`s changelog [HERE](CHANGELOG.md)
+
+---
+
+## License 📃
+
+remotefs-fuse is licensed under the MIT license.
+
+You can read the entire license [HERE](LICENSE)
diff --git a/remotefs-fuse-cli/src/cli.rs b/remotefs-fuse-cli/src/cli.rs
index d25b232..5a18f2d 100644
--- a/remotefs-fuse-cli/src/cli.rs
+++ b/remotefs-fuse-cli/src/cli.rs
@@ -15,6 +15,7 @@ mod webdav;
use std::path::PathBuf;
use argh::FromArgs;
+use remotefs_fuse::MountOption;
#[cfg(feature = "aws-s3")]
use self::aws_s3::AwsS3Args;
@@ -44,16 +45,24 @@ pub struct CliArgs {
#[argh(option)]
pub volume: String,
/// uid to use for the mounted filesystem
+ #[cfg(unix)]
#[argh(option)]
pub uid: Option,
/// gid to use for the mounted filesystem
#[argh(option)]
+ #[cfg(unix)]
pub gid: Option,
/// default file permissions for those remote file protocols that don't support file permissions.
///
/// this is a 3-digit octal number, e.g. 644
#[argh(option, from_str_fn(from_octal))]
+ #[cfg(unix)]
pub default_mode: Option,
+ /// mount options
+ ///
+ /// Mount options are specific to the underlying filesystem and are passed as key=value pairs.
+ #[argh(option, short = 'o')]
+ pub option: Vec,
/// enable verbose logging.
///
/// use multiple times to increase verbosity
@@ -63,6 +72,7 @@ pub struct CliArgs {
remote: RemoteArgs,
}
+#[cfg(unix)]
fn from_octal(s: &str) -> Result {
u32::from_str_radix(s, 8).map_err(|_| "Invalid octal number".to_string())
}
diff --git a/remotefs-fuse-cli/src/main.rs b/remotefs-fuse-cli/src/main.rs
index f1376d9..2aa37a1 100644
--- a/remotefs-fuse-cli/src/main.rs
+++ b/remotefs-fuse-cli/src/main.rs
@@ -1,7 +1,7 @@
mod cli;
mod remotefs_wrapper;
-use remotefs_fuse::{Mount, MountOption};
+use remotefs_fuse::Mount;
fn main() -> anyhow::Result<()> {
let args = argh::from_env::();
@@ -13,25 +13,29 @@ fn main() -> anyhow::Result<()> {
// make options
let mut options = vec![
#[cfg(unix)]
- MountOption::AllowRoot,
+ remotefs_fuse::MountOption::AllowRoot,
#[cfg(unix)]
- MountOption::RW,
+ remotefs_fuse::MountOption::RW,
#[cfg(unix)]
- MountOption::Exec,
+ remotefs_fuse::MountOption::Exec,
#[cfg(unix)]
- MountOption::Sync,
+ remotefs_fuse::MountOption::Sync,
#[cfg(unix)]
- MountOption::FSName(volume),
+ remotefs_fuse::MountOption::FSName(volume),
];
+ options.extend(args.option.clone());
+ #[cfg(unix)]
if let Some(uid) = args.uid {
log::info!("Default uid: {uid}");
options.push(MountOption::Uid(uid));
}
+ #[cfg(unix)]
if let Some(gid) = args.gid {
log::info!("Default gid: {gid}");
options.push(MountOption::Gid(gid));
}
+ #[cfg(unix)]
if let Some(default_mode) = args.default_mode {
log::info!("Default mode: {default_mode:o}");
options.push(MountOption::DefaultMode(default_mode));
diff --git a/remotefs-fuse/src/lib.rs b/remotefs-fuse/src/lib.rs
index 0644b9c..4430084 100644
--- a/remotefs-fuse/src/lib.rs
+++ b/remotefs-fuse/src/lib.rs
@@ -4,7 +4,39 @@
//! # remotefs-fuse
//!
-//! TODO
+//! **remotefs-fuse** is a library that allows you to mount a remote file system using **FUSE** on Linux and macOS and with
+//! **Dokany** on Windows.
+//!
+//! ## Requirements
+//!
+//! - **Linux**: you need to have `fuse3` installed on your system.
+//!
+//! Of course, you also need to have the `FUSE` kernel module installed.
+//! To build `remotefs-fuse` on Linux, you need to have the `libfuse3` development package installed.
+//!
+//! In Ubuntu, you can install it with:
+//!
+//! ```sh
+//! sudo apt-get install fuse3 libfuse3-dev
+//! ```
+//!
+//! In CentOS, you can install it with:
+//!
+//! ```sh
+//! sudo yum install fuse-devel
+//! ```
+//!
+//! - **macOS**: you need to have the `macfuse` service installed on your system.
+//!
+//! You can install it with:
+//!
+//! ```sh
+//! brew install macfuse
+//! ```
+//!
+//! - **Windows**: you need to have the `dokany` service installed on your system.
+//!
+//! You can install it from
//!
//! ## Get started
//!
@@ -18,6 +50,37 @@
//!
//! - `no-log`: disable logging. By default, this library will log via the `log` crate.
//!
+//! ## Example
+//!
+//! ```rust,no_run,ignore
+//! use remotefs_fuse::Mount;
+//!
+//! let options = vec![
+//! #[cfg(unix)]
+//! remotefs_fuse::MountOption::AllowRoot,
+//! #[cfg(unix)]
+//! remotefs_fuse::MountOption::RW,
+//! #[cfg(unix)]
+//! remotefs_fuse::MountOption::Exec,
+//! #[cfg(unix)]
+//! remotefs_fuse::MountOption::Sync,
+//! #[cfg(unix)]
+//! remotefs_fuse::MountOption::FSName(volume),
+//! ];
+//!
+//! let remote = todo!();
+//! let mount_path = std::path::PathBuf::from("/mnt/remote");
+//! let mut mount = Mount::mount(remote, &mount_path, &options).expect("Failed to mount");
+//! let mut umount = mount.unmounter();
+//!
+//! // setup signal handler
+//! ctrlc::set_handler(move || {
+//! umount.umount().expect("Failed to unmount");
+//! })?;
+//!
+//! mount.run().expect("Failed to run filesystem event loop");
+//! ```
+//!
#![doc(html_playground_url = "https://play.rust-lang.org")]
#![doc(
diff --git a/remotefs-fuse/src/mount/option.rs b/remotefs-fuse/src/mount/option.rs
index da4b146..9eb35fe 100644
--- a/remotefs-fuse/src/mount/option.rs
+++ b/remotefs-fuse/src/mount/option.rs
@@ -1,189 +1,328 @@
-/// Mount options for mounting a FUSE filesystem
-///
-/// Some of them are *nix-specific, and may not be available on other platforms, while other
-/// are for Windows only.
-#[derive(Debug, Eq, PartialEq, Hash, Clone)]
-pub enum MountOption {
- /* nix driver */
- /// Treat all files as if they are owned by the given user.
- /// This flag can be useful when mounting for instance sftp volumes,
- /// where the uid/gid of the files may be different from the user mounting the filesystem.
- /// This doesn't change the ownership of the files, but allows the user to access them.
- /// Of course, if the signed in user doesn't have the right permissions, the files will still be inaccessible.
- Uid(u32),
- /// Treat all files as if they are owned by the given user.
- /// This flag can be useful when mounting for instance sftp volumes,
- /// where the uid/gid of the files may be different from the user mounting the filesystem.
- /// This doesn't change the ownership of the files, but allows the user to access them.
- /// Of course, if the signed in user doesn't have the right permissions, the files will still be inaccessible.
- Gid(u32),
- /// Set the default file mode in case the filesystem doesn't provide one
- /// If not set, the default is 0755
- DefaultMode(u32),
- /* fuser */
- /// Set the name of the source in mtab
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- FSName(String),
- /// Set the filesystem subtype in mtab
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Subtype(String),
- /// Allows passing an option which is not otherwise supported in these enums
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Custom(String),
- /// Allow all users to access files on this filesystem. By default access is restricted to the
- /// user who mounted it
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- AllowOther,
- /// Allow the root user to access this filesystem, in addition to the user who mounted it
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- AllowRoot,
- /// Automatically unmount when the mounting process exits
- ///
- /// `AutoUnmount` requires `AllowOther` or `AllowRoot`. If `AutoUnmount` is set and neither `Allow...` is set, the FUSE configuration must permit `allow_other`, otherwise mounting will fail.
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- AutoUnmount,
- /// Enable permission checking in the kernel
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- DefaultPermissions,
-
- /// Enable special character and block devices
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Dev,
- /// Disable special character and block devices
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- NoDev,
- /// Honor set-user-id and set-groupd-id bits on files
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Suid,
- /// Don't honor set-user-id and set-groupd-id bits on files
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- NoSuid,
- /// Read-only filesystem
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- RO,
- /// Read-write filesystem
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- RW,
- /// Allow execution of binaries
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Exec,
- /// Don't allow execution of binaries
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- NoExec,
- /// Support inode access time
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Atime,
- /// Don't update inode access time
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- NoAtime,
- /// All modifications to directories will be done synchronously
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- DirSync,
- /// All I/O will be done synchronously
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Sync,
- /// All I/O will be done asynchronously
- #[cfg(unix)]
- #[cfg_attr(docsrs, doc(cfg(unix)))]
- Async,
-
- // dokany
- /// Only use a single thread to process events. This is highly not recommended as can easily create a bottleneck.
- #[cfg(windows)]
- #[cfg_attr(docsrs, doc(cfg(windows)))]
- SingleThread,
- /// Controls behavior of the volume.
- #[cfg(windows)]
- #[cfg_attr(docsrs, doc(cfg(windows)))]
- Flags(u32),
- /// Max timeout of each request before Dokan gives up to wait events to complete.
- /// Timeout request is a sign that the userland implementation is no longer able to properly manage requests in time.
- /// The driver will therefore unmount the device when a timeout trigger in order to keep the system stable.
- ///
- /// If zero, defaults to 15 seconds.
- #[cfg(windows)]
- #[cfg_attr(docsrs, doc(cfg(windows)))]
- Timeout(std::time::Duration),
- /// Allocation Unit Size of the volume. This will affect the file size.
- #[cfg(windows)]
- #[cfg_attr(docsrs, doc(cfg(windows)))]
- AllocationUnitSize(u32),
- /// Sector Size of the volume. This will affect the file size.
- #[cfg(windows)]
- #[cfg_attr(docsrs, doc(cfg(windows)))]
- SectorSize(u32),
-}
-
-#[cfg(unix)]
-#[cfg_attr(docsrs, doc(cfg(unix)))]
-impl TryFrom<&MountOption> for fuser::MountOption {
- type Error = &'static str;
-
- fn try_from(value: &MountOption) -> Result {
- Ok(match value {
- MountOption::FSName(name) => fuser::MountOption::FSName(name.clone()),
- MountOption::Subtype(name) => fuser::MountOption::Subtype(name.clone()),
- MountOption::Custom(name) => fuser::MountOption::CUSTOM(name.clone()),
- MountOption::AllowOther => fuser::MountOption::AllowOther,
- MountOption::AllowRoot => fuser::MountOption::AllowRoot,
- MountOption::AutoUnmount => fuser::MountOption::AutoUnmount,
- MountOption::DefaultPermissions => fuser::MountOption::DefaultPermissions,
- MountOption::Dev => fuser::MountOption::Dev,
- MountOption::NoDev => fuser::MountOption::NoDev,
- MountOption::Suid => fuser::MountOption::Suid,
- MountOption::NoSuid => fuser::MountOption::NoSuid,
- MountOption::RO => fuser::MountOption::RO,
- MountOption::RW => fuser::MountOption::RW,
- MountOption::Exec => fuser::MountOption::Exec,
- MountOption::NoExec => fuser::MountOption::NoExec,
- MountOption::Atime => fuser::MountOption::Atime,
- MountOption::NoAtime => fuser::MountOption::NoAtime,
- MountOption::DirSync => fuser::MountOption::DirSync,
- MountOption::Sync => fuser::MountOption::Sync,
- MountOption::Async => fuser::MountOption::Async,
- _ => return Err("Unsupported mount option"),
- })
- }
-}
-
-#[cfg(windows)]
-#[cfg_attr(docsrs, doc(cfg(windows)))]
-impl MountOption {
- pub fn into_dokan_options(options: &[MountOption]) -> dokan::MountOptions<'_> {
- let mut dokan_options = dokan::MountOptions::default();
-
- for option in options {
- match option {
- MountOption::SingleThread => dokan_options.single_thread = true,
- MountOption::Flags(flags) => {
- dokan_options.flags = dokan::MountFlags::from_bits_truncate(*flags)
- }
- MountOption::Timeout(timeout) => dokan_options.timeout = *timeout,
- MountOption::AllocationUnitSize(size) => dokan_options.allocation_unit_size = *size,
- MountOption::SectorSize(size) => dokan_options.sector_size = *size,
- _ => {}
- }
- }
-
- dokan_options
- }
-}
+use std::str::FromStr;
+use std::time::Duration;
+
+/// Mount options for mounting a FUSE filesystem
+///
+/// Some of them are *nix-specific, and may not be available on other platforms, while other
+/// are for Windows only.
+///
+/// [`MountOption`] implements [`FromStr`] with the syntax `key[=value]` for all options.
+#[derive(Debug, Eq, PartialEq, Hash, Clone)]
+pub enum MountOption {
+ /* nix driver */
+ #[cfg(unix)]
+ /// Treat all files as if they are owned by the given user.
+ /// This flag can be useful when mounting for instance sftp volumes,
+ /// where the uid/gid of the files may be different from the user mounting the filesystem.
+ /// This doesn't change the ownership of the files, but allows the user to access them.
+ /// Of course, if the signed in user doesn't have the right permissions, the files will still be inaccessible.
+ Uid(u32),
+ #[cfg(unix)]
+ /// Treat all files as if they are owned by the given user.
+ /// This flag can be useful when mounting for instance sftp volumes,
+ /// where the uid/gid of the files may be different from the user mounting the filesystem.
+ /// This doesn't change the ownership of the files, but allows the user to access them.
+ /// Of course, if the signed in user doesn't have the right permissions, the files will still be inaccessible.
+ Gid(u32),
+ #[cfg(unix)]
+ /// Set the default file mode in case the filesystem doesn't provide one
+ /// If not set, the default is 0755
+ DefaultMode(u32),
+ /* fuser */
+ /// Set the name of the source in mtab
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ FSName(String),
+ /// Set the filesystem subtype in mtab
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Subtype(String),
+ /// Allows passing an option which is not otherwise supported in these enums
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Custom(String),
+ /// Allow all users to access files on this filesystem. By default access is restricted to the
+ /// user who mounted it
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ AllowOther,
+ /// Allow the root user to access this filesystem, in addition to the user who mounted it
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ AllowRoot,
+ /// Automatically unmount when the mounting process exits
+ ///
+ /// `AutoUnmount` requires `AllowOther` or `AllowRoot`. If `AutoUnmount` is set and neither `Allow...` is set, the FUSE configuration must permit `allow_other`, otherwise mounting will fail.
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ AutoUnmount,
+ /// Enable permission checking in the kernel
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ DefaultPermissions,
+
+ /// Enable special character and block devices
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Dev,
+ /// Disable special character and block devices
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ NoDev,
+ /// Honor set-user-id and set-groupd-id bits on files
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Suid,
+ /// Don't honor set-user-id and set-groupd-id bits on files
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ NoSuid,
+ /// Read-only filesystem
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ RO,
+ /// Read-write filesystem
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ RW,
+ /// Allow execution of binaries
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Exec,
+ /// Don't allow execution of binaries
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ NoExec,
+ /// Support inode access time
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Atime,
+ /// Don't update inode access time
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ NoAtime,
+ /// All modifications to directories will be done synchronously
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ DirSync,
+ /// All I/O will be done synchronously
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Sync,
+ /// All I/O will be done asynchronously
+ #[cfg(unix)]
+ #[cfg_attr(docsrs, doc(cfg(unix)))]
+ Async,
+
+ // dokany
+ /// Only use a single thread to process events. This is highly not recommended as can easily create a bottleneck.
+ #[cfg(windows)]
+ #[cfg_attr(docsrs, doc(cfg(windows)))]
+ SingleThread,
+ /// Controls behavior of the volume.
+ #[cfg(windows)]
+ #[cfg_attr(docsrs, doc(cfg(windows)))]
+ Flags(u32),
+ /// Max timeout of each request before Dokan gives up to wait events to complete.
+ /// Timeout request is a sign that the userland implementation is no longer able to properly manage requests in time.
+ /// The driver will therefore unmount the device when a timeout trigger in order to keep the system stable.
+ ///
+ /// If zero, defaults to 15 seconds.
+ #[cfg(windows)]
+ #[cfg_attr(docsrs, doc(cfg(windows)))]
+ Timeout(std::time::Duration),
+ /// Allocation Unit Size of the volume. This will affect the file size.
+ #[cfg(windows)]
+ #[cfg_attr(docsrs, doc(cfg(windows)))]
+ AllocationUnitSize(u32),
+ /// Sector Size of the volume. This will affect the file size.
+ #[cfg(windows)]
+ #[cfg_attr(docsrs, doc(cfg(windows)))]
+ SectorSize(u32),
+}
+
+#[cfg(unix)]
+#[cfg_attr(docsrs, doc(cfg(unix)))]
+impl TryFrom<&MountOption> for fuser::MountOption {
+ type Error = &'static str;
+
+ fn try_from(value: &MountOption) -> Result {
+ Ok(match value {
+ MountOption::FSName(name) => fuser::MountOption::FSName(name.clone()),
+ MountOption::Subtype(name) => fuser::MountOption::Subtype(name.clone()),
+ MountOption::Custom(name) => fuser::MountOption::CUSTOM(name.clone()),
+ MountOption::AllowOther => fuser::MountOption::AllowOther,
+ MountOption::AllowRoot => fuser::MountOption::AllowRoot,
+ MountOption::AutoUnmount => fuser::MountOption::AutoUnmount,
+ MountOption::DefaultPermissions => fuser::MountOption::DefaultPermissions,
+ MountOption::Dev => fuser::MountOption::Dev,
+ MountOption::NoDev => fuser::MountOption::NoDev,
+ MountOption::Suid => fuser::MountOption::Suid,
+ MountOption::NoSuid => fuser::MountOption::NoSuid,
+ MountOption::RO => fuser::MountOption::RO,
+ MountOption::RW => fuser::MountOption::RW,
+ MountOption::Exec => fuser::MountOption::Exec,
+ MountOption::NoExec => fuser::MountOption::NoExec,
+ MountOption::Atime => fuser::MountOption::Atime,
+ MountOption::NoAtime => fuser::MountOption::NoAtime,
+ MountOption::DirSync => fuser::MountOption::DirSync,
+ MountOption::Sync => fuser::MountOption::Sync,
+ MountOption::Async => fuser::MountOption::Async,
+ _ => return Err("Unsupported mount option"),
+ })
+ }
+}
+
+#[cfg(windows)]
+#[cfg_attr(docsrs, doc(cfg(windows)))]
+impl MountOption {
+ pub fn into_dokan_options(options: &[MountOption]) -> dokan::MountOptions<'_> {
+ let mut dokan_options = dokan::MountOptions::default();
+
+ for option in options {
+ match option {
+ MountOption::SingleThread => dokan_options.single_thread = true,
+ MountOption::Flags(flags) => {
+ dokan_options.flags = dokan::MountFlags::from_bits_truncate(*flags)
+ }
+ MountOption::Timeout(timeout) => dokan_options.timeout = *timeout,
+ MountOption::AllocationUnitSize(size) => dokan_options.allocation_unit_size = *size,
+ MountOption::SectorSize(size) => dokan_options.sector_size = *size,
+ }
+ }
+
+ dokan_options
+ }
+}
+
+impl FromStr for MountOption {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result {
+ let (option, value) = match s.find('=') {
+ Some(index) => (
+ (s[..index]).to_ascii_lowercase().to_string(),
+ Some(&s[index + 1..]),
+ ),
+ None => (s.to_ascii_lowercase().to_string(), None),
+ };
+
+ match (option.as_str(), value) {
+ #[cfg(unix)]
+ ("uid", Some(value)) => {
+ let value = value
+ .parse()
+ .map_err(|e| format!("Invalid uid value: {}", e))?;
+ Ok(MountOption::Uid(value))
+ }
+ #[cfg(unix)]
+ ("uid", None) => Err("uid requires a value".to_string()),
+ #[cfg(unix)]
+ ("gid", Some(value)) => {
+ let value = value
+ .parse()
+ .map_err(|e| format!("Invalid gid value: {}", e))?;
+ Ok(MountOption::Gid(value))
+ }
+ #[cfg(unix)]
+ ("gid", None) => Err("gid requires a value".to_string()),
+ #[cfg(unix)]
+ ("default_mode", Some(value)) => {
+ let value = u32::from_str_radix(value, 8)
+ .map_err(|e| format!("Invalid default_mode value: {}", e))?;
+ Ok(MountOption::DefaultMode(value))
+ }
+ #[cfg(unix)]
+ ("default_mode", None) => Err("default_mode requires a value".to_string()),
+ #[cfg(unix)]
+ ("fsname", Some(value)) => Ok(MountOption::FSName(value.to_string())),
+ #[cfg(unix)]
+ ("fsname", None) => Err("fsname requires a value".to_string()),
+ #[cfg(unix)]
+ ("subtype", Some(value)) => Ok(MountOption::Subtype(value.to_string())),
+ #[cfg(unix)]
+ ("subtype", None) => Err("subtype requires a value".to_string()),
+ #[cfg(unix)]
+ ("custom", Some(value)) => Ok(MountOption::Custom(value.to_string())),
+ #[cfg(unix)]
+ ("custom", None) => Err("custom requires a value".to_string()),
+ #[cfg(unix)]
+ ("allow_other", None) => Ok(MountOption::AllowOther),
+ #[cfg(unix)]
+ ("allow_root", None) => Ok(MountOption::AllowRoot),
+ #[cfg(unix)]
+ ("auto_unmount", None) => Ok(MountOption::AutoUnmount),
+ #[cfg(unix)]
+ ("default_permissions", None) => Ok(MountOption::DefaultPermissions),
+ #[cfg(unix)]
+ ("dev", None) => Ok(MountOption::Dev),
+ #[cfg(unix)]
+ ("nodev", None) => Ok(MountOption::NoDev),
+ #[cfg(unix)]
+ ("suid", None) => Ok(MountOption::Suid),
+ #[cfg(unix)]
+ ("nosuid", None) => Ok(MountOption::NoSuid),
+ #[cfg(unix)]
+ ("ro", None) => Ok(MountOption::RO),
+ #[cfg(unix)]
+ ("rw", None) => Ok(MountOption::RW),
+ #[cfg(unix)]
+ ("exec", None) => Ok(MountOption::Exec),
+ #[cfg(unix)]
+ ("noexec", None) => Ok(MountOption::NoExec),
+ #[cfg(unix)]
+ ("atime", None) => Ok(MountOption::Atime),
+ #[cfg(unix)]
+ ("noatime", None) => Ok(MountOption::NoAtime),
+ #[cfg(unix)]
+ ("dirsync", None) => Ok(MountOption::DirSync),
+ #[cfg(unix)]
+ ("sync", None) => Ok(MountOption::Sync),
+ #[cfg(unix)]
+ ("async", None) => Ok(MountOption::Async),
+ #[cfg(windows)]
+ ("single_thread", None) => Ok(MountOption::SingleThread),
+ #[cfg(windows)]
+ ("flags", Some(value)) => {
+ let value = value
+ .parse()
+ .map_err(|e| format!("Invalid flags value: {}", e))?;
+ Ok(MountOption::Flags(value))
+ }
+ #[cfg(windows)]
+ ("flags", None) => Err("flags requires a value".to_string()),
+ #[cfg(windows)]
+ ("timeout", Some(value)) => {
+ let value = Duration::from_millis(
+ value
+ .parse()
+ .map_err(|e| format!("Invalid timeout value: {}", e))?,
+ );
+ Ok(MountOption::Timeout(value))
+ }
+ #[cfg(windows)]
+ ("timeout", None) => Err("timeout requires a value".to_string()),
+ #[cfg(windows)]
+ ("allocation_unit_size", Some(value)) => {
+ let value = value
+ .parse()
+ .map_err(|e| format!("Invalid allocation_unit_size value: {}", e))?;
+ Ok(MountOption::AllocationUnitSize(value))
+ }
+ #[cfg(windows)]
+ ("allocation_unit_size", None) => {
+ Err("allocation_unit_size requires a value".to_string())
+ }
+ #[cfg(windows)]
+ ("sector_size", Some(value)) => {
+ let value = value
+ .parse()
+ .map_err(|e| format!("Invalid sector_size value: {}", e))?;
+ Ok(MountOption::SectorSize(value))
+ }
+ #[cfg(windows)]
+ ("sector_size", None) => Err("sector_size requires a value".to_string()),
+ _ => Err(format!("Unknown mount option: {}", s)),
+ }
+ }
+}