Skip to content

Commit

Permalink
Move FileAttr and FileType to ll module and improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
zargony committed Aug 18, 2019
1 parent 54c8405 commit 47c93e0
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 67 deletions.
8 changes: 6 additions & 2 deletions examples/hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ const HELLO_DIR_ATTR: FileAttr = FileAttr {
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
mtime: UNIX_EPOCH,
ctime: UNIX_EPOCH,
#[cfg(target_os = "macos")]
crtime: UNIX_EPOCH,
kind: FileType::Directory,
ftype: FileType::Directory,
perm: 0o755,
nlink: 2,
uid: 501,
gid: 20,
rdev: 0,
#[cfg(target_os = "macos")]
flags: 0,
};

Expand All @@ -32,13 +34,15 @@ const HELLO_TXT_ATTR: FileAttr = FileAttr {
atime: UNIX_EPOCH, // 1970-01-01 00:00:00
mtime: UNIX_EPOCH,
ctime: UNIX_EPOCH,
#[cfg(target_os = "macos")]
crtime: UNIX_EPOCH,
kind: FileType::RegularFile,
ftype: FileType::RegularFile,
perm: 0o644,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
#[cfg(target_os = "macos")]
flags: 0,
};

Expand Down
53 changes: 1 addition & 52 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use libc::{c_int, ENOSYS};

pub use fuse_abi::FUSE_ROOT_ID;
pub use fuse_abi::consts;
pub use ll::{FileAttr, FileAttrTryFromError, FileType, FileTypeTryFromError};
pub use reply::{Reply, ReplyEmpty, ReplyData, ReplyEntry, ReplyAttr, ReplyOpen};
pub use reply::{ReplyWrite, ReplyStatfs, ReplyCreate, ReplyLock, ReplyBmap, ReplyDirectory};
pub use reply::ReplyXattr;
Expand All @@ -29,58 +30,6 @@ mod reply;
mod request;
mod session;

/// File types
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FileType {
/// Named pipe (S_IFIFO)
NamedPipe,
/// Character device (S_IFCHR)
CharDevice,
/// Block device (S_IFBLK)
BlockDevice,
/// Directory (S_IFDIR)
Directory,
/// Regular file (S_IFREG)
RegularFile,
/// Symbolic link (S_IFLNK)
Symlink,
/// Unix domain socket (S_IFSOCK)
Socket,
}

/// File attributes
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileAttr {
/// Inode number
pub ino: u64,
/// Size in bytes
pub size: u64,
/// Size in blocks
pub blocks: u64,
/// Time of last access
pub atime: SystemTime,
/// Time of last modification
pub mtime: SystemTime,
/// Time of last change
pub ctime: SystemTime,
/// Time of creation (macOS only)
pub crtime: SystemTime,
/// Kind of file (directory, file, pipe, etc)
pub kind: FileType,
/// Permissions
pub perm: u16,
/// Number of hard links
pub nlink: u32,
/// User id
pub uid: u32,
/// Group id
pub gid: u32,
/// Rdev
pub rdev: u32,
/// Flags (macOS only, see chflags(2))
pub flags: u32,
}

/// Filesystem trait.
///
/// This trait must be implemented to provide a userspace filesystem via FUSE.
Expand Down
142 changes: 142 additions & 0 deletions src/ll/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Low-level filesystem attributes.

use std::convert::TryFrom;
use std::os::unix::fs::FileTypeExt;
use std::time::SystemTime;
use std::{error, fmt, fs};


/// Error type returned when a `FileAttr` conversion fails.
#[derive(Debug)]
pub struct FileAttrTryFromError;

impl fmt::Display for FileAttrTryFromError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Could not convert invalid file attributes")
}
}

impl error::Error for FileAttrTryFromError {}


/// File attributes.
///
/// Holds metadata required to represent a file in a filesystem. Besides the
/// inode number, which uniquely identifies a file, attributes contain more
/// useful metadata like file size, ownership information and permissions that
/// users and query and act upon.
///
/// This is the filesystem side representation of file metadata. On the user
/// side, Rust abstracts this information in `std::fs::Metadata`.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileAttr {
/// Inode number.
pub ino: u64,
/// Size in bytes.
pub size: u64,
/// Size in blocks.
pub blocks: u64,
/// Time of last access.
pub atime: SystemTime,
/// Time of last modification.
pub mtime: SystemTime,
/// Time of last change.
pub ctime: SystemTime,
/// macOS only: Time of creation.
#[cfg(target_os = "macos")]
pub crtime: SystemTime,
/// Type of the file (e.g. regular file, directory, pipe, etc).
pub ftype: FileType,
/// File permissions.
pub perm: u16,
/// Number of hard links.
pub nlink: u32,
/// User id of file owner.
pub uid: u32,
/// Group id of file owner.
pub gid: u32,
/// Rdev.
pub rdev: u32,
/// macOS only: Flags (see chflags(2)).
#[cfg(target_os = "macos")]
pub flags: u32,
}

// TODO: Convert `std::fs::Metadata` to `FileAttr` if ever possible


/// Error type returned when a `FileType` conversion fails.
#[derive(Debug)]
pub struct FileTypeTryFromError;

impl fmt::Display for FileTypeTryFromError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Could not convert unknown file type")
}
}

impl error::Error for FileTypeTryFromError {}


/// File type.
///
/// Determines the type of a file (e.g. wether it's a regular file or a
/// symlink).
///
/// This is the filesystem side representation of the type of a file. On the
/// user side, Rust abstracts this information in `std::fs::FileType`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum FileType {
/// Named pipe (FIFO).
///
/// Also known as `S_IFIFO` in libc.
NamedPipe,
/// Character device.
///
/// Also known as `S_IFCHR` in libc.
CharDevice,
/// Directory.
///
/// Also known as `S_IFDIR` in libc.
Directory,
/// Block device.
///
/// Also known as `S_IFBLK` in libc.
BlockDevice,
/// Regular file.
///
/// Also known as `S_IFREG` in libc.
RegularFile,
/// Symbolic link.
///
/// Also known as `S_IFLNK` in libc.
Symlink,
/// Unix domain socket.
///
/// Also known as `S_IFSOCK` in libc.
Socket,
}

impl TryFrom<fs::FileType> for FileType {
type Error = FileTypeTryFromError;

fn try_from(ft: fs::FileType) -> Result<Self, Self::Error> {
if ft.is_fifo() {
Ok(FileType::NamedPipe)
} else if ft.is_char_device() {
Ok(FileType::CharDevice)
} else if ft.is_dir() {
Ok(FileType::Directory)
} else if ft.is_block_device() {
Ok(FileType::BlockDevice)
} else if ft.is_file() {
Ok(FileType::RegularFile)
} else if ft.is_symlink() {
Ok(FileType::Symlink)
} else if ft.is_socket() {
Ok(FileType::Socket)
} else {
Err(FileTypeTryFromError)
}
}
}
3 changes: 3 additions & 0 deletions src/ll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@

mod argument;

mod attr;
pub use attr::{FileAttr, FileAttrTryFromError, FileType, FileTypeTryFromError};

mod request;
pub use request::{Operation, Request, RequestError};
74 changes: 61 additions & 13 deletions src/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ fn time_from_system_time(system_time: &SystemTime) -> Result<(u64, u32), SystemT
// Some platforms like Linux x86_64 have mode_t = u32, and lint warns of a trivial_numeric_casts.
// But others like macOS x86_64 have mode_t = u16, requiring a typecast. So, just silence lint.
#[allow(trivial_numeric_casts)]
/// Returns the mode for a given file kind and permission
fn mode_from_kind_and_perm(kind: FileType, perm: u16) -> u32 {
(match kind {
/// Returns the mode for a given file type and permission
fn mode_from_type_and_perm(file_type: FileType, perm: u16) -> u32 {
(match file_type {
FileType::NamedPipe => S_IFIFO,
FileType::CharDevice => S_IFCHR,
FileType::BlockDevice => S_IFBLK,
Expand Down Expand Up @@ -97,7 +97,7 @@ fn fuse_attr_from_attr(attr: &FileAttr) -> fuse_attr {
mtimensec: mtime_nanos,
ctimensec: ctime_nanos,
crtimensec: crtime_nanos,
mode: mode_from_kind_and_perm(attr.kind, attr.perm),
mode: mode_from_type_and_perm(attr.ftype, attr.perm),
nlink: attr.nlink,
uid: attr.uid,
gid: attr.gid,
Expand All @@ -124,7 +124,7 @@ fn fuse_attr_from_attr(attr: &FileAttr) -> fuse_attr {
atimensec: atime_nanos,
mtimensec: mtime_nanos,
ctimensec: ctime_nanos,
mode: mode_from_kind_and_perm(attr.kind, attr.perm),
mode: mode_from_type_and_perm(attr.ftype, attr.perm),
nlink: attr.nlink,
uid: attr.uid,
gid: attr.gid,
Expand Down Expand Up @@ -566,7 +566,7 @@ impl ReplyDirectory {
/// Add an entry to the directory reply buffer. Returns true if the buffer is full.
/// A transparent offset value can be provided for each entry. The kernel uses these
/// value to request the next entries in further readdir calls
pub fn add<T: AsRef<OsStr>>(&mut self, ino: u64, offset: i64, kind: FileType, name: T) -> bool {
pub fn add<T: AsRef<OsStr>>(&mut self, ino: u64, offset: i64, file_type: FileType, name: T) -> bool {
let name = name.as_ref().as_bytes();
let entlen = mem::size_of::<fuse_dirent>() + name.len();
let entsize = (entlen + mem::size_of::<u64>() - 1) & !(mem::size_of::<u64>() - 1); // 64bit align
Expand All @@ -578,7 +578,7 @@ impl ReplyDirectory {
(*pdirent).ino = ino;
(*pdirent).off = offset as u64;
(*pdirent).namelen = name.len() as u32;
(*pdirent).typ = mode_from_kind_and_perm(kind, 0) >> 12;
(*pdirent).typ = mode_from_type_and_perm(file_type, 0) >> 12;
let p = p.offset(mem::size_of_val(&*pdirent) as isize);
ptr::copy_nonoverlapping(name.as_ptr(), p, name.len());
let p = p.offset(name.len() as isize);
Expand Down Expand Up @@ -774,8 +774,24 @@ mod test {
let reply: ReplyEntry = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr { ino: 0x11, size: 0x22, blocks: 0x33, atime: time, mtime: time, ctime: time, crtime: time,
kind: FileType::RegularFile, perm: 0o644, nlink: 0x55, uid: 0x66, gid: 0x77, rdev: 0x88, flags: 0x99 };
let attr = FileAttr {
ino: 0x11,
size: 0x22,
blocks: 0x33,
atime: time,
mtime: time,
ctime: time,
#[cfg(target_os = "macos")]
crtime: time,
ftype: FileType::RegularFile,
perm: 0o644,
nlink: 0x55,
uid: 0x66,
gid: 0x77,
rdev: 0x88,
#[cfg(target_os = "macos")]
flags: 0x99,
};
reply.entry(&ttl, &attr, 0xaa);
}

Expand Down Expand Up @@ -808,8 +824,24 @@ mod test {
let reply: ReplyAttr = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr { ino: 0x11, size: 0x22, blocks: 0x33, atime: time, mtime: time, ctime: time, crtime: time,
kind: FileType::RegularFile, perm: 0o644, nlink: 0x55, uid: 0x66, gid: 0x77, rdev: 0x88, flags: 0x99 };
let attr = FileAttr {
ino: 0x11,
size: 0x22,
blocks: 0x33,
atime: time,
mtime: time,
ctime: time,
#[cfg(target_os = "macos")]
crtime: time,
ftype: FileType::RegularFile,
perm: 0o644,
nlink: 0x55,
uid: 0x66,
gid: 0x77,
rdev: 0x88,
#[cfg(target_os = "macos")]
flags: 0x99,
};
reply.attr(&ttl, &attr);
}

Expand Down Expand Up @@ -903,8 +935,24 @@ mod test {
let reply: ReplyCreate = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr { ino: 0x11, size: 0x22, blocks: 0x33, atime: time, mtime: time, ctime: time, crtime: time,
kind: FileType::RegularFile, perm: 0o644, nlink: 0x55, uid: 0x66, gid: 0x77, rdev: 0x88, flags: 0x99 };
let attr = FileAttr {
ino: 0x11,
size: 0x22,
blocks: 0x33,
atime: time,
mtime: time,
ctime: time,
#[cfg(target_os = "macos")]
crtime: time,
ftype: FileType::RegularFile,
perm: 0o644,
nlink: 0x55,
uid: 0x66,
gid: 0x77,
rdev: 0x88,
#[cfg(target_os = "macos")]
flags: 0x99,
};
reply.created(&ttl, &attr, 0xaa, 0xbb, 0xcc);
}

Expand Down

0 comments on commit 47c93e0

Please sign in to comment.