Skip to content

Commit b4231bc

Browse files
committed
1 parent 99aa528 commit b4231bc

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

examples/ioctl.rs

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
use clap::{crate_version, Arg, ArgAction, Command};
2+
use fuser::{
3+
FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry,
4+
Request,
5+
};
6+
use libc::{EINVAL, ENOENT};
7+
use std::ffi::OsStr;
8+
use std::time::{Duration, UNIX_EPOCH};
9+
10+
const TTL: Duration = Duration::from_secs(1); // 1 second
11+
12+
struct FiocFS {
13+
content: Vec<u8>,
14+
root_attr: FileAttr,
15+
fioc_file_attr: FileAttr,
16+
}
17+
18+
impl FiocFS {
19+
fn new() -> Self {
20+
let uid = unsafe { libc::getuid() };
21+
let gid = unsafe { libc::getgid() };
22+
23+
let root_attr = FileAttr {
24+
ino: 1,
25+
size: 0,
26+
blocks: 0,
27+
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
28+
mtime: UNIX_EPOCH,
29+
ctime: UNIX_EPOCH,
30+
crtime: UNIX_EPOCH,
31+
kind: FileType::Directory,
32+
perm: 0o755,
33+
nlink: 2,
34+
uid,
35+
gid,
36+
rdev: 0,
37+
flags: 0,
38+
blksize: 512,
39+
};
40+
41+
let fioc_file_attr = FileAttr {
42+
ino: 2,
43+
size: 0,
44+
blocks: 1,
45+
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
46+
mtime: UNIX_EPOCH,
47+
ctime: UNIX_EPOCH,
48+
crtime: UNIX_EPOCH,
49+
kind: FileType::RegularFile,
50+
perm: 0o644,
51+
nlink: 1,
52+
uid,
53+
gid,
54+
rdev: 0,
55+
flags: 0,
56+
blksize: 512,
57+
};
58+
59+
Self {
60+
content: vec![],
61+
root_attr,
62+
fioc_file_attr,
63+
}
64+
}
65+
}
66+
67+
impl Filesystem for FiocFS {
68+
fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
69+
if parent == 1 && name.to_str() == Some("fioc") {
70+
reply.entry(&TTL, &self.fioc_file_attr, 0);
71+
} else {
72+
reply.error(ENOENT);
73+
}
74+
}
75+
76+
fn getattr(&mut self, _req: &Request, ino: u64, _fh: Option<u64>, reply: ReplyAttr) {
77+
match ino {
78+
1 => reply.attr(&TTL, &self.root_attr),
79+
2 => reply.attr(&TTL, &self.fioc_file_attr),
80+
_ => reply.error(ENOENT),
81+
}
82+
}
83+
84+
fn read(
85+
&mut self,
86+
_req: &Request,
87+
ino: u64,
88+
_fh: u64,
89+
offset: i64,
90+
_size: u32,
91+
_flags: i32,
92+
_lock: Option<u64>,
93+
reply: ReplyData,
94+
) {
95+
if ino == 2 {
96+
reply.data(&self.content[offset as usize..])
97+
} else {
98+
reply.error(ENOENT);
99+
}
100+
}
101+
102+
fn readdir(
103+
&mut self,
104+
_req: &Request,
105+
ino: u64,
106+
_fh: u64,
107+
offset: i64,
108+
mut reply: ReplyDirectory,
109+
) {
110+
if ino != 1 {
111+
reply.error(ENOENT);
112+
return;
113+
}
114+
115+
let entries = vec![
116+
(1, FileType::Directory, "."),
117+
(1, FileType::Directory, ".."),
118+
(2, FileType::RegularFile, "fioc"),
119+
];
120+
121+
for (i, entry) in entries.into_iter().enumerate().skip(offset as usize) {
122+
// i + 1 means the index of the next entry
123+
if reply.add(entry.0, (i + 1) as i64, entry.1, entry.2) {
124+
break;
125+
}
126+
}
127+
reply.ok();
128+
}
129+
130+
fn ioctl(
131+
&mut self,
132+
_req: &Request<'_>,
133+
ino: u64,
134+
_fh: u64,
135+
_flags: u32,
136+
cmd: u32,
137+
in_data: &[u8],
138+
_out_size: u32,
139+
reply: fuser::ReplyIoctl,
140+
) {
141+
if ino != 2 {
142+
reply.error(EINVAL);
143+
return;
144+
}
145+
146+
const FIOC_GET_SIZE: u32 = 0;
147+
const FIOC_SET_SIZE: u32 = 1;
148+
149+
match cmd {
150+
FIOC_GET_SIZE => {
151+
let size_bytes = (self.content.len() as u32).to_ne_bytes();
152+
reply.ioctl(0, &size_bytes);
153+
}
154+
FIOC_SET_SIZE => {
155+
let new_size = u32::from_ne_bytes(in_data.try_into().unwrap());
156+
self.content = vec![0_u8; new_size as usize];
157+
reply.ioctl(0, &[]);
158+
}
159+
_ => reply.error(EINVAL),
160+
}
161+
}
162+
}
163+
164+
fn main() {
165+
let matches = Command::new("hello")
166+
.version(crate_version!())
167+
.author("Colin Marc")
168+
.arg(
169+
Arg::new("MOUNT_POINT")
170+
.required(true)
171+
.index(1)
172+
.help("Act as a client, and mount FUSE at given path"),
173+
)
174+
.arg(
175+
Arg::new("auto_unmount")
176+
.long("auto_unmount")
177+
.action(ArgAction::SetTrue)
178+
.help("Automatically unmount on process exit"),
179+
)
180+
.arg(
181+
Arg::new("allow-root")
182+
.long("allow-root")
183+
.action(ArgAction::SetTrue)
184+
.help("Allow root user to access filesystem"),
185+
)
186+
.get_matches();
187+
188+
env_logger::init();
189+
190+
let mountpoint = matches.get_one::<String>("MOUNT_POINT").unwrap();
191+
let mut options = vec![MountOption::FSName("fioc".to_string())];
192+
if matches.get_flag("auto_unmount") {
193+
options.push(MountOption::AutoUnmount);
194+
}
195+
if matches.get_flag("allow-root") {
196+
options.push(MountOption::AllowRoot);
197+
}
198+
199+
let fs = FiocFS::new();
200+
fuser::mount2(fs, mountpoint, &options).unwrap();
201+
}

0 commit comments

Comments
 (0)