1use std::path::PathBuf;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use anyhow::Context;
6use clippy_utilities::Cast;
7use nix::errno::Errno;
8use nix::fcntl::OFlag;
9use nix::sys::stat::{Mode, SFlag};
10use serde::{Deserialize, Serialize};
11use tracing::debug;
12
13use crate::protocol::FuseAttr;
14use crate::error::{AsyncFusexError, AsyncFusexResult};
15use crate::util;
16
17pub fn build_error_result_from_errno<T>(error_code: Errno, err_msg: String) -> AsyncFusexResult<T> {
22 Err(AsyncFusexError::from(
23 anyhow::Error::new(error_code).context(err_msg),
24 ))
25}
26
27pub const ROOT_ID: u64 = 1;
29
30pub type INum = u64;
32
33#[derive(Debug)]
35pub struct StatFsParam {
36 pub blocks: u64,
38 pub bfree: u64,
40 pub bavail: u64,
42 pub files: u64,
44 pub f_free: u64,
46 pub bsize: u32,
48 pub namelen: u32,
50 pub frsize: u32,
52}
53
54#[derive(Debug)]
56pub struct SetAttrParam {
57 pub valid: u32,
59 pub fh: Option<u64>,
61 pub mode: Option<u32>,
63 pub u_id: Option<u32>,
65 pub g_id: Option<u32>,
67 pub size: Option<u64>,
69 #[cfg(feature = "abi-7-9")]
71 pub lock_owner: Option<u64>,
72 pub a_time: Option<SystemTime>,
74 pub m_time: Option<SystemTime>,
76 #[cfg(feature = "abi-7-23")]
78 pub c_time: Option<SystemTime>,
79}
80
81#[derive(Debug)]
83pub struct CreateParam {
84 pub parent: INum,
86 pub name: String,
88 pub mode: u32,
90 pub rdev: u32,
92 pub uid: u32,
94 pub gid: u32,
96 pub node_type: SFlag,
98 pub link: Option<PathBuf>,
100}
101
102#[derive(Serialize, Deserialize, Debug)]
104pub struct RenameParam {
105 pub old_parent: INum,
107 pub old_name: String,
109 pub new_parent: INum,
111 pub new_name: String,
113 pub flags: u32,
115}
116
117#[derive(Debug)]
119pub struct FileLockParam {
120 pub fh: u64,
122 pub lock_owner: u64,
124 pub start: u64,
126 pub end: u64,
128 pub typ: u32,
130 pub pid: u32,
132}
133
134#[derive(Copy, Clone, Debug)]
136pub struct FileAttr {
137 pub ino: INum,
139 pub size: u64,
141 pub blocks: u64,
143 pub atime: SystemTime,
145 pub mtime: SystemTime,
147 pub ctime: SystemTime,
149 pub kind: SFlag,
151 pub perm: u16,
153 pub nlink: u32,
155 pub uid: u32,
157 pub gid: u32,
159 pub rdev: u32,
161 pub version: u64,
163}
164
165pub const NEED_CHECK_PERM: bool = false;
170
171impl FileAttr {
172 #[allow(dead_code)]
174 pub(crate) fn now() -> Self {
175 let now = SystemTime::now();
176 Self {
177 ino: 0,
178 size: 4096,
179 blocks: 8,
180 atime: now,
181 mtime: now,
182 ctime: now,
183 kind: SFlag::S_IFREG,
184 perm: 0o775,
185 nlink: 0,
186 uid: 0,
187 gid: 0,
188 rdev: 0,
189 version: 0,
190 }
191 }
192
193 #[allow(dead_code)]
195 pub(crate) fn setattr_precheck(
196 &self,
197 param: &SetAttrParam,
198 context_uid: u32,
199 context_gid: u32,
200 ) -> AsyncFusexResult<Option<FileAttr>> {
201 let cur_attr = *self;
202 let mut dirty_attr = cur_attr;
203
204 let st_now = SystemTime::now();
205 let mut attr_changed = false;
206
207 let check_permission = || -> AsyncFusexResult<()> {
208 if NEED_CHECK_PERM {
209 if cur_attr.uid == 0 && context_uid != 0 {
211 return build_error_result_from_errno(
212 Errno::EPERM,
213 "setattr() cannot change atime".to_owned(),
214 );
215 }
216 cur_attr.check_perm(context_uid, context_gid, 2)?;
217 if context_uid != cur_attr.uid {
218 return build_error_result_from_errno(
219 Errno::EACCES,
220 "setattr() cannot change atime".to_owned(),
221 );
222 }
223 Ok(())
224 } else {
225 Ok(())
227 }
228 };
229
230 if let Some(gid) = param.g_id {
231 if context_uid != 0 && cur_attr.uid != context_uid {
232 return build_error_result_from_errno(
233 Errno::EPERM,
234 "setattr() cannot change gid".to_owned(),
235 );
236 }
237
238 if cur_attr.gid != gid {
239 dirty_attr.gid = gid;
240 attr_changed = true;
241 }
242 }
243
244 if let Some(uid) = param.u_id {
245 if cur_attr.uid != uid {
246 if context_uid != 0 {
247 return build_error_result_from_errno(
248 Errno::EPERM,
249 "setattr() cannot change uid".to_owned(),
250 );
251 }
252 dirty_attr.uid = uid;
253 attr_changed = true;
254 }
255 }
256
257 if let Some(mode) = param.mode {
258 let mode: u16 = mode.cast();
259 if mode != cur_attr.perm {
260 if context_uid != 0 && context_uid != cur_attr.uid {
261 return build_error_result_from_errno(
262 Errno::EPERM,
263 "setattr() cannot change mode".to_owned(),
264 );
265 }
266 dirty_attr.perm = mode;
267 attr_changed = true;
268 }
269 }
270
271 if let Some(atime) = param.a_time {
272 check_permission()?;
273 if atime != cur_attr.atime {
274 dirty_attr.atime = atime;
275 attr_changed = true;
276 }
277 }
278
279 if let Some(mtime) = param.m_time {
280 check_permission()?;
281 if mtime != cur_attr.mtime {
282 dirty_attr.mtime = mtime;
283 attr_changed = true;
284 }
285 }
286
287 if let Some(file_size) = param.size {
288 dirty_attr.size = file_size;
289 dirty_attr.mtime = st_now;
290 attr_changed = true;
291 }
292
293 if attr_changed {
294 dirty_attr.ctime = st_now;
295 }
296
297 #[cfg(feature = "abi-7-23")]
300 if let Some(ctime) = param.c_time {
301 check_permission()?;
302 if ctime != cur_attr.ctime {
303 dirty_attr.ctime = ctime;
304 attr_changed = true;
305 }
306 }
307
308 Ok(attr_changed.then_some(dirty_attr))
309 }
310
311 pub fn check_perm(&self, uid: u32, gid: u32, access_mode: u8) -> AsyncFusexResult<()> {
328 if NEED_CHECK_PERM {
329 self.check_perm_inner(uid, gid, access_mode)
330 } else {
331 Ok(())
332 }
333 }
334
335 #[inline]
338 fn check_perm_inner(&self, uid: u32, gid: u32, access_mode: u8) -> AsyncFusexResult<()> {
339 debug_assert!(
340 access_mode <= 0o7 && access_mode != 0,
341 "check_perm() found access_mode={access_mode} invalid",
342 );
343 if uid == 0 {
344 return Ok(());
345 }
346
347 let file_mode = self.get_access_mode(uid, gid);
348 debug!(
349 "check_perm() got access_mode={access_mode} and file_mode={file_mode} \
350 from uid={uid} gid={gid}",
351 );
352 if (file_mode & access_mode) != access_mode {
353 return build_error_result_from_errno(
354 Errno::EACCES,
355 format!("check_perm() failed {uid} {gid} {file_mode}"),
356 );
357 }
358 Ok(())
359 }
360
361 #[allow(clippy::default_numeric_fallback)]
363 #[allow(clippy::arithmetic_side_effects)]
364 fn get_access_mode(&self, uid: u32, gid: u32) -> u8 {
365 let perm = self.perm;
366 let mode = if uid == self.uid {
367 (perm >> 6) & 0o7
368 } else if gid == self.gid {
369 (perm >> 3) & 0o7
370 } else {
371 perm & 0o7
372 };
373 mode.cast()
374 }
375}
376
377impl Default for FileAttr {
378 fn default() -> Self {
379 Self {
380 ino: 0,
381 size: 4096,
382 blocks: 8,
383 atime: SystemTime::UNIX_EPOCH,
384 mtime: SystemTime::UNIX_EPOCH,
385 ctime: SystemTime::UNIX_EPOCH,
386 kind: SFlag::S_IFREG,
387 perm: 0o775,
388 nlink: 0,
389 uid: 0,
390 gid: 0,
391 rdev: 0,
392 version: 0,
393 }
394 }
395}
396
397pub fn parse_oflag(flags: u32) -> OFlag {
399 debug_assert!(
400 flags < std::i32::MAX.cast::<u32>(),
401 "helper_parse_oflag() found flags={flags} overflow, larger than u16::MAX",
402 );
403 let o_flags = OFlag::from_bits_truncate(flags.cast());
404 debug!("helper_parse_oflag() read file flags={:?}", o_flags);
405 o_flags
406}
407
408pub fn parse_mode(mode: u32) -> Mode {
410 debug_assert!(
411 mode < std::u16::MAX.cast::<u32>(),
412 "helper_parse_mode() found mode={mode} overflow, larger than u16::MAX",
413 );
414
415 #[cfg(target_os = "linux")]
416 let file_mode = Mode::from_bits_truncate(mode);
417 debug!("parse_mode() read mode={:?}", file_mode);
418 file_mode
419}
420
421#[must_use]
423pub fn parse_mode_bits(mode: u32) -> u16 {
424 #[cfg(target_os = "linux")]
425 let bits = parse_mode(mode).bits().cast();
426
427 bits
428}
429
430#[must_use]
432pub fn time_from_system_time(system_time: &SystemTime) -> (u64, u32) {
433 let duration = system_time
434 .duration_since(UNIX_EPOCH)
435 .context(format!(
436 "failed to convert SystemTime={system_time:?} to Duration"
437 ))
438 .unwrap_or_else(|e| {
439 panic!(
440 "time_from_system_time() failed to convert SystemTime={:?} \
441 to timestamp(seconds, nano-seconds), the error is: {}",
442 system_time,
443 util::format_anyhow_error(&e),
444 )
445 });
446 (duration.as_secs(), duration.subsec_nanos())
447}
448
449#[must_use]
451pub fn convert_to_fuse_attr(attr: FileAttr) -> FuseAttr {
452 let (a_time_secs, a_time_nanos) = time_from_system_time(&attr.atime);
453 let (m_time_secs, m_time_nanos) = time_from_system_time(&attr.mtime);
454 let (c_time_secs, c_time_nanos) = time_from_system_time(&attr.ctime);
455
456 FuseAttr {
457 ino: attr.ino,
458 size: attr.size,
459 blocks: attr.blocks,
460 atime: a_time_secs,
461 mtime: m_time_secs,
462 ctime: c_time_secs,
463 atimensec: a_time_nanos,
464 mtimensec: m_time_nanos,
465 ctimensec: c_time_nanos,
466 mode: crate::util::mode_from_kind_and_perm(attr.kind, attr.perm),
467 nlink: attr.nlink,
468 uid: attr.uid,
469 gid: attr.gid,
470 rdev: attr.rdev,
471 #[cfg(feature = "abi-7-9")]
472 blksize: 0, #[cfg(feature = "abi-7-9")]
474 padding: 0,
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481
482 #[test]
483 #[allow(clippy::assertions_on_result_states)]
484 fn test_permission_check() {
485 let file = FileAttr {
486 ino: 0,
487 size: 0,
488 blocks: 0,
489 atime: SystemTime::now(),
490 mtime: SystemTime::now(),
491 ctime: SystemTime::now(),
492 kind: SFlag::S_IFREG,
493 perm: 0o741,
494 nlink: 0,
495 uid: 1000,
496 gid: 1000,
497 rdev: 0,
498 version: 0,
499 };
500
501 assert!(file.check_perm_inner(1000, 1001, 7).is_ok());
503 assert!(file.check_perm_inner(1000, 1001, 6).is_ok());
504 assert!(file.check_perm_inner(1000, 1001, 5).is_ok());
505 assert!(file.check_perm_inner(1000, 1001, 4).is_ok());
506 assert!(file.check_perm_inner(1000, 1001, 3).is_ok());
507 assert!(file.check_perm_inner(1000, 1001, 2).is_ok());
508 assert!(file.check_perm_inner(1000, 1001, 1).is_ok());
509
510 assert!(file.check_perm_inner(1001, 1000, 7).is_err());
512 assert!(file.check_perm_inner(1001, 1000, 6).is_err());
513 assert!(file.check_perm_inner(1001, 1000, 5).is_err());
514 assert!(file.check_perm_inner(1001, 1000, 4).is_ok());
515 assert!(file.check_perm_inner(1001, 1000, 3).is_err());
516 assert!(file.check_perm_inner(1001, 1000, 2).is_err());
517 assert!(file.check_perm_inner(1001, 1000, 1).is_err());
518
519 assert!(file.check_perm_inner(1002, 1002, 7).is_err());
521 assert!(file.check_perm_inner(1002, 1002, 6).is_err());
522 assert!(file.check_perm_inner(1002, 1002, 5).is_err());
523 assert!(file.check_perm_inner(1002, 1002, 4).is_err());
524 assert!(file.check_perm_inner(1002, 1002, 3).is_err());
525 assert!(file.check_perm_inner(1002, 1002, 2).is_err());
526 assert!(file.check_perm_inner(1002, 1002, 1).is_ok());
527 }
528}