sortix-mirror/kernel/inode.cpp

658 lines
16 KiB
C++
Raw Normal View History

/*
2021-02-16 21:19:50 +00:00
* Copyright (c) 2012-2017, 2021 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* inode.cpp
* Interfaces and utility classes for implementing inodes.
*/
#include <sys/uio.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
2021-02-16 21:19:50 +00:00
#include <poll.h>
#include <string.h>
2013-05-16 15:58:16 +00:00
#include <sortix/clock.h>
#include <sortix/stat.h>
#include <sortix/statvfs.h>
2013-10-27 00:42:10 +00:00
#include <sortix/kernel/inode.h>
#include <sortix/kernel/interlock.h>
2013-10-27 00:42:10 +00:00
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/memorymanagement.h>
2021-02-16 21:19:50 +00:00
#include <sortix/kernel/poll.h>
#include <sortix/kernel/refcount.h>
2013-05-16 15:58:16 +00:00
#include <sortix/kernel/time.h>
namespace Sortix {
AbstractInode::AbstractInode()
{
metalock = KTHREAD_MUTEX_INITIALIZER;
inode_type = INODE_TYPE_UNKNOWN;
stat_mode = 0;
stat_nlink = 0;
stat_uid = 0;
stat_gid = 0;
stat_size = 0;
2013-05-16 15:58:16 +00:00
stat_atim = Time::Get(CLOCK_REALTIME);
stat_ctim = Time::Get(CLOCK_REALTIME);
stat_mtim = Time::Get(CLOCK_REALTIME);
stat_blksize = Page::Size();
stat_blocks = 0;
supports_iovec = false;
}
AbstractInode::~AbstractInode()
{
}
bool AbstractInode::pass()
{
return true;
}
void AbstractInode::unpass()
{
}
void AbstractInode::linked()
{
InterlockedIncrement(&stat_nlink);
}
void AbstractInode::unlinked()
{
InterlockedDecrement(&stat_nlink);
}
int AbstractInode::sync(ioctx_t* /*ctx*/)
{
return 0;
}
int AbstractInode::stat(ioctx_t* ctx, struct stat* st)
{
struct stat retst;
ScopedLock lock(&metalock);
memset(&retst, 0, sizeof(retst));
retst.st_dev = dev;
retst.st_rdev = dev;
retst.st_ino = ino;
retst.st_mode = stat_mode;
retst.st_nlink = (nlink_t) stat_nlink;
retst.st_uid = stat_uid;
retst.st_gid = stat_gid;
retst.st_size = stat_size;
retst.st_atim = stat_atim;
retst.st_ctim = stat_ctim;
retst.st_mtim = stat_mtim;
retst.st_blksize = stat_blksize;
retst.st_blocks = stat_size / 512;
if ( !ctx->copy_to_dest(st, &retst, sizeof(retst)) )
return -1;
return 0;
}
// TODO: Provide an easier mechanism for letting subclasses give this
// information than overriding this method. Additionally, what should be
// done in abstract kernel objects where this call doesn't make that much
// sense?
int AbstractInode::statvfs(ioctx_t* ctx, struct statvfs* stvfs)
{
struct statvfs retstvfs;
ScopedLock lock(&metalock);
memset(&retstvfs, 0, sizeof(retstvfs));
retstvfs.f_bsize = 0;
retstvfs.f_frsize = 0;
retstvfs.f_blocks = 0;
retstvfs.f_bfree = 0;
retstvfs.f_bavail = 0;
retstvfs.f_files = 0;
retstvfs.f_ffree = 0;
retstvfs.f_favail = 0;
retstvfs.f_fsid = dev;
retstvfs.f_flag = ST_NOSUID;
retstvfs.f_namemax = ULONG_MAX;
if ( !ctx->copy_to_dest(stvfs, &retstvfs, sizeof(retstvfs)) )
return -1;
return 0;
}
int AbstractInode::chmod(ioctx_t* /*ctx*/, mode_t mode)
{
ScopedLock lock(&metalock);
stat_mode = (mode & S_SETABLE) | this->type;
return 0;
}
int AbstractInode::chown(ioctx_t* /*ctx*/, uid_t owner, gid_t group)
{
ScopedLock lock(&metalock);
stat_uid = owner;
stat_gid= group;
return 0;
}
int AbstractInode::truncate(ioctx_t* /*ctx*/, off_t /*length*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EISDIR, -1;
return errno = EINVAL, -1;
}
off_t AbstractInode::lseek(ioctx_t* /*ctx*/, off_t /*offset*/, int /*whence*/)
{
Fix SEEK_END, file offset overflow, and read/write/mkpartition syscall bugs. Fix SEEK_END seeking twice as far as requested. Centralize lseek handling in one place and avoid overflow bugs. Inode lseek handlers now only need to handle SEEK_END with offset 0. Prevent the file offset from ever going below zero or overflowing. Character devices are now not seekable, but lseek will pretend they are, yet always stay at the file offset 0. pread/pwrite on character devices will now ignore the file offset and call read/write. This change prevents character devices from being memory mapped, notably /dev/zero can no longer be memory mapped. None of the current ports seem to rely on this behavior and will work with just MAP_ANONYMOUS. Refactor read and write system calls to have a shared return statement for both seekable and non-seekable IO. Fix file offset overflow bugs in read and write system calls. Fix system calls returning EPERM instead of properly returning EBADF when the file has not been opened in the right mode. Truncate IO counts and total vector IO length so the IO operation does not do any IO beyond OFF_MAX. Truncate also total vector IO length for recvmsg and sendmsg. Fail with EINVAL if total vector IO length exceeds SSIZE_MAX. Don't stop early if the total IO length is zero, so zero length IO now block on any locks internal to the inode. Handle reads at the maximum file offset with an end of file condition and handle writes of at least one byte at the maximum file offset by failing with EFBIG. Refactor UtilMemoryBuffer to store the file size using off_t instead of size_t to avoid casts and keep file sizes in the off_t type. Properly handle errors in the code, such as failing with EROFS instead of EBADF if the backing memory is not writeable, and failing with EFBIG if writing beyond the end of the file. Fix mkpartition not rejecting invalid partition start offsets and lengths. Strictly enforce partition start and length checks in the partition code. Enforce partitions exist within regular files or block devices. Fix a few indention issues.
2017-10-26 15:12:07 +00:00
return errno = ESPIPE, -1;
}
ssize_t AbstractInode::read(ioctx_t* ctx, uint8_t* buf, size_t count)
{
if ( !supports_iovec )
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EISDIR, -1;
return errno = EBADF, -1;
}
struct iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) buf;
iov.iov_len = count;
return readv(ctx, &iov, 1);
}
ssize_t AbstractInode::readv(ioctx_t* ctx, const struct iovec* iov, int iovcnt)
{
if ( supports_iovec )
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EISDIR, -1;
return errno = EBADF, -1;
}
ssize_t sofar = 0;
for ( int i = 0; i < iovcnt && sofar < SSIZE_MAX; i++ )
{
size_t maxcount = SSIZE_MAX - sofar;
uint8_t* buf = (uint8_t*) iov[i].iov_base;
size_t count = iov[i].iov_len;
if ( maxcount < count )
count = maxcount;
int old_dflags = ctx->dflags;
if ( sofar )
ctx->dflags |= O_NONBLOCK;
ssize_t amount = read(ctx, buf, count);
ctx->dflags = old_dflags;
if ( amount < 0 )
return sofar ? sofar : -1;
if ( amount == 0 )
break;
sofar += amount;
if ( (size_t) amount < count )
break;
}
return sofar;
}
ssize_t AbstractInode::pread(ioctx_t* ctx, uint8_t* buf, size_t count,
off_t off)
{
if ( !supports_iovec )
{
if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY )
return errno = ESPIPE, -1;
if ( inode_type == INODE_TYPE_DIR )
return errno = EISDIR, -1;
return errno = EBADF, -1;
}
struct iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) buf;
iov.iov_len = count;
return preadv(ctx, &iov, 1, off);
}
ssize_t AbstractInode::preadv(ioctx_t* ctx, const struct iovec* iov, int iovcnt,
off_t off)
{
if ( supports_iovec )
{
if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY )
return errno = ESPIPE, -1;
if ( inode_type == INODE_TYPE_DIR )
return errno = EISDIR, -1;
return errno = EBADF, -1;
}
ssize_t sofar = 0;
for ( int i = 0; i < iovcnt && sofar < SSIZE_MAX; i++ )
{
size_t maxcount = SSIZE_MAX - sofar;
uint8_t* buf = (uint8_t*) iov[i].iov_base;
size_t count = iov[i].iov_len;
if ( maxcount < count )
count = maxcount;
off_t offset;
if ( __builtin_add_overflow(off, sofar, &offset) )
return sofar ? sofar : (errno = EOVERFLOW, -1);
int old_dflags = ctx->dflags;
if ( sofar )
ctx->dflags |= O_NONBLOCK;
ssize_t amount = pread(ctx, buf, count, offset);
ctx->dflags = old_dflags;
if ( amount < 0 )
return sofar ? sofar : -1;
if ( amount == 0 )
break;
sofar += amount;
if ( (size_t) amount < count )
break;
}
return sofar;
}
ssize_t AbstractInode::write(ioctx_t* ctx, const uint8_t* buf, size_t count)
{
if ( !supports_iovec )
return errno = EBADF, -1;
struct iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) buf;
iov.iov_len = count;
return writev(ctx, &iov, 1);
}
ssize_t AbstractInode::writev(ioctx_t* ctx, const struct iovec* iov, int iovcnt)
{
if ( supports_iovec )
return errno = EBADF, -1;
ssize_t sofar = 0;
for ( int i = 0; i < iovcnt && sofar < SSIZE_MAX; i++ )
{
size_t maxcount = SSIZE_MAX - sofar;
const uint8_t* buf = (uint8_t*) iov[i].iov_base;
size_t count = iov[i].iov_len;
if ( maxcount < count )
count = maxcount;
int old_dflags = ctx->dflags;
if ( sofar )
ctx->dflags |= O_NONBLOCK;
ssize_t amount = write(ctx, buf, count);
ctx->dflags = old_dflags;
if ( amount < 0 )
return sofar ? sofar : -1;
if ( amount == 0 )
break;
sofar += amount;
if ( (size_t) amount < count )
break;
}
return sofar;
}
ssize_t AbstractInode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count,
off_t off)
{
if ( !supports_iovec )
{
if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY )
return errno = ESPIPE, -1;
return errno = EBADF, -1;
}
struct iovec iov;
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) buf;
iov.iov_len = count;
return pwritev(ctx, &iov, 1, off);
}
ssize_t AbstractInode::pwritev(ioctx_t* ctx, const struct iovec* iov,
int iovcnt, off_t off)
{
if ( supports_iovec )
{
if ( inode_type == INODE_TYPE_STREAM || inode_type == INODE_TYPE_TTY )
return errno = ESPIPE, -1;
return errno = EBADF, -1;
}
ssize_t sofar = 0;
for ( int i = 0; i < iovcnt && sofar < SSIZE_MAX; i++ )
{
size_t maxcount = SSIZE_MAX - sofar;
uint8_t* buf = (uint8_t*) iov[i].iov_base;
size_t count = iov[i].iov_len;
if ( maxcount < count )
count = maxcount;
off_t offset;
if ( __builtin_add_overflow(off, sofar, &offset) )
return sofar ? sofar : (errno = EOVERFLOW, -1);
int old_dflags = ctx->dflags;
if ( sofar )
ctx->dflags |= O_NONBLOCK;
ssize_t amount = pwrite(ctx, buf, count, offset);
ctx->dflags = old_dflags;
if ( amount < 0 )
return sofar ? sofar : -1;
if ( amount == 0 )
break;
sofar += amount;
if ( (size_t) amount < count )
break;
}
return sofar;
}
2016-02-24 15:29:37 +00:00
int AbstractInode::utimens(ioctx_t* /*ctx*/, const struct timespec* times)
{
2013-05-16 15:58:16 +00:00
ScopedLock lock(&metalock);
2016-02-24 15:29:37 +00:00
struct timespec now = { 0, 0 };
if ( times[0].tv_nsec == UTIME_NOW || times[1].tv_nsec == UTIME_NOW )
now = Time::Get(CLOCK_REALTIME);
if ( times[0].tv_nsec == UTIME_NOW )
stat_atim = now;
else if ( times[0].tv_nsec != UTIME_OMIT )
stat_atim = times[0];
if ( times[1].tv_nsec == UTIME_NOW )
stat_mtim = now;
else if ( times[1].tv_nsec != UTIME_OMIT )
stat_mtim = times[1];
return 0;
}
int AbstractInode::isatty(ioctx_t* /*ctx*/)
{
if ( inode_type == INODE_TYPE_TTY )
return 1;
return errno = ENOTTY, 0;
}
ssize_t AbstractInode::readdirents(ioctx_t* /*ctx*/,
2015-11-20 01:57:09 +00:00
struct dirent* /*dirent*/,
size_t /*size*/,
off_t /*start*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = ENOTDIR, -1;
return errno = ENOTDIR, -1;
}
Ref<Inode> AbstractInode::open(ioctx_t* /*ctx*/, const char* /*filename*/,
int /*flags*/, mode_t /*mode*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, Ref<Inode>(NULL);
return errno = ENOTDIR, Ref<Inode>(NULL);
}
2016-10-22 21:47:28 +00:00
Ref<Inode> AbstractInode::factory(ioctx_t* /*ctx*/, const char* /*filename*/,
int /*flags*/, mode_t /*mode*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, Ref<Inode>(NULL);
return errno = ENOTDIR, Ref<Inode>(NULL);
}
int AbstractInode::mkdir(ioctx_t* /*ctx*/, const char* /*filename*/,
mode_t /*mode*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::link(ioctx_t* /*ctx*/, const char* /*filename*/,
Ref<Inode> /*node*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::link_raw(ioctx_t* /*ctx*/, const char* /*filename*/,
Ref<Inode> /*node*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::unlink(ioctx_t* /*ctx*/, const char* /*filename*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::unlink_raw(ioctx_t* /*ctx*/, const char* /*filename*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::rmdir(ioctx_t* /*ctx*/, const char* /*filename*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::rmdir_me(ioctx_t* /*ctx*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
int AbstractInode::symlink(ioctx_t* /*ctx*/, const char* /*oldname*/,
const char* /*filename*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
ssize_t AbstractInode::readlink(ioctx_t* /*ctx*/, char* /*buf*/,
size_t /*bufsiz*/)
{
return errno = EINVAL, -1;
}
2013-12-20 20:55:05 +00:00
int AbstractInode::tcgetwincurpos(ioctx_t* /*ctx*/, struct wincurpos* /*wcp*/)
{
if ( inode_type == INODE_TYPE_TTY )
return errno = EBADF, -1;
return errno = ENOTTY, -1;
}
int AbstractInode::ioctl(ioctx_t* /*ctx*/, int /*cmd*/, uintptr_t /*arg*/)
{
return errno = ENOTTY, -1;
}
2013-06-12 00:18:07 +00:00
int AbstractInode::tcsetpgrp(ioctx_t* /*ctx*/, pid_t /*pgid*/)
{
if ( inode_type == INODE_TYPE_TTY )
return errno = EBADF, -1;
return errno = ENOTTY, -1;
}
pid_t AbstractInode::tcgetpgrp(ioctx_t* /*ctx*/)
{
if ( inode_type == INODE_TYPE_TTY )
return errno = EBADF, -1;
return errno = ENOTTY, -1;
}
int AbstractInode::settermmode(ioctx_t* /*ctx*/, unsigned /*mode*/)
{
if ( inode_type == INODE_TYPE_TTY )
return errno = EBADF, -1;
return errno = ENOTTY, -1;
}
int AbstractInode::gettermmode(ioctx_t* /*ctx*/, unsigned* /*mode*/)
{
if ( inode_type == INODE_TYPE_TTY )
return errno = EBADF, -1;
return errno = ENOTTY, -1;
}
2021-02-16 21:19:50 +00:00
int AbstractInode::poll(ioctx_t* /*ctx*/, PollNode* node)
2012-12-29 22:09:09 +00:00
{
2021-02-16 21:19:50 +00:00
short status = POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM;
if ( !(status & node->events) )
return errno = EAGAIN, -1;
node->master->revents |= status & node->events;
return 0;
2012-12-29 22:09:09 +00:00
}
2012-12-20 15:19:07 +00:00
int AbstractInode::rename_here(ioctx_t* /*ctx*/, Ref<Inode> /*from*/,
const char* /*oldname*/, const char* /*newname*/)
{
if ( inode_type == INODE_TYPE_DIR )
return errno = EBADF, -1;
return errno = ENOTDIR, -1;
}
Ref<Inode> AbstractInode::accept4(ioctx_t* /*ctx*/, uint8_t* /*addr*/,
size_t* /*addrlen*/, int /*flags*/)
2013-03-19 21:40:37 +00:00
{
return errno = ENOTSOCK, Ref<Inode>();
}
int AbstractInode::bind(ioctx_t* /*ctx*/, const uint8_t* /*addr*/,
size_t /*addrlen*/)
{
return errno = ENOTSOCK, -1;
}
int AbstractInode::connect(ioctx_t* /*ctx*/, const uint8_t* /*addr*/,
size_t /*addrlen*/)
{
return errno = ENOTSOCK, -1;
}
int AbstractInode::listen(ioctx_t* /*ctx*/, int /*backlog*/)
{
return errno = ENOTSOCK, -1;
}
ssize_t AbstractInode::recv(ioctx_t* /*ctx*/, uint8_t* /*buf*/,
size_t /*count*/, int /*flags*/)
{
return errno = ENOTSOCK, -1;
}
ssize_t AbstractInode::recvmsg(ioctx_t* /*ctx*/, struct msghdr* /*msg*/,
int /*flags*/)
{
return errno = ENOTSOCK, -1;
}
2013-03-19 21:40:37 +00:00
ssize_t AbstractInode::send(ioctx_t* /*ctx*/, const uint8_t* /*buf*/,
size_t /*count*/, int /*flags*/)
{
return errno = ENOTSOCK, -1;
}
ssize_t AbstractInode::sendmsg(ioctx_t* /*ctx*/, const struct msghdr* /*msg*/,
int /*flags*/)
{
return errno = ENOTSOCK, -1;
}
2014-02-28 16:10:08 +00:00
int AbstractInode::getsockopt(ioctx_t* /*ctx*/, int /*level*/, int /*option_name*/,
void* /*option_value*/, size_t* /*option_size_ptr*/)
{
return errno = ENOTSOCK, -1;
}
int AbstractInode::setsockopt(ioctx_t* /*ctx*/, int /*level*/, int /*option_name*/,
const void* /*option_value*/, size_t /*option_size*/)
{
return errno = ENOTSOCK, -1;
}
2014-05-05 19:36:40 +00:00
ssize_t AbstractInode::tcgetblob(ioctx_t* /*ctx*/, const char* /*name*/, void* /*buffer*/, size_t /*count*/)
{
return errno = ENOTTY, -1;
}
ssize_t AbstractInode::tcsetblob(ioctx_t* /*ctx*/, const char* /*name*/, const void* /*buffer*/, size_t /*count*/)
{
return errno = ENOTTY, -1;
}
2014-05-07 12:14:38 +00:00
int AbstractInode::unmounted(ioctx_t* /*ctx*/)
{
return 0;
}
2016-01-23 19:56:07 +00:00
int AbstractInode::tcdrain(ioctx_t* /*ctx*/)
{
return errno = ENOTTY, -1;
}
int AbstractInode::tcflow(ioctx_t* /*ctx*/, int /*action*/)
{
return errno = ENOTTY, -1;
}
int AbstractInode::tcflush(ioctx_t* /*ctx*/, int /*queue_selector*/)
{
return errno = ENOTTY, -1;
}
int AbstractInode::tcgetattr(ioctx_t* /*ctx*/, struct termios* /*tio*/)
{
return errno = ENOTTY, -1;
}
pid_t AbstractInode::tcgetsid(ioctx_t* /*ctx*/)
{
return errno = ENOTTY, -1;
}
int AbstractInode::tcsendbreak(ioctx_t* /*ctx*/, int /*duration*/)
{
return errno = ENOTTY, -1;
}
int AbstractInode::tcsetattr(ioctx_t* /*ctx*/, int /*actions*/, const struct termios* /*tio*/)
{
return errno = ENOTTY, -1;
}
2016-08-06 13:44:37 +00:00
int AbstractInode::shutdown(ioctx_t* /*ctx*/, int /*how*/)
{
return errno = ENOTSOCK, -1;
}
int AbstractInode::getpeername(ioctx_t* /*ctx*/, uint8_t* /*addr*/,
size_t* /*addrsize*/)
{
return errno = ENOTSOCK, -1;
}
int AbstractInode::getsockname(ioctx_t* /*ctx*/, uint8_t* /*addr*/,
size_t* /*addrsize*/)
{
return errno = ENOTSOCK, -1;
}
} // namespace Sortix