Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
This commit is contained in:
commit
d0f7acf9eb
275 changed files with 51417 additions and 0 deletions
294
kernel/fs/file.c
Normal file
294
kernel/fs/file.c
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "stat.h"
|
||||
#include "param.h"
|
||||
#include "fs.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "file.h"
|
||||
#include "posix.h"
|
||||
#include "errno.h"
|
||||
|
||||
extern struct devsw devsw[];
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct file file[NFILE];
|
||||
} ftable;
|
||||
|
||||
void
|
||||
fileinit(void)
|
||||
{
|
||||
initlock(&ftable.lock, "ftable");
|
||||
}
|
||||
|
||||
struct file*
|
||||
filealloc(void)
|
||||
{
|
||||
struct file *f;
|
||||
|
||||
acquire(&ftable.lock);
|
||||
for(f = ftable.file; f < ftable.file + NFILE; f++){
|
||||
if(f->ref == 0){
|
||||
f->ref = 1;
|
||||
// Cleared here rather than left to the caller: every creator sets
|
||||
// readable/writable/off, but only open() sets nonblock, so a struct file
|
||||
// recycled from a non-blocking device would hand its flag to the next
|
||||
// pipe or socket that happened to land in this slot.
|
||||
f->nonblock = 0;
|
||||
release(&ftable.lock);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
release(&ftable.lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct file*
|
||||
filedup(struct file *f)
|
||||
{
|
||||
acquire(&ftable.lock);
|
||||
if(f->ref < 1)
|
||||
panic("filedup");
|
||||
f->ref++;
|
||||
release(&ftable.lock);
|
||||
return f;
|
||||
}
|
||||
|
||||
void
|
||||
fileclose(struct file *f)
|
||||
{
|
||||
struct file ff;
|
||||
|
||||
acquire(&ftable.lock);
|
||||
if(f->ref < 1)
|
||||
panic("fileclose");
|
||||
if(--f->ref > 0){
|
||||
release(&ftable.lock);
|
||||
return;
|
||||
}
|
||||
ff = *f;
|
||||
f->ref = 0;
|
||||
f->type = FD_NONE;
|
||||
release(&ftable.lock);
|
||||
|
||||
if(ff.type == FD_PIPE)
|
||||
pipeclose(ff.pipe, ff.writable);
|
||||
else if(ff.type == FD_SOCKET)
|
||||
sockclose(ff.sock);
|
||||
else if(ff.type == FD_INODE){
|
||||
// Give a device a chance to put the hardware back before the inode goes.
|
||||
// Done here rather than in exit() because a program that closes the
|
||||
// framebuffer without exiting has finished with it just as thoroughly.
|
||||
if(ff.ip && ff.ip->type == T_DEV && ff.ip->major >= 0 &&
|
||||
ff.ip->major < NDEV && devsw[ff.ip->major].close)
|
||||
devsw[ff.ip->major].close(ff.ip);
|
||||
|
||||
begin_op();
|
||||
iput(ff.ip);
|
||||
end_op();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
filestat(struct file *f, struct stat *st)
|
||||
{
|
||||
if(f->type == FD_INODE){
|
||||
ilock(f->ip);
|
||||
stati(f->ip, st);
|
||||
iunlock(f->ip);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
fileread(struct file *f, char *addr, int n)
|
||||
{
|
||||
int r;
|
||||
|
||||
if(f->readable == 0)
|
||||
return -1;
|
||||
|
||||
// O_NONBLOCK on a pipe or a socket. Reported here rather than inside
|
||||
// piperead/sockread so that the blocking implementations stay unchanged:
|
||||
// this is a check of the same condition their sleep loops wait on, made one
|
||||
// level up. Writes still block -- fcntl documents that.
|
||||
if(f->type == FD_PIPE){
|
||||
if(f->nonblock && !(pipe_poll(f->pipe, f->readable, f->writable) & POLLIN))
|
||||
return -EAGAIN;
|
||||
return piperead(f->pipe, addr, n);
|
||||
}
|
||||
|
||||
if(f->type == FD_SOCKET){
|
||||
if(f->nonblock && !sockreadable(f->sock))
|
||||
return -EAGAIN;
|
||||
return sockread(f->sock, addr, n);
|
||||
}
|
||||
|
||||
if(f->type == FD_INODE){
|
||||
ilock(f->ip);
|
||||
if(f->ip->type == 3){
|
||||
if(f->ip->major < 0 || f->ip->major >= NDEV || !devsw[f->ip->major].read){
|
||||
iunlock(f->ip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// O_NONBLOCK. Without this a game that opens an input device
|
||||
// non-blocking and drains it once per frame stops dead on the first
|
||||
// frame -- it blocks in read() waiting for a key, and the result looks
|
||||
// exactly like a hung renderer rather than a blocking read.
|
||||
if(f->nonblock && devsw[f->ip->major].ready &&
|
||||
!devsw[f->ip->major].ready(f->ip)){
|
||||
iunlock(f->ip);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
r = devsw[f->ip->major].read(f->ip, f->off, addr, n);
|
||||
if(r > 0)
|
||||
f->off += r;
|
||||
iunlock(f->ip);
|
||||
return r;
|
||||
}
|
||||
|
||||
if((f->ip->rwmode & 2) == 0){
|
||||
cprintf("Schunix: permission denied (read)\n");
|
||||
iunlock(f->ip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The "recycle bin" check that used to sit here is gone.
|
||||
//
|
||||
// It made a file that still existed, still had a name and still had blocks
|
||||
// refuse every read with a printf -- a state no POSIX program is prepared
|
||||
// for. It was reachable only through `delete`, a NO_LINUX syscall from the
|
||||
// third-party filesystem this file derives from, which the decision to keep
|
||||
// only Linux numbering had already orphaned.
|
||||
//
|
||||
// The on-disk `showmode` field stays for now: removing it changes the inode
|
||||
// layout, and this whole file is being replaced by SchFS. It is simply
|
||||
// never consulted.
|
||||
|
||||
if((r = readi(f->ip, 0, (uint64)addr, f->off, n)) > 0)
|
||||
f->off += r;
|
||||
iunlock(f->ip);
|
||||
return r;
|
||||
}
|
||||
|
||||
panic("fileread");
|
||||
}
|
||||
|
||||
int
|
||||
filewrite(struct file *f, char *addr, int n)
|
||||
{
|
||||
int r;
|
||||
|
||||
if(f->writable == 0)
|
||||
return -1;
|
||||
|
||||
if(f->type == FD_PIPE)
|
||||
return pipewrite(f->pipe, addr, n);
|
||||
|
||||
if(f->type == FD_SOCKET)
|
||||
return sockwrite(f->sock, addr, n);
|
||||
|
||||
if(f->type == FD_INODE){
|
||||
ilock(f->ip);
|
||||
|
||||
if(f->ip->type == 3){
|
||||
if(f->ip->major < 0 || f->ip->major >= NDEV || !devsw[f->ip->major].write){
|
||||
iunlock(f->ip);
|
||||
return -1;
|
||||
}
|
||||
r = devsw[f->ip->major].write(f->ip, f->off, addr, n);
|
||||
if(r > 0)
|
||||
f->off += r;
|
||||
iunlock(f->ip);
|
||||
return r;
|
||||
}
|
||||
|
||||
if((f->ip->rwmode & 1) == 0){
|
||||
cprintf("Schunix: permission denied (write)\n");
|
||||
iunlock(f->ip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Recycle-bin check removed here too; see the note in fileread().
|
||||
iunlock(f->ip);
|
||||
|
||||
int max = ((LOGSIZE-1-1-2) / 2) * 512;
|
||||
int i = 0;
|
||||
while(i < n){
|
||||
int n1 = n - i;
|
||||
if(n1 > max)
|
||||
n1 = max;
|
||||
|
||||
begin_op();
|
||||
ilock(f->ip);
|
||||
if ((r = writei(f->ip, 1, (uint64)(addr + i), f->off, n1)) > 0)
|
||||
f->off += r;
|
||||
|
||||
iunlock(f->ip);
|
||||
end_op();
|
||||
|
||||
if(r < 0)
|
||||
break;
|
||||
if(r != n1)
|
||||
panic("short filewrite");
|
||||
i += r;
|
||||
}
|
||||
return i == n ? n : -1;
|
||||
}
|
||||
panic("filewrite");
|
||||
}
|
||||
|
||||
// poll(2) readiness for one open file. Returns a POLL* bitmask restricted
|
||||
// to the requested events (plus error/hangup conditions, which POSIX says
|
||||
// are always reported). Pipes and the console tty have real state; other
|
||||
// inodes (regular files, block-ish devices) are always ready, per POSIX.
|
||||
int
|
||||
file_poll(struct file *f, int events)
|
||||
{
|
||||
if(f->type == FD_PIPE)
|
||||
return pipe_poll(f->pipe, f->readable, f->writable) &
|
||||
(events | POLLERR | POLLHUP);
|
||||
|
||||
if(f->type == FD_SOCKET){
|
||||
int rev = 0;
|
||||
if(sockreadable(f->sock))
|
||||
rev |= POLLIN;
|
||||
// A stream socket is writable whenever it has a peer. The buffer can
|
||||
// still be full, in which case the write blocks -- same latitude poll()
|
||||
// has always had, and the same answer a pipe gives.
|
||||
if(sock_hup(f->sock))
|
||||
rev |= POLLHUP;
|
||||
else
|
||||
rev |= POLLOUT;
|
||||
return rev & (events | POLLERR | POLLHUP);
|
||||
}
|
||||
|
||||
if(f->type == FD_INODE){
|
||||
// An input device is only readable when an event is waiting. Reporting it
|
||||
// always-ready -- which is what the generic answer below does -- turns a
|
||||
// poll loop into a busy loop that blocks in read().
|
||||
if(f->ip && f->ip->type == T_DEV && f->ip->major == INPUT_MAJOR){
|
||||
int rev = 0;
|
||||
if((events & POLLIN) && f->readable && input_ready(f->ip->minor))
|
||||
rev |= POLLIN;
|
||||
if(events & POLLOUT)
|
||||
rev |= POLLOUT;
|
||||
return rev;
|
||||
}
|
||||
|
||||
if(f->ip && f->ip->type == T_DEV && f->ip->major == CONSOLE){
|
||||
int rev = 0;
|
||||
if((events & POLLIN) && f->readable && console_input_ready())
|
||||
rev |= POLLIN;
|
||||
if((events & POLLOUT) && f->writable)
|
||||
rev |= POLLOUT;
|
||||
return rev;
|
||||
}
|
||||
return events & (POLLIN | POLLOUT);
|
||||
}
|
||||
return POLLNVAL;
|
||||
}
|
||||
Loading…
Reference in a new issue