distinguish between file permissions and file mode

This commit is contained in:
Ashwin Naren
2026-03-02 11:49:58 -08:00
parent d164e06558
commit edca499714
16 changed files with 80 additions and 31 deletions

View File

@@ -41,6 +41,28 @@ bitflags! {
const S_ISGID = 0x0400;
const S_ISUID = 0x0800;
}
}
bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct FileMode: u16 {
const S_IXOTH = 0x0001;
const S_IWOTH = 0x0002;
const S_IROTH = 0x0004;
const S_IXGRP = 0x0008;
const S_IWGRP = 0x0010;
const S_IRGRP = 0x0020;
const S_IXUSR = 0x0040;
const S_IWUSR = 0x0080;
const S_IRUSR = 0x0100;
const S_ISVTX = 0x0200;
const S_ISGID = 0x0400;
const S_ISUID = 0x0800;
// Mutually-exclusive file types:
const S_IFIFO = 0x1000;
@@ -53,6 +75,28 @@ bitflags! {
}
}
impl From<FileMode> for FilePermissions {
fn from(mode: FileMode) -> Self {
FilePermissions::from_bits_truncate(mode.bits())
}
}
impl FileMode {
pub fn new(file_type: FileType, permissions: FilePermissions) -> Self {
let mut mode = FileMode::from_bits_truncate(permissions.bits());
mode |= match file_type {
FileType::Directory => FileMode::S_IFDIR,
FileType::File => FileMode::S_IFREG,
FileType::Symlink => FileMode::S_IFLNK,
FileType::BlockDevice(_) => FileMode::S_IFBLK,
FileType::CharDevice(_) => FileMode::S_IFCHR,
FileType::Fifo => FileMode::S_IFIFO,
FileType::Socket => FileMode::S_IFSOCK,
};
mode
}
}
/// Represents file metadata, similar to `stat`.
#[derive(Debug, Clone)]
pub struct FileAttr {
@@ -65,12 +109,18 @@ pub struct FileAttr {
pub mtime: Duration, // Modification time
pub ctime: Duration, // Change time
pub file_type: FileType,
pub mode: FilePermissions,
pub permissions: FilePermissions,
pub nlinks: u32,
pub uid: Uid,
pub gid: Gid,
}
impl FileAttr {
pub fn mode(&self) -> FileMode {
FileMode::new(self.file_type, self.permissions)
}
}
impl Default for FileAttr {
fn default() -> Self {
Self {
@@ -83,7 +133,7 @@ impl Default for FileAttr {
mtime: Duration::new(0, 0),
ctime: Duration::new(0, 0),
file_type: FileType::File,
mode: FilePermissions::empty(),
permissions: FilePermissions::empty(),
nlinks: 1,
uid: Uid::new_root(),
gid: Gid::new_root_group(),
@@ -116,7 +166,7 @@ impl FileAttr {
if uid.is_root() {
if requested_mode.contains(AccessMode::X_OK) {
// Root still needs at least one execute bit to be set for X_OK
if self.mode.intersects(
if self.permissions.intersects(
FilePermissions::S_IXUSR | FilePermissions::S_IXGRP | FilePermissions::S_IXOTH,
) {
return Ok(());
@@ -129,13 +179,13 @@ impl FileAttr {
// Determine which set of permission bits to use (owner, group, or other)
let perms_to_check = if self.uid == uid {
// User is the owner
self.mode
self.permissions
} else if self.gid == gid {
// User is in the file's group. Shift group bits to align with owner bits for easier checking.
FilePermissions::from_bits_truncate(self.mode.bits() << 3)
FilePermissions::from_bits_truncate(self.permissions.bits() << 3)
} else {
// Others. Shift other bits to align with owner bits.
FilePermissions::from_bits_truncate(self.mode.bits() << 6)
FilePermissions::from_bits_truncate(self.permissions.bits() << 6)
};
if requested_mode.contains(AccessMode::R_OK)
@@ -175,11 +225,11 @@ mod tests {
const OTHER_UID: Uid = Uid::new(1002);
const OTHER_GID: Gid = Gid::new(3000);
fn setup_file(mode: FilePermissions) -> FileAttr {
fn setup_file(permissions: FilePermissions) -> FileAttr {
FileAttr {
uid: OWNER_UID,
gid: FILE_GROUP_GID,
mode,
permissions,
..Default::default()
}
}

View File

@@ -96,8 +96,7 @@ impl From<Metadata> for FileAttr {
FileAttr {
size: meta.size_in_bytes,
file_type: meta.file_type.into(),
// Infallible, since they are identical
mode: FilePermissions::from_bits(meta.mode.bits()).unwrap(),
permissions: FilePermissions::from_bits_truncate(meta.mode.bits()),
uid: Uid::new(meta.uid),
gid: Gid::new(meta.gid),
atime: meta.atime,

View File

@@ -221,7 +221,7 @@ impl<T: Fat32Operations> Fat32DirStream<T> {
let attr = FileAttr {
size: dir_entry.size as u64,
file_type,
mode: FilePermissions::from_bits_retain(0o755),
permissions: FilePermissions::from_bits_retain(0o755),
atime: fat_date_to_duration(dir_entry.adate),
mtime: fat_datetime_to_duration(dir_entry.mdate, dir_entry.mtime, 0),
ctime: fat_datetime_to_duration(

View File

@@ -126,14 +126,14 @@ where
G: PageAllocGetter<C>,
T: AddressTranslator<()>,
{
fn new(id: InodeId, mode: FilePermissions) -> Result<Self> {
fn new(id: InodeId, permissions: FilePermissions) -> Result<Self> {
Ok(Self {
id,
attr: SpinLockIrq::new(FileAttr {
file_type: FileType::File,
size: 0,
nlinks: 1,
mode,
permissions,
..Default::default()
}),
inner: SpinLockIrq::new(TmpFsRegInner {
@@ -666,14 +666,14 @@ where
G: PageAllocGetter<C>,
T: AddressTranslator<()>,
{
pub fn new(id: u64, fs: Weak<TmpFs<C, G, T>>, mode: FilePermissions) -> Arc<Self> {
pub fn new(id: u64, fs: Weak<TmpFs<C, G, T>>, permissions: FilePermissions) -> Arc<Self> {
Arc::new_cyclic(|weak_this| Self {
entries: SpinLockIrq::new(Vec::new()),
attrs: SpinLockIrq::new(FileAttr {
size: 0,
file_type: FileType::Directory,
block_size: BLOCK_SZ as _,
mode,
permissions,
..Default::default()
}),
id,

View File

@@ -28,7 +28,7 @@ impl DevFs {
id: InodeId::from_fsid_and_inodeid(DEVFS_ID, 0),
attr: SpinLock::new(FileAttr {
file_type: FileType::Directory,
mode: FilePermissions::from_bits_retain(0o755),
permissions: FilePermissions::from_bits_retain(0o755),
..FileAttr::default()
}),
kind: InodeKind::Directory(SpinLock::new(BTreeMap::new())),
@@ -44,7 +44,7 @@ impl DevFs {
&self,
name: String,
device_id: CharDevDescriptor,
mode: FilePermissions,
permissions: FilePermissions,
) -> Result<()> {
let InodeKind::Directory(ref children) = self.root.kind else {
// This should be impossible as the root is always a directory.
@@ -66,7 +66,7 @@ impl DevFs {
attr: SpinLock::new(FileAttr {
id,
file_type: FileType::CharDevice(device_id),
mode,
permissions,
..FileAttr::default()
}),
// This is the crucial part: we store the device handle.

View File

@@ -16,7 +16,7 @@ impl ProcCmdlineInode {
id,
attr: FileAttr {
file_type: libkernel::fs::FileType::File,
mode: libkernel::fs::attr::FilePermissions::from_bits_retain(0o444),
permissions: libkernel::fs::attr::FilePermissions::from_bits_retain(0o444),
..FileAttr::default()
},
}

View File

@@ -26,7 +26,7 @@ impl ProcRootInode {
id: InodeId::from_fsid_and_inodeid(PROCFS_ID, 0),
attr: FileAttr {
file_type: FileType::Directory,
mode: FilePermissions::from_bits_retain(0o555),
permissions: FilePermissions::from_bits_retain(0o555),
..FileAttr::default()
},
}

View File

@@ -31,7 +31,7 @@ impl ProcTaskInode {
id: inode_id,
attr: FileAttr {
file_type: FileType::Directory,
mode: FilePermissions::from_bits_retain(0o555),
permissions: FilePermissions::from_bits_retain(0o555),
..FileAttr::default()
},
desc,

View File

@@ -58,7 +58,7 @@ impl ProcTaskFileInode {
| TaskFileType::Stat => FileType::File,
TaskFileType::Cwd | TaskFileType::Root => FileType::Symlink,
},
mode: FilePermissions::from_bits_retain(0o444),
permissions: FilePermissions::from_bits_retain(0o444),
..FileAttr::default()
},
process_stats,

View File

@@ -507,7 +507,7 @@ impl VFS {
{
let creds = task.creds.lock_save_irq();
if attr.mode.contains(FilePermissions::S_ISVTX)
if attr.permissions.contains(FilePermissions::S_ISVTX)
&& attr.uid != creds.euid()
&& parent_attr.uid != creds.euid()
{

View File

@@ -62,7 +62,7 @@ impl Inode for PipeInode {
mtime: self.time,
ctime: self.time,
file_type: FileType::Fifo,
mode: FilePermissions::from_bits_retain(0o0600),
permissions: FilePermissions::from_bits_retain(0o0600),
nlinks: 1,
uid: self.uid,
gid: self.gid,

View File

@@ -37,7 +37,7 @@ pub async fn sys_fchmodat(dirfd: Fd, path: TUA<c_char>, mode: u16, flags: i32) -
return Err(KernelError::NotPermitted);
}
attr.mode = mode;
attr.permissions = mode;
node.setattr(attr).await?;
Ok(0)

View File

@@ -103,14 +103,14 @@ pub async fn sys_renameat2(
let creds = task.creds.lock_save_irq();
if (old_attr.mode.contains(FilePermissions::S_ISVTX)
if (old_attr.permissions.contains(FilePermissions::S_ISVTX)
&& old_attr.uid != creds.euid()
&& old_parent_attr.uid != creds.euid())
|| new_parent_attr.uid != creds.euid()
{
creds.caps().check_capable(CapabilitiesFlags::CAP_FOWNER)?;
} else if let Some(new_attr) = new_attr
&& new_attr.mode.contains(FilePermissions::S_ISVTX)
&& new_attr.permissions.contains(FilePermissions::S_ISVTX)
&& new_attr.uid != creds.euid()
{
creds.caps().check_capable(CapabilitiesFlags::CAP_FOWNER)?;

View File

@@ -45,7 +45,7 @@ impl From<FileAttr> for Stat {
Self {
st_dev: value.id.fs_id(),
st_ino: value.id.inode_id(),
st_mode: value.mode.bits() as u32 | u32::from(value.file_type),
st_mode: value.mode().bits() as u32 | u32::from(value.file_type),
st_nlink: value.nlinks,
st_uid: value.uid.into(),
st_gid: value.gid.into(),

View File

@@ -146,7 +146,7 @@ pub async fn sys_statx(
if mask.contains(StatXMask::STATX_MODE) {
stat_x.stx_mask |= StatXMask::STATX_MODE.bits();
stat_x.stx_mode |= attr.mode.bits() as u16;
stat_x.stx_mode |= attr.mode().bits() as u16;
}
if mask.contains(StatXMask::STATX_NLINK) {

View File

@@ -13,7 +13,7 @@ pub async fn sys_fchmod(fd: Fd, mode: u16) -> Result<usize> {
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
let mode = FilePermissions::from_bits_retain(mode);
let permissions = FilePermissions::from_bits_retain(mode);
let inode = file.inode().ok_or(KernelError::BadFd)?;
let mut attr = inode.getattr().await?;
@@ -22,7 +22,7 @@ pub async fn sys_fchmod(fd: Fd, mode: u16) -> Result<usize> {
return Err(KernelError::NotPermitted);
}
attr.mode = mode;
attr.permissions = permissions;
inode.setattr(attr).await?;
Ok(0)