Skip to content

Commit

Permalink
Add ioctl example
Browse files Browse the repository at this point in the history
  • Loading branch information
colinmarc authored and cberner committed Sep 7, 2024
1 parent 99aa528 commit 7f338a9
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 1 deletion.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ clap = { version = "4.4", features = ["cargo", "derive"] }
bincode = "1.3.1"
serde = { version = "1.0.102", features = ["std", "derive"] }
tempfile = "3.10.1"
nix = { version = "0.28.0", features = ["poll", "fs"] }
nix = { version = "0.28.0", features = ["poll", "fs", "ioctl"] }

[build-dependencies]
pkg-config = { version = "0.3.14", optional = true }
Expand Down Expand Up @@ -77,3 +77,7 @@ required-features = ["abi-7-12"]
[[example]]
name = "notify_inval_inode"
required-features = ["abi-7-12"]

[[example]]
name = "ioctl"
required-features = ["abi-7-11"]
209 changes: 209 additions & 0 deletions examples/ioctl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// This example requires fuse 7.11 or later. Run with:
//
// cargo run --example ioctl --features abi-7-11 /tmp/foobar

use clap::{crate_version, Arg, ArgAction, Command};
use fuser::{
FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry,
Request,
};
use libc::{EINVAL, ENOENT};
use log::debug;
use std::ffi::OsStr;
use std::time::{Duration, UNIX_EPOCH};

const TTL: Duration = Duration::from_secs(1); // 1 second

struct FiocFS {
content: Vec<u8>,
root_attr: FileAttr,
fioc_file_attr: FileAttr,
}

impl FiocFS {
fn new() -> Self {
let uid = unsafe { libc::getuid() };
let gid = unsafe { libc::getgid() };

let root_attr = FileAttr {
ino: 1,
size: 0,
blocks: 0,
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
mtime: UNIX_EPOCH,
ctime: UNIX_EPOCH,
crtime: UNIX_EPOCH,
kind: FileType::Directory,
perm: 0o755,
nlink: 2,
uid,
gid,
rdev: 0,
flags: 0,
blksize: 512,
};

let fioc_file_attr = FileAttr {
ino: 2,
size: 0,
blocks: 1,
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
mtime: UNIX_EPOCH,
ctime: UNIX_EPOCH,
crtime: UNIX_EPOCH,
kind: FileType::RegularFile,
perm: 0o644,
nlink: 1,
uid,
gid,
rdev: 0,
flags: 0,
blksize: 512,
};

Self {
content: vec![],
root_attr,
fioc_file_attr,
}
}
}

impl Filesystem for FiocFS {
fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
if parent == 1 && name.to_str() == Some("fioc") {
reply.entry(&TTL, &self.fioc_file_attr, 0);
} else {
reply.error(ENOENT);
}
}

fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option<u64>, reply: ReplyAttr) {
match ino {
1 => reply.attr(&TTL, &self.root_attr),
2 => reply.attr(&TTL, &self.fioc_file_attr),
_ => reply.error(ENOENT),
}
}

fn read(
&mut self,
_req: &Request,
ino: u64,
_fh: u64,
offset: i64,
_size: u32,
_flags: i32,
_lock: Option<u64>,
reply: ReplyData,
) {
if ino == 2 {
reply.data(&self.content[offset as usize..])
} else {
reply.error(ENOENT);
}
}

fn readdir(
&mut self,
_req: &Request,
ino: u64,
_fh: u64,
offset: i64,
mut reply: ReplyDirectory,
) {
if ino != 1 {
reply.error(ENOENT);
return;
}

let entries = vec![
(1, FileType::Directory, "."),
(1, FileType::Directory, ".."),
(2, FileType::RegularFile, "fioc"),
];

for (i, entry) in entries.into_iter().enumerate().skip(offset as usize) {
// i + 1 means the index of the next entry
if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) {
break;
}
}
reply.ok();
}

fn ioctl(
&mut self,
_req: &Request<'_>,
ino: u64,
_fh: u64,
_flags: u32,
cmd: u32,
in_data: &[u8],
_out_size: u32,
reply: fuser::ReplyIoctl,
) {
if ino != 2 {
reply.error(EINVAL);
return;
}

const FIOC_GET_SIZE: u64 = nix::request_code_read!('E', 0, std::mem::size_of::<usize>());
const FIOC_SET_SIZE: u64 = nix::request_code_write!('E', 1, std::mem::size_of::<usize>());

match cmd.into() {
FIOC_GET_SIZE => {
let size_bytes = self.content.len().to_ne_bytes();
reply.ioctl(0, &size_bytes);
}
FIOC_SET_SIZE => {
let new_size = usize::from_ne_bytes(in_data.try_into().unwrap());
self.content = vec![0_u8; new_size];
reply.ioctl(0, &[]);
}
_ => {
debug!("unknown ioctl: {}", cmd);
reply.error(EINVAL);
}
}
}
}

fn main() {
let matches = Command::new("hello")
.version(crate_version!())
.author("Colin Marc")
.arg(
Arg::new("MOUNT_POINT")
.required(true)
.index(1)
.help("Act as a client, and mount FUSE at given path"),
)
.arg(
Arg::new("auto_unmount")
.long("auto_unmount")
.action(ArgAction::SetTrue)
.help("Automatically unmount on process exit"),
)
.arg(
Arg::new("allow-root")
.long("allow-root")
.action(ArgAction::SetTrue)
.help("Allow root user to access filesystem"),
)
.get_matches();

env_logger::init();

let mountpoint = matches.get_one::<String>("MOUNT_POINT").unwrap();
let mut options = vec![MountOption::FSName("fioc".to_string())];
if matches.get_flag("auto_unmount") {
options.push(MountOption::AutoUnmount);
}
if matches.get_flag("allow-root") {
options.push(MountOption::AllowRoot);
}

let fs = FiocFS::new();
fuser::mount2(fs, mountpoint, &options).unwrap();
}

0 comments on commit 7f338a9

Please sign in to comment.