sortix-mirror/kernel/disk/node.cpp
Jonas 'Sortie' Termansen 568c97c77f 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-12-04 23:56:46 +01:00

188 lines
5.3 KiB
C++

/*
* Copyright (c) 2011-2017 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.
*
* disk/node.cpp
* Inode adapter for harddisks.
*/
#include <sys/types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sortix/stat.h>
#include <sortix/kernel/harddisk.h>
#include <sortix/kernel/inode.h>
#include <sortix/kernel/kthread.h>
#include <sortix/kernel/log.h>
#include "node.h"
namespace Sortix {
PortNode::PortNode(Harddisk* harddisk, uid_t owner, gid_t group, mode_t mode,
dev_t dev, ino_t /*ino*/)
{
this->harddisk = harddisk;
inode_type = INODE_TYPE_FILE;
this->dev = dev;
this->ino = (ino_t) this;
this->stat_uid = owner;
this->stat_gid = group;
this->type = S_IFBLK;
this->stat_size = harddisk->GetSize();
this->stat_mode = (mode & S_SETABLE) | this->type;
this->stat_blksize = harddisk->GetBlockSize();
this->stat_blocks = harddisk->GetBlockCount();
}
PortNode::~PortNode()
{
// TODO: Ownership of `port'.
}
int PortNode::sync(ioctx_t* ctx)
{
return harddisk->sync(ctx);
}
int PortNode::truncate(ioctx_t* /*ctx*/, off_t length)
{
if ( length != harddisk->GetSize() )
return errno = EPERM, -1;
return 0;
}
off_t PortNode::lseek(ioctx_t* /*ctx*/, off_t offset, int whence)
{
if ( whence == SEEK_END && offset == 0 )
return (off_t) harddisk->GetSize();
return errno = EINVAL, -1;
}
ssize_t PortNode::pread(ioctx_t* ctx, uint8_t* buf, size_t count, off_t off)
{
return harddisk->pread(ctx, (unsigned char*) buf, count, off);
}
ssize_t PortNode::pwrite(ioctx_t* ctx, const uint8_t* buf, size_t count,
off_t off)
{
return harddisk->pwrite(ctx, (const unsigned char*) buf, count, off);
}
ssize_t PortNode::tcgetblob(ioctx_t* ctx, const char* name, void* buffer,
size_t count)
{
const void* result_pointer = NULL;
size_t result_size = 0;
char string[sizeof(uintmax_t) * 3];
static const char index[] = "harddisk-driver\0"
"harddisk-model\0"
"harddisk-serial\0"
"harddisk-revision\0"
"harddisk-size\0"
"harddisk-block-count\0"
"harddisk-block-size\0"
"harddisk-cylinders\0"
"harddisk-heads\0"
"harddisk-sectors\0"
"harddisk-ata-identify\0";
if ( !name )
{
result_pointer = index;
result_size = sizeof(index) - 1;
}
else if ( !strcmp(name, "harddisk-driver") )
{
result_pointer = harddisk->GetDriver();
result_size = strlen((const char*) result_pointer);
}
else if ( !strcmp(name, "harddisk-model") )
{
result_pointer = harddisk->GetModel();
result_size = strlen((const char*) result_pointer);
}
else if ( !strcmp(name, "harddisk-serial") )
{
result_pointer = harddisk->GetSerial();
result_size = strlen((const char*) result_pointer);
}
else if ( !strcmp(name, "harddisk-revision") )
{
result_pointer = harddisk->GetRevision();
result_size = strlen((const char*) result_pointer);
}
else if ( !strcmp(name, "harddisk-size") )
{
snprintf(string, sizeof(string), "%" PRIiOFF, harddisk->GetSize());
result_pointer = string;
result_size = strlen(string);
}
else if ( !strcmp(name, "harddisk-block-count") )
{
snprintf(string, sizeof(string), "%" PRIiBLKCNT, harddisk->GetBlockCount());
result_pointer = string;
result_size = strlen(string);
}
else if ( !strcmp(name, "harddisk-block-size") )
{
snprintf(string, sizeof(string), "%" PRIiBLKSIZE, harddisk->GetBlockSize());
result_pointer = string;
result_size = strlen(string);
}
else if ( !strcmp(name, "harddisk-cylinders") )
{
snprintf(string, sizeof(string), "%" PRIu16, harddisk->GetCylinderCount());
result_pointer = string;
result_size = strlen(string);
}
else if ( !strcmp(name, "harddisk-heads") )
{
snprintf(string, sizeof(string), "%" PRIu16, harddisk->GetHeadCount());
result_pointer = string;
result_size = strlen(string);
}
else if ( !strcmp(name, "harddisk-sectors") )
{
snprintf(string, sizeof(string), "%" PRIu16, harddisk->GetSectorCount());
result_pointer = string;
result_size = strlen(string);
}
else if ( !strcmp(name, "harddisk-ata-identify") )
{
if ( !(result_pointer = harddisk->GetATAIdentify(&result_size)) )
return -1;
}
else
{
return errno = ENOENT, -1;
}
if ( SSIZE_MAX < result_size )
return errno = EOVERFLOW, -1;
if ( buffer && count < result_size )
return errno = ERANGE, -1;
if ( buffer && !ctx->copy_to_dest(buffer, result_pointer, result_size) )
return -1;
return (ssize_t) result_size;
}
} // namespace Sortix