2011-11-16 02:37:29 -05:00
|
|
|
/******************************************************************************
|
|
|
|
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
|
|
|
|
|
|
|
This file is part of Sortix.
|
|
|
|
|
|
|
|
Sortix is free software: you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation, either version 3 of the License, or (at your option) any later
|
|
|
|
version.
|
|
|
|
|
|
|
|
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
|
|
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
io.cpp
|
|
|
|
Provides system calls for input and output.
|
|
|
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
2012-03-21 19:52:29 -04:00
|
|
|
#include <sortix/kernel/platform.h>
|
2011-11-16 02:37:29 -05:00
|
|
|
#include <libmaxsi/error.h>
|
2012-02-24 16:02:01 -05:00
|
|
|
#include <sortix/seek.h>
|
2011-11-16 02:37:29 -05:00
|
|
|
#include "thread.h"
|
|
|
|
#include "process.h"
|
|
|
|
#include "device.h"
|
|
|
|
#include "stream.h"
|
|
|
|
#include "syscall.h"
|
|
|
|
#include "io.h"
|
|
|
|
|
|
|
|
using namespace Maxsi;
|
|
|
|
|
|
|
|
namespace Sortix
|
|
|
|
{
|
|
|
|
namespace IO
|
|
|
|
{
|
|
|
|
struct SysWrite_t
|
|
|
|
{
|
|
|
|
union { size_t align1; int fd; };
|
|
|
|
union { size_t align2; const byte* buffer; };
|
|
|
|
union { size_t align3; size_t count; };
|
|
|
|
};
|
|
|
|
|
|
|
|
STATIC_ASSERT(sizeof(SysWrite_t) <= sizeof(Thread::scstate));
|
|
|
|
|
|
|
|
ssize_t SysWrite(int fd, const byte* buffer, size_t count)
|
|
|
|
{
|
|
|
|
// TODO: Check that buffer is a valid user-space buffer.
|
|
|
|
if ( SSIZE_MAX < count ) { count = SSIZE_MAX; }
|
|
|
|
Process* process = CurrentProcess();
|
|
|
|
Device* dev = process->descriptors.Get(fd);
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !dev ) { Error::Set(EBADF); return -1; }
|
|
|
|
if ( !dev->IsType(Device::STREAM) ) { Error::Set(EBADF); return -1; }
|
2011-11-16 02:37:29 -05:00
|
|
|
DevStream* stream = (DevStream*) dev;
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !stream->IsWritable() ) { Error::Set(EBADF); return -1; }
|
2011-11-16 02:37:29 -05:00
|
|
|
ssize_t written = stream->Write(buffer, count);
|
|
|
|
if ( 0 <= written ) { return written; }
|
2012-01-22 12:49:04 -05:00
|
|
|
if ( Error::Last() != EBLOCKING ) { return -1; }
|
2011-11-16 02:37:29 -05:00
|
|
|
|
|
|
|
// The stream will resume our system call once progress has been
|
|
|
|
// made. Our request is certainly not forgotten.
|
|
|
|
|
|
|
|
// Resume the system call with these parameters.
|
|
|
|
Thread* thread = CurrentThread();
|
|
|
|
thread->scfunc = (void*) SysWrite;
|
|
|
|
SysWrite_t* state = (SysWrite_t*) thread->scstate;
|
|
|
|
state->fd = fd;
|
|
|
|
state->buffer = buffer;
|
|
|
|
state->count = count;
|
|
|
|
thread->scsize = sizeof(SysWrite_t);
|
|
|
|
|
|
|
|
// Now go do something else.
|
|
|
|
Syscall::Incomplete();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SysRead_t
|
|
|
|
{
|
|
|
|
union { size_t align1; int fd; };
|
|
|
|
union { size_t align2; byte* buffer; };
|
|
|
|
union { size_t align3; size_t count; };
|
|
|
|
};
|
|
|
|
|
|
|
|
STATIC_ASSERT(sizeof(SysRead_t) <= sizeof(Thread::scstate));
|
|
|
|
|
|
|
|
ssize_t SysRead(int fd, byte* buffer, size_t count)
|
|
|
|
{
|
|
|
|
// TODO: Check that buffer is a valid user-space buffer.
|
|
|
|
if ( SSIZE_MAX < count ) { count = SSIZE_MAX; }
|
|
|
|
Process* process = CurrentProcess();
|
|
|
|
Device* dev = process->descriptors.Get(fd);
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !dev ) { Error::Set(EBADF); return -1; }
|
|
|
|
if ( !dev->IsType(Device::STREAM) ) { Error::Set(EBADF); return -1; }
|
2011-11-16 02:37:29 -05:00
|
|
|
DevStream* stream = (DevStream*) dev;
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !stream->IsReadable() ) { Error::Set(EBADF); return -1;}
|
2011-11-16 02:37:29 -05:00
|
|
|
ssize_t bytesread = stream->Read(buffer, count);
|
|
|
|
if ( 0 <= bytesread ) { return bytesread; }
|
2012-01-22 12:49:04 -05:00
|
|
|
if ( Error::Last() != EBLOCKING ) { return -1; }
|
2011-11-16 02:37:29 -05:00
|
|
|
|
|
|
|
// The stream will resume our system call once progress has been
|
|
|
|
// made. Our request is certainly not forgotten.
|
|
|
|
|
|
|
|
// Resume the system call with these parameters.
|
|
|
|
Thread* thread = CurrentThread();
|
|
|
|
thread->scfunc = (void*) SysRead;
|
|
|
|
SysRead_t* state = (SysRead_t*) thread->scstate;
|
|
|
|
state->fd = fd;
|
|
|
|
state->buffer = buffer;
|
|
|
|
state->count = count;
|
|
|
|
thread->scsize = sizeof(SysRead_t);
|
|
|
|
|
|
|
|
// Now go do something else.
|
|
|
|
Syscall::Incomplete();
|
2012-03-21 19:52:29 -04:00
|
|
|
return 0;
|
2011-11-16 02:37:29 -05:00
|
|
|
}
|
|
|
|
|
2011-12-26 17:12:12 -05:00
|
|
|
void SysSeek(int fd, off_t* offset, int whence)
|
|
|
|
{
|
|
|
|
// TODO: Validate that offset is a legal user-space off_t!
|
|
|
|
Process* process = CurrentProcess();
|
|
|
|
Device* dev = process->descriptors.Get(fd);
|
|
|
|
if ( !dev ) { Error::Set(EBADF); *offset = -1; return; }
|
|
|
|
if ( !dev->IsType(Device::BUFFER) ) { Error::Set(EBADF); *offset = -1; return; }
|
|
|
|
DevBuffer* buffer = (DevBuffer*) dev;
|
|
|
|
off_t origin;
|
|
|
|
switch ( whence )
|
|
|
|
{
|
|
|
|
case SEEK_SET: origin = 0; break;
|
|
|
|
case SEEK_CUR: origin = buffer->Position(); break;
|
|
|
|
case SEEK_END: origin = buffer->Size(); break;
|
|
|
|
default: Error::Set(EINVAL); *offset = -1; break;
|
|
|
|
}
|
|
|
|
off_t newposition = origin + *offset;
|
|
|
|
if ( newposition < 0 ) { Error::Set(EINVAL); *offset = -1; return; }
|
|
|
|
if ( !buffer->Seek(newposition) ) { *offset = -1; return; }
|
|
|
|
*offset = buffer->Position();
|
|
|
|
}
|
|
|
|
|
2011-11-17 04:22:43 -05:00
|
|
|
int SysClose(int fd)
|
|
|
|
{
|
|
|
|
Process* process = CurrentProcess();
|
|
|
|
Device* dev = process->descriptors.Get(fd);
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !dev ) { Error::Set(EBADF); return -1; }
|
2011-11-17 04:22:43 -05:00
|
|
|
process->descriptors.Free(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-17 14:34:04 -05:00
|
|
|
int SysDup(int fd)
|
|
|
|
{
|
|
|
|
Process* process = CurrentProcess();
|
|
|
|
Device* dev = process->descriptors.Get(fd);
|
2011-11-26 14:56:45 -05:00
|
|
|
if ( !dev ) { Error::Set(EBADF); return -1; }
|
2011-11-17 14:34:04 -05:00
|
|
|
return process->descriptors.Allocate(dev);
|
|
|
|
}
|
|
|
|
|
2011-11-16 02:37:29 -05:00
|
|
|
void Init()
|
|
|
|
{
|
|
|
|
Syscall::Register(SYSCALL_WRITE, (void*) SysWrite);
|
|
|
|
Syscall::Register(SYSCALL_READ, (void*) SysRead);
|
2011-11-17 04:22:43 -05:00
|
|
|
Syscall::Register(SYSCALL_CLOSE, (void*) SysClose);
|
2011-11-17 14:34:04 -05:00
|
|
|
Syscall::Register(SYSCALL_DUP, (void*) SysDup);
|
2011-12-26 17:12:12 -05:00
|
|
|
Syscall::Register(SYSCALL_SEEK, (void*) SysSeek);
|
2011-11-16 02:37:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|