SchOS/include/file.h
Nexoniarz d0f7acf9eb 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.
2026-08-09 13:47:01 +02:00

132 lines
4.9 KiB
C

struct file {
enum { FD_NONE, FD_PIPE, FD_INODE, FD_SOCKET } type;
int ref; // reference count
char readable;
char writable;
// O_NONBLOCK. A property of the open file description, not of the
// descriptor, which is why it lives here and not in myproc()->ofile_flags --
// dup() shares it and F_SETFL on either fd changes it for both, exactly as
// POSIX says.
char nonblock;
struct pipe *pipe;
struct inode *ip;
struct socket *sock;
uint64 off;
};
// ---------------------------------------------------------------------------
// Filesystem operations
//
// One table per filesystem, chosen by iget() from the device number and hung
// off the inode. Everything above this line -- the syscall layer, exec, the
// page cache -- calls the wrappers in fs.c and never learns which filesystem it
// is talking to.
//
// A vtable rather than a chain of `if (dev == SCHFS_DEV)` because there are two
// filesystems today and there will be more: the whole point of the exercise is
// that adding one is a new table, not another branch in fifteen functions.
//
// Every hook takes an inode that is already locked, except ialloc (which has no
// inode yet) and iload (which is what makes one valid in the first place).
// ---------------------------------------------------------------------------
struct stat;
struct fs_ops {
const char *name;
// Fill in the type/size/link fields of a freshly iget()'d inode.
void (*iload)(struct inode *);
// Write those fields back to disk.
void (*istore)(struct inode *);
// The last link is gone and nobody has it open: release the object.
void (*ifree)(struct inode *);
int (*read)(struct inode *, int user_dst, uint64 dst, uint64 off, uint n);
int (*write)(struct inode *, int user_src, uint64 src, uint64 off, uint n);
// Set the length. Growing is allowed only where the filesystem can represent
// the gap; v1 cannot and ignores it.
void (*truncate)(struct inode *, uint64 newsize);
// Directory operations. `poff` is an opaque cookie identifying where the
// entry lives, meaningful only to the same filesystem's dirunlink.
struct inode *(*dirlookup)(struct inode *dp, char *name, uint *poff);
int (*dirlink)(struct inode *dp, char *name, uint64 inum);
int (*dirunlink)(struct inode *dp, char *name, uint off);
int (*dirempty)(struct inode *dp);
// Create an object with no name yet. The caller sets nlink and calls
// dirlink; until it does, the object is unreachable.
struct inode *(*ialloc)(uint dev, short type);
// Read directory entries into a getdents64-shaped stream. `off` is an opaque
// position that starts at 0 and is echoed back; returns bytes written, 0 at
// the end of the directory, or negative on error.
int (*getdents)(struct inode *dp, char *out, int cap, uint64 *off);
};
// In-memory inode.
//
// `inum` and `size` are 64-bit because the filesystem underneath may be: SchFS
// object ids and file sizes are both uint64 on disk, and a 32-bit copy here
// would silently truncate them into a different file. The v1 filesystem uses a
// small fraction of the range and is unaffected.
struct inode {
uint dev;
uint64 inum;
int ref;
struct sleeplock lock;
int valid;
const struct fs_ops *ops; // chosen by iget from dev; never null once held
short type;
short major;
short minor;
short nlink;
uint64 size;
uint rwmode;
uint supermode;
uint showmode;
uint atime;
uint mtime;
uint ctime;
uint addrs[NDIRECT+3];
};
#define I_VALID 0x2
// Device number that means "the mounted SchFS volume". Real disks are 0 and 1,
// and this is deliberately far from them so a stray value never looks like a
// SchFS inode.
#define SCHFS_DEV 64
// Device number that means "the synthetic /proc". Like SCHFS_DEV, it is
// deliberately nowhere near a real disk's 0 or 1.
#define PROCFS_DEV 65
// table mapping major device number to
// device functions
struct devsw {
int (*read)(struct inode*, uint, char*, int);
int (*write)(struct inode*, uint, char*, int);
// Optional. Returns 0 when a read would block, which is the only thing
// fileread needs to know to honour O_NONBLOCK without every device driver
// growing a flags argument. A device that omits this is treated as always
// ready, which is the right answer for a disk and for /dev/zero.
int (*ready)(struct inode*);
// Optional. The last descriptor for this open file is going away. A device
// that hands a program exclusive-feeling control of the hardware needs this
// to put the hardware back -- /dev/fb0 uses it to give the text console its
// screen back when whatever was drawing on it exits.
void (*close)(struct inode*);
// Optional. A descriptor has just been opened on this device. /dev/input
// uses it to discard events that arrived while nobody was listening -- see
// inputopen().
void (*open)(struct inode*);
};
extern struct devsw devsw[];
#define CONSOLE 1
// Major number of /dev/input/eventN. The minor is the device index.
#define INPUT_MAJOR 7