diff --git a/kernel/src/fs/file_handle.rs b/kernel/src/fs/file_handle.rs index 07df09cf4..b8cee32b1 100644 --- a/kernel/src/fs/file_handle.rs +++ b/kernel/src/fs/file_handle.rs @@ -48,7 +48,10 @@ pub trait FileLike: Pollable + Send + Sync + Any { } fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - return_errno_with_message!(Errno::EINVAL, "ioctl is not supported"); + // `ENOTTY` means that "The specified operation does not apply to the kind of object that + // the file descriptor references". + // Reference: . + return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } /// Obtains the mappable object to map this file into the user address space. @@ -56,7 +59,10 @@ pub trait FileLike: Pollable + Send + Sync + Any { /// If this file has a corresponding mappable object of [`Mappable`], /// then it can be either an inode or an MMIO region. fn mappable(&self) -> Result { - return_errno_with_message!(Errno::EINVAL, "the file is not mappable"); + // `ENODEV` means that "The underlying filesystem of the specified file does not support + // memory mapping". + // Reference: . + return_errno_with_message!(Errno::ENODEV, "the file is not mappable"); } fn resize(&self, new_size: usize) -> Result<()> { diff --git a/kernel/src/fs/inode_handle/mod.rs b/kernel/src/fs/inode_handle/mod.rs index 462fb2c58..04ad43a84 100644 --- a/kernel/src/fs/inode_handle/mod.rs +++ b/kernel/src/fs/inode_handle/mod.rs @@ -184,7 +184,7 @@ impl InodeHandle_ { // return the file-specific mappable object. file_io.mappable() } else { - return_errno_with_message!(Errno::EINVAL, "the file is not mappable"); + return_errno_with_message!(Errno::ENODEV, "the file is not mappable"); } } @@ -329,7 +329,7 @@ pub trait FileIo: Pollable + Send + Sync + 'static { } fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { - return_errno_with_message!(Errno::EINVAL, "ioctl is not supported"); + return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } } diff --git a/kernel/src/fs/overlayfs/fs.rs b/kernel/src/fs/overlayfs/fs.rs index d4d2f2a7c..e6fa540d8 100644 --- a/kernel/src/fs/overlayfs/fs.rs +++ b/kernel/src/fs/overlayfs/fs.rs @@ -500,12 +500,7 @@ impl OverlayInode { pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result { self.upper().map_or_else( - || { - Err(Error::with_message( - Errno::EOPNOTSUPP, - "ioctl is not supported", - )) - }, + || Err(Error::with_message(Errno::ENOTTY, "ioctl is not supported")), |upper| upper.ioctl(cmd, arg), ) } diff --git a/kernel/src/fs/ramfs/fs.rs b/kernel/src/fs/ramfs/fs.rs index 4853489cd..b22e32427 100644 --- a/kernel/src/fs/ramfs/fs.rs +++ b/kernel/src/fs/ramfs/fs.rs @@ -1204,7 +1204,7 @@ impl Inode for RamInode { if let Some(device) = self.inner.as_device() { return device.ioctl(cmd, arg); } - return_errno_with_message!(Errno::EINVAL, "ioctl is not supported"); + return_errno_with_message!(Errno::ENOTTY, "ioctl is not supported"); } fn is_seekable(&self) -> bool { diff --git a/test/src/apps/file_io/access_err.c b/test/src/apps/file_io/access_err.c new file mode 100644 index 000000000..ca9eafe0f --- /dev/null +++ b/test/src/apps/file_io/access_err.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: MPL-2.0 + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "../test.h" + +#define FILENAME "/tmp/testfile" +#define DIRNAME "/tmp" +#define PAGE_SIZE 4096 + +static struct flock f_rdlck = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1024, +}; +static struct flock f_wrlck = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1024, +}; +static struct flock f_unlck = { + .l_type = F_UNLCK, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 1024, +}; + +FN_SETUP(create) +{ + CHECK(creat(FILENAME, 0666)); +} +END_SETUP() + +FN_TEST(readable) +{ + int fd; + char buf[1]; + void *addr; + + // Test 1: Normal file + + fd = TEST_SUCC(open(FILENAME, O_RDONLY)); + + TEST_SUCC(read(fd, buf, sizeof(buf))); + TEST_ERRNO(write(fd, buf, sizeof(buf)), EBADF); + TEST_SUCC(lseek(fd, 0, SEEK_SET)); + TEST_ERRNO(ioctl(fd, TCGETS), ENOTTY); + TEST_ERRNO(ftruncate(fd, 1), EINVAL); + TEST_ERRNO(fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1), EBADF); + + addr = TEST_SUCC(mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0)); + TEST_SUCC(munmap(addr, PAGE_SIZE)); + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0), + EACCES); + + TEST_SUCC(flock(fd, LOCK_SH)); + TEST_SUCC(flock(fd, LOCK_UN)); + TEST_SUCC(flock(fd, LOCK_EX)); + TEST_SUCC(flock(fd, LOCK_UN)); + + TEST_SUCC(fcntl(fd, F_SETLK, &f_rdlck)); + TEST_SUCC(fcntl(fd, F_SETLK, &f_unlck)); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_wrlck), EBADF); + TEST_SUCC(fcntl(fd, F_SETLK, &f_unlck)); + + TEST_RES(fcntl(fd, F_GETLK, &f_rdlck), f_rdlck.l_type == F_UNLCK); + TEST_RES(fcntl(fd, F_GETLK, &f_wrlck), f_wrlck.l_type == F_UNLCK); + f_rdlck.l_type = F_RDLCK; + f_wrlck.l_type = F_WRLCK; + + TEST_SUCC(close(fd)); + + // Test 2: Directory + + fd = TEST_SUCC(open(DIRNAME, O_RDONLY)); + + TEST_ERRNO(read(fd, buf, sizeof(buf)), EISDIR); + TEST_ERRNO(write(fd, buf, sizeof(buf)), EBADF); + TEST_SUCC(lseek(fd, 0, SEEK_SET)); + TEST_ERRNO(ioctl(fd, TCGETS), ENOTTY); + TEST_ERRNO(ftruncate(fd, 1), EINVAL); + TEST_ERRNO(fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1), EBADF); + + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0), ENODEV); + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0), + EACCES); + + TEST_SUCC(flock(fd, LOCK_SH)); + TEST_SUCC(flock(fd, LOCK_UN)); + TEST_SUCC(flock(fd, LOCK_EX)); + TEST_SUCC(flock(fd, LOCK_UN)); + + TEST_SUCC(fcntl(fd, F_SETLK, &f_rdlck)); + TEST_SUCC(fcntl(fd, F_SETLK, &f_unlck)); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_wrlck), EBADF); + TEST_SUCC(fcntl(fd, F_SETLK, &f_unlck)); + + TEST_RES(fcntl(fd, F_GETLK, &f_rdlck), f_rdlck.l_type == F_UNLCK); + TEST_RES(fcntl(fd, F_GETLK, &f_wrlck), f_wrlck.l_type == F_UNLCK); + f_rdlck.l_type = F_RDLCK; + f_wrlck.l_type = F_WRLCK; + + TEST_SUCC(close(fd)); +} +END_TEST() + +FN_TEST(writeable) +{ + int fd; + char buf[1]; + void *addr; + + // Test 1: Normal file + + fd = TEST_SUCC(open(FILENAME, O_WRONLY)); + + TEST_ERRNO(read(fd, buf, sizeof(buf)), EBADF); + TEST_SUCC(write(fd, buf, sizeof(buf))); + TEST_SUCC(lseek(fd, 0, SEEK_SET)); + TEST_ERRNO(ioctl(fd, TCGETS), ENOTTY); + TEST_SUCC(ftruncate(fd, 1)); + TEST_SUCC(fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1)); + + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0), EACCES); + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + 0), + EACCES); + + TEST_SUCC(flock(fd, LOCK_SH)); + TEST_SUCC(flock(fd, LOCK_UN)); + TEST_SUCC(flock(fd, LOCK_EX)); + TEST_SUCC(flock(fd, LOCK_UN)); + + TEST_ERRNO(fcntl(fd, F_SETLK, &f_rdlck), EBADF); + TEST_SUCC(fcntl(fd, F_SETLK, &f_unlck)); + TEST_SUCC(fcntl(fd, F_SETLK, &f_wrlck)); + TEST_SUCC(fcntl(fd, F_SETLK, &f_unlck)); + + TEST_RES(fcntl(fd, F_GETLK, &f_rdlck), f_rdlck.l_type == F_UNLCK); + TEST_RES(fcntl(fd, F_GETLK, &f_wrlck), f_wrlck.l_type == F_UNLCK); + f_rdlck.l_type = F_RDLCK; + f_wrlck.l_type = F_WRLCK; + + TEST_SUCC(close(fd)); + + // Test 2: Directory + + TEST_ERRNO(open(DIRNAME, O_WRONLY), EISDIR); + TEST_ERRNO(open(DIRNAME, O_RDWR), EISDIR); +} +END_TEST() + +FN_TEST(path) +{ + int fd; + char buf[1]; + void *addr; + + // Test 1: Normal file + + fd = TEST_SUCC(open(FILENAME, O_RDWR | O_PATH)); + + TEST_ERRNO(read(fd, buf, sizeof(buf)), EBADF); + TEST_ERRNO(write(fd, buf, sizeof(buf)), EBADF); + TEST_ERRNO(lseek(fd, 0, SEEK_SET), EBADF); + TEST_ERRNO(ioctl(fd, TCGETS), EBADF); + TEST_ERRNO(ftruncate(fd, 1), EBADF); + TEST_ERRNO(fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1), EBADF); + + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0), EBADF); + // FIXME: Asterinas reports `EACCES` because it performs the permission check first. + // TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + // 0), + // EBADF); + + TEST_ERRNO(flock(fd, LOCK_SH), EBADF); + TEST_ERRNO(flock(fd, LOCK_UN), EBADF); + TEST_ERRNO(flock(fd, LOCK_EX), EBADF); + TEST_ERRNO(flock(fd, LOCK_UN), EBADF); + + TEST_ERRNO(fcntl(fd, F_SETLK, &f_rdlck), EBADF); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_unlck), EBADF); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_wrlck), EBADF); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_unlck), EBADF); + + TEST_ERRNO(fcntl(fd, F_GETLK, &f_rdlck), EBADF); + TEST_ERRNO(fcntl(fd, F_GETLK, &f_wrlck), EBADF); + + TEST_SUCC(close(fd)); + + // Test 2: Directory + + fd = TEST_SUCC(open(DIRNAME, O_RDWR | O_PATH)); + + TEST_ERRNO(read(fd, buf, sizeof(buf)), EBADF); + TEST_ERRNO(write(fd, buf, sizeof(buf)), EBADF); + TEST_ERRNO(lseek(fd, 0, SEEK_SET), EBADF); + TEST_ERRNO(ioctl(fd, TCGETS), EBADF); + TEST_ERRNO(ftruncate(fd, 1), EBADF); + TEST_ERRNO(fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1), EBADF); + + TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0), EBADF); + // FIXME: Asterinas reports `EACCES` because it performs the permission check first. + // TEST_ERRNO(mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, + // 0), + // EBADF); + + TEST_ERRNO(flock(fd, LOCK_SH), EBADF); + TEST_ERRNO(flock(fd, LOCK_UN), EBADF); + TEST_ERRNO(flock(fd, LOCK_EX), EBADF); + TEST_ERRNO(flock(fd, LOCK_UN), EBADF); + + TEST_ERRNO(fcntl(fd, F_SETLK, &f_rdlck), EBADF); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_unlck), EBADF); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_wrlck), EBADF); + TEST_ERRNO(fcntl(fd, F_SETLK, &f_unlck), EBADF); + + TEST_ERRNO(fcntl(fd, F_GETLK, &f_rdlck), EBADF); + TEST_ERRNO(fcntl(fd, F_GETLK, &f_wrlck), EBADF); + + TEST_SUCC(close(fd)); +} +END_TEST() + +FN_SETUP(unlink) +{ + CHECK(unlink(FILENAME)); +} +END_SETUP() diff --git a/test/src/apps/scripts/fs.sh b/test/src/apps/scripts/fs.sh index 462c27bfd..987846694 100755 --- a/test/src/apps/scripts/fs.sh +++ b/test/src/apps/scripts/fs.sh @@ -102,6 +102,7 @@ pipe/pipe_err pipe/short_rw epoll/epoll_err epoll/poll_err +file_io/access_err file_io/iovec_err devfs/full devfs/random