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. // Mounted snapshots. Same block device, an older tree root, read-only. // // Each needs its own device number because an inode is identified by // (dev, inum) and a snapshot's object ids are the *same numbers* as the live // tree's -- one number would make the views alias in the inode cache and hand // a reader whichever was cached first. So a slot is a device: SCHFS_SNAP_DEV // through SCHFS_SNAP_DEV + SCHFS_SNAP_SLOTS - 1. // // Four rather than one because "one at a time" is an odd thing to explain and // the cost is two scratch pages per slot, allocated on first use. Four rather // than many because each is a device number and they are a fixed, small space. #define SCHFS_SNAP_SLOTS 4 #define SCHFS_SNAP_DEV 66 #define SCHFS_IS_SNAP_DEV(d) \ ((d) >= SCHFS_SNAP_DEV && (d) < SCHFS_SNAP_DEV + SCHFS_SNAP_SLOTS) #define PROCFS_DEV 65 // table mapping major device number to // device functions struct devsw { int (*read)(struct inode*, uint64, char*, int); int (*write)(struct inode*, uint64, 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