mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 23:36:34 +00:00
Add support for group-based permission checking in ext2
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
6cea8d2a8c
commit
e75b6320ad
@ -18,7 +18,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::utils::{Extension, FallocMode, InodeMode, Metadata},
|
fs::utils::{Extension, FallocMode, InodeMode, Metadata},
|
||||||
process::{Gid, Uid},
|
process::{posix_thread::AsPosixThread, Gid, Uid},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Max length of file name.
|
/// Max length of file name.
|
||||||
@ -2075,11 +2075,12 @@ impl TryFrom<RawInode> for InodeDesc {
|
|||||||
impl InodeDesc {
|
impl InodeDesc {
|
||||||
pub fn new(type_: InodeType, perm: FilePerm) -> Dirty<Self> {
|
pub fn new(type_: InodeType, perm: FilePerm) -> Dirty<Self> {
|
||||||
let now = now();
|
let now = now();
|
||||||
|
let credentials = current_thread!().as_posix_thread().unwrap().credentials();
|
||||||
Dirty::new_dirty(Self {
|
Dirty::new_dirty(Self {
|
||||||
type_,
|
type_,
|
||||||
perm,
|
perm,
|
||||||
uid: 0,
|
uid: credentials.fsuid().into(),
|
||||||
gid: 0,
|
gid: credentials.fsgid().into(),
|
||||||
size: 0,
|
size: 0,
|
||||||
atime: now,
|
atime: now,
|
||||||
ctime: now,
|
ctime: now,
|
||||||
|
@ -134,9 +134,6 @@ impl FsResolver {
|
|||||||
let parent = lookup_ctx
|
let parent = lookup_ctx
|
||||||
.parent()
|
.parent()
|
||||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "parent not found"))?;
|
.ok_or_else(|| Error::with_message(Errno::ENOENT, "parent not found"))?;
|
||||||
if !parent.mode()?.is_writable() {
|
|
||||||
return_errno_with_message!(Errno::EACCES, "file cannot be created");
|
|
||||||
}
|
|
||||||
|
|
||||||
let tail_file_name = lookup_ctx.tail_file_name().unwrap();
|
let tail_file_name = lookup_ctx.tail_file_name().unwrap();
|
||||||
let new_dentry =
|
let new_dentry =
|
||||||
|
@ -8,14 +8,8 @@ use crate::{prelude::*, process::signal::Pollable};
|
|||||||
|
|
||||||
impl InodeHandle<Rights> {
|
impl InodeHandle<Rights> {
|
||||||
pub fn new(dentry: Dentry, access_mode: AccessMode, status_flags: StatusFlags) -> Result<Self> {
|
pub fn new(dentry: Dentry, access_mode: AccessMode, status_flags: StatusFlags) -> Result<Self> {
|
||||||
let inode_mode = dentry.inode().mode()?;
|
let inode = dentry.inode();
|
||||||
if access_mode.is_readable() && !inode_mode.is_readable() {
|
inode.check_permission(access_mode.into())?;
|
||||||
return_errno_with_message!(Errno::EACCES, "file is not readable");
|
|
||||||
}
|
|
||||||
if access_mode.is_writable() && !inode_mode.is_writable() {
|
|
||||||
return_errno_with_message!(Errno::EACCES, "file is not writable");
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::new_unchecked_access(dentry, access_mode, status_flags)
|
Self::new_unchecked_access(dentry, access_mode, status_flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,9 @@ use ostd::sync::RwMutexWriteGuard;
|
|||||||
use crate::{
|
use crate::{
|
||||||
fs::{
|
fs::{
|
||||||
path::mount::MountNode,
|
path::mount::MountNode,
|
||||||
utils::{FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, NAME_MAX},
|
utils::{
|
||||||
|
FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, Permission, NAME_MAX,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{Gid, Uid},
|
process::{Gid, Uid},
|
||||||
@ -461,6 +463,13 @@ impl Dentry {
|
|||||||
|
|
||||||
/// Creates a new `Dentry` to represent the child directory of a file system.
|
/// Creates a new `Dentry` to represent the child directory of a file system.
|
||||||
pub fn new_fs_child(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Self> {
|
pub fn new_fs_child(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Self> {
|
||||||
|
if self
|
||||||
|
.inode()
|
||||||
|
.check_permission(Permission::MAY_WRITE)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return_errno!(Errno::EACCES);
|
||||||
|
}
|
||||||
let new_child_dentry = self.inner.create(name, type_, mode)?;
|
let new_child_dentry = self.inner.create(name, type_, mode)?;
|
||||||
Ok(Self::new(self.mount_node.clone(), new_child_dentry))
|
Ok(Self::new(self.mount_node.clone(), new_child_dentry))
|
||||||
}
|
}
|
||||||
@ -474,7 +483,7 @@ impl Dentry {
|
|||||||
if self.type_() != InodeType::Dir {
|
if self.type_() != InodeType::Dir {
|
||||||
return_errno!(Errno::ENOTDIR);
|
return_errno!(Errno::ENOTDIR);
|
||||||
}
|
}
|
||||||
if !self.mode()?.is_executable() {
|
if self.inode().check_permission(Permission::MAY_EXEC).is_err() {
|
||||||
return_errno!(Errno::EACCES);
|
return_errno!(Errno::EACCES);
|
||||||
}
|
}
|
||||||
if name.len() > NAME_MAX {
|
if name.len() > NAME_MAX {
|
||||||
|
@ -6,13 +6,14 @@ use core::{any::TypeId, time::Duration};
|
|||||||
|
|
||||||
use aster_rights::Full;
|
use aster_rights::Full;
|
||||||
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
||||||
|
use ostd::task::Task;
|
||||||
|
|
||||||
use super::{DirentVisitor, FallocMode, FileSystem, IoctlCmd};
|
use super::{AccessMode, DirentVisitor, FallocMode, FileSystem, IoctlCmd};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::IoEvents,
|
||||||
fs::device::{Device, DeviceType},
|
fs::device::{Device, DeviceType},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::{signal::PollHandle, Gid, Uid},
|
process::{posix_thread::AsPosixThread, signal::PollHandle, Gid, Uid},
|
||||||
time::clocks::RealTimeCoarseClock,
|
time::clocks::RealTimeCoarseClock,
|
||||||
vm::vmo::Vmo,
|
vm::vmo::Vmo,
|
||||||
};
|
};
|
||||||
@ -80,6 +81,43 @@ impl From<DeviceType> for InodeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Permission: u16 {
|
||||||
|
// This implementation refers the implementation of linux
|
||||||
|
// https://elixir.bootlin.com/linux/v6.0.9/source/include/linux/fs.h#L95
|
||||||
|
const MAY_EXEC = 0x0001;
|
||||||
|
const MAY_WRITE = 0x0002;
|
||||||
|
const MAY_READ = 0x0004;
|
||||||
|
const MAY_APPEND = 0x0008;
|
||||||
|
const MAY_ACCESS = 0x0010;
|
||||||
|
const MAY_OPEN = 0x0020;
|
||||||
|
const MAY_CHDIR = 0x0040;
|
||||||
|
const MAY_NOT_BLOCK = 0x0080;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Permission {
|
||||||
|
pub fn may_read(&self) -> bool {
|
||||||
|
self.contains(Self::MAY_READ)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn may_write(&self) -> bool {
|
||||||
|
self.contains(Self::MAY_WRITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn may_exec(&self) -> bool {
|
||||||
|
self.contains(Self::MAY_EXEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<AccessMode> for Permission {
|
||||||
|
fn from(access_mode: AccessMode) -> Permission {
|
||||||
|
match access_mode {
|
||||||
|
AccessMode::O_RDONLY => Permission::MAY_READ,
|
||||||
|
AccessMode::O_WRONLY => Permission::MAY_WRITE,
|
||||||
|
AccessMode::O_RDWR => Permission::MAY_READ | Permission::MAY_WRITE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct InodeMode: u16 {
|
pub struct InodeMode: u16 {
|
||||||
/// set-user-ID
|
/// set-user-ID
|
||||||
@ -110,18 +148,42 @@ bitflags! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InodeMode {
|
impl InodeMode {
|
||||||
pub fn is_readable(&self) -> bool {
|
pub fn is_owner_readable(&self) -> bool {
|
||||||
self.contains(Self::S_IRUSR)
|
self.contains(Self::S_IRUSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_writable(&self) -> bool {
|
pub fn is_owner_writable(&self) -> bool {
|
||||||
self.contains(Self::S_IWUSR)
|
self.contains(Self::S_IWUSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_executable(&self) -> bool {
|
pub fn is_owner_executable(&self) -> bool {
|
||||||
self.contains(Self::S_IXUSR)
|
self.contains(Self::S_IXUSR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_group_readable(&self) -> bool {
|
||||||
|
self.contains(Self::S_IRGRP)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_group_writable(&self) -> bool {
|
||||||
|
self.contains(Self::S_IWGRP)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_group_executable(&self) -> bool {
|
||||||
|
self.contains(Self::S_IXGRP)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_other_readable(&self) -> bool {
|
||||||
|
self.contains(Self::S_IROTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_other_writable(&self) -> bool {
|
||||||
|
self.contains(Self::S_IWOTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_other_executable(&self) -> bool {
|
||||||
|
self.contains(Self::S_IXOTH)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_sticky_bit(&self) -> bool {
|
pub fn has_sticky_bit(&self) -> bool {
|
||||||
self.contains(Self::S_ISVTX)
|
self.contains(Self::S_ISVTX)
|
||||||
}
|
}
|
||||||
@ -434,6 +496,47 @@ pub trait Inode: Any + Sync + Send {
|
|||||||
fn extension(&self) -> Option<&Extension> {
|
fn extension(&self) -> Option<&Extension> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to check for read/write/execute permissions on a file.
|
||||||
|
///
|
||||||
|
/// Similar to Linux, using "fsuid" here allows setting filesystem permissions
|
||||||
|
/// without changing the "normal" uids for other tasks.
|
||||||
|
fn check_permission(&self, mut perm: Permission) -> Result<()> {
|
||||||
|
let creds = match Task::current() {
|
||||||
|
Some(task) => match task.as_posix_thread() {
|
||||||
|
Some(thread) => thread.credentials(),
|
||||||
|
None => return Ok(()),
|
||||||
|
},
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
perm =
|
||||||
|
perm.intersection(Permission::MAY_READ | Permission::MAY_WRITE | Permission::MAY_EXEC);
|
||||||
|
let mode = self.mode().unwrap();
|
||||||
|
|
||||||
|
if self.metadata().uid == creds.fsuid() {
|
||||||
|
if (perm.may_read() && !mode.is_owner_readable())
|
||||||
|
|| (perm.may_write() && !mode.is_owner_writable())
|
||||||
|
|| (perm.may_exec() && !mode.is_owner_executable())
|
||||||
|
{
|
||||||
|
return_errno_with_message!(Errno::EACCES, "owner permission check failed");
|
||||||
|
}
|
||||||
|
} else if self.metadata().gid == creds.fsgid() {
|
||||||
|
if (perm.may_read() && !mode.is_group_readable())
|
||||||
|
|| (perm.may_write() && !mode.is_group_writable())
|
||||||
|
|| (perm.may_exec() && !mode.is_group_executable())
|
||||||
|
{
|
||||||
|
return_errno_with_message!(Errno::EACCES, "group permission check failed");
|
||||||
|
}
|
||||||
|
} else if (perm.may_read() && !mode.is_other_readable())
|
||||||
|
|| (perm.may_write() && !mode.is_other_writable())
|
||||||
|
|| (perm.may_exec() && !mode.is_other_executable())
|
||||||
|
{
|
||||||
|
return_errno_with_message!(Errno::EACCES, "other permission check failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Inode {
|
impl dyn Inode {
|
||||||
|
@ -11,7 +11,7 @@ pub use falloc_mode::FallocMode;
|
|||||||
pub use file_creation_mask::FileCreationMask;
|
pub use file_creation_mask::FileCreationMask;
|
||||||
pub use flock::{FlockItem, FlockList, FlockType};
|
pub use flock::{FlockItem, FlockList, FlockType};
|
||||||
pub use fs::{FileSystem, FsFlags, SuperBlock};
|
pub use fs::{FileSystem, FsFlags, SuperBlock};
|
||||||
pub use inode::{Extension, Inode, InodeMode, InodeType, Metadata, MknodType};
|
pub use inode::{Extension, Inode, InodeMode, InodeType, Metadata, MknodType, Permission};
|
||||||
pub use ioctl::IoctlCmd;
|
pub use ioctl::IoctlCmd;
|
||||||
pub use page_cache::{PageCache, PageCacheBackend};
|
pub use page_cache::{PageCache, PageCacheBackend};
|
||||||
pub use random_test::{generate_random_operation, new_fs_in_memory};
|
pub use random_test::{generate_random_operation, new_fs_in_memory};
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
fs::{
|
fs::{
|
||||||
fs_resolver::{split_path, FsPath},
|
fs_resolver::{split_path, FsPath},
|
||||||
path::Dentry,
|
path::Dentry,
|
||||||
utils::{InodeMode, InodeType},
|
utils::{InodeMode, InodeType, Permission},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::posix_thread::AsPosixThread,
|
process::posix_thread::AsPosixThread,
|
||||||
@ -19,7 +19,11 @@ pub fn lookup_socket_file(path: &str) -> Result<Dentry> {
|
|||||||
fs.lookup(&fs_path)?
|
fs.lookup(&fs_path)?
|
||||||
};
|
};
|
||||||
|
|
||||||
if !dentry.mode()?.is_readable() || !dentry.mode()?.is_writable() {
|
if dentry
|
||||||
|
.inode()
|
||||||
|
.check_permission(Permission::MAY_READ | Permission::MAY_WRITE)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
return_errno_with_message!(Errno::EACCES, "the socket file cannot be read or written")
|
return_errno_with_message!(Errno::EACCES, "the socket file cannot be read or written")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ use crate::{
|
|||||||
fs::{
|
fs::{
|
||||||
fs_resolver::{FsPath, FsResolver, AT_FDCWD},
|
fs_resolver::{FsPath, FsResolver, AT_FDCWD},
|
||||||
path::Dentry,
|
path::Dentry,
|
||||||
|
utils::Permission,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
@ -77,7 +78,11 @@ pub fn check_executable_file(dentry: &Dentry) -> Result<()> {
|
|||||||
return_errno_with_message!(Errno::EACCES, "the dentry is not a regular file");
|
return_errno_with_message!(Errno::EACCES, "the dentry is not a regular file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dentry.mode()?.is_executable() {
|
if dentry
|
||||||
|
.inode()
|
||||||
|
.check_permission(Permission::MAY_EXEC)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
return_errno_with_message!(Errno::EACCES, "the dentry is not executable");
|
return_errno_with_message!(Errno::EACCES, "the dentry is not executable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
fs::{
|
fs::{
|
||||||
file_table::FileDesc,
|
file_table::FileDesc,
|
||||||
fs_resolver::{FsPath, AT_FDCWD},
|
fs_resolver::{FsPath, AT_FDCWD},
|
||||||
utils::PATH_MAX,
|
utils::{Permission, PATH_MAX},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
@ -81,19 +81,19 @@ pub fn do_faccessat(
|
|||||||
return Ok(SyscallReturn::Return(0));
|
return Ok(SyscallReturn::Return(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
let inode_mode = dentry.mode()?;
|
let inode = dentry.inode();
|
||||||
|
|
||||||
// FIXME: The current implementation is dummy
|
// FIXME: The current implementation is dummy
|
||||||
if mode.contains(AccessMode::R_OK) && !inode_mode.is_readable() {
|
if mode.contains(AccessMode::R_OK) {
|
||||||
return_errno_with_message!(Errno::EACCES, "Read permission denied");
|
inode.check_permission(Permission::MAY_READ)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode.contains(AccessMode::W_OK) && !inode_mode.is_writable() {
|
if mode.contains(AccessMode::W_OK) {
|
||||||
return_errno_with_message!(Errno::EACCES, "Write permission denied");
|
inode.check_permission(Permission::MAY_WRITE)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if mode.contains(AccessMode::X_OK) && !inode_mode.is_executable() {
|
if mode.contains(AccessMode::X_OK) {
|
||||||
return_errno_with_message!(Errno::EACCES, "Execute permission denied");
|
inode.check_permission(Permission::MAY_EXEC)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SyscallReturn::Return(0))
|
Ok(SyscallReturn::Return(0))
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
# Please keep the list sorted by name.
|
# Please keep the list sorted by name.
|
||||||
TESTS ?= \
|
TESTS ?= \
|
||||||
access_test \
|
access_test \
|
||||||
alarm_test \
|
|
||||||
chmod_test \
|
|
||||||
chown_test \
|
chown_test \
|
||||||
creat_test \
|
creat_test \
|
||||||
dup_test \
|
dup_test \
|
||||||
|
@ -1 +1,4 @@
|
|||||||
AccessTest.UsrReadWrite
|
AccessTest.UsrReadWrite
|
||||||
|
AccessTest.NoPerms
|
||||||
|
AccessTest.UsrReadOnly
|
||||||
|
AccessTest.UsrReadExec
|
@ -1,2 +1,3 @@
|
|||||||
MkdirTest.HonorsUmask
|
MkdirTest.HonorsUmask
|
||||||
MkdirTest.HonorsUmask2
|
MkdirTest.HonorsUmask2
|
||||||
|
MkdirTest.FailsOnDirWithoutWritePerms
|
@ -2,3 +2,4 @@ CreateTest.ChmodReadToWriteBetweenOpens_NoRandomSave
|
|||||||
CreateTest.ChmodWriteToReadBetweenOpens_NoRandomSave
|
CreateTest.ChmodWriteToReadBetweenOpens_NoRandomSave
|
||||||
CreateTest.CreateWithReadFlagNotAllowedByMode_NoRandomSave
|
CreateTest.CreateWithReadFlagNotAllowedByMode_NoRandomSave
|
||||||
CreateTest.CreateWithWriteFlagNotAllowedByMode_NoRandomSave
|
CreateTest.CreateWithWriteFlagNotAllowedByMode_NoRandomSave
|
||||||
|
CreateTest.CreateFailsOnDirWithoutWritePerms
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
AccessTest.NoPerms
|
|
||||||
AccessTest.UsrReadOnly
|
|
||||||
AccessTest.UsrReadExec
|
|
@ -1 +0,0 @@
|
|||||||
MkdirTest.FailsOnDirWithoutWritePerms
|
|
@ -2,4 +2,3 @@ CreateTest.HonorsUmask_NoRandomSave
|
|||||||
CreateTest.CreatWithOTrunc
|
CreateTest.CreatWithOTrunc
|
||||||
CreateTest.CreatDirWithOTruncAndReadOnly
|
CreateTest.CreatDirWithOTruncAndReadOnly
|
||||||
CreateTest.CreateFailsOnUnpermittedDir
|
CreateTest.CreateFailsOnUnpermittedDir
|
||||||
CreateTest.CreateFailsOnDirWithoutWritePerms
|
|
||||||
|
Reference in New Issue
Block a user