The last three phases of the filesystem, which were written together and
touch the same files throughout -- kmount.c, objw.c and vfs.c are each
rewritten by all three -- so they land as one commit rather than three
that could not be built separately.
Durability. A transaction that reaches the platter out of order is worse
than one that never reached it: the uberblock can name a tree whose
blocks are still in the drive's write cache. bdirty/bflush give the
buffer layer an explicit barrier, ideflush issues ATA FLUSH CACHE and
usb_storage_flush the SCSI SYNCHRONIZE CACHE that means the same thing,
and a commit now waits for the data before it writes the root that
points at it. The uberblock groups are read newest-first with fallback,
so a volume whose newest roots are torn mounts at an older one and says
which generations were lost.
Reflink. Extents carry a refcount, so two objects can name the same
blocks and neither frees them while the other holds them. Exposed as
FICLONE -- Linux's ioctl value, so `cp --reflink` works unmodified --
and as cmd/reflink for the case where there is no cp to hand.
Attributes, and the one genuinely new idea here. attr.c stores named
values on an object, reachable through setxattr/getxattr/removexattr at
Linux's syscall numbers. On top of that, schfs_index declares an index
over an attribute name and schfs_query answers "which objects carry this
value" from the B-tree rather than by walking. A query against an
unindexed attribute is refused with an error, never silently answered by
a full scan: a query that quietly becomes a scan is worse than one that
admits it cannot help. cmd/schattr drives both.
Three bugs fixed on the way, each found by the numbers being wrong
rather than by inspection:
- statfs's f_fsid was declared int64[2]. Linux's fsid_t is two *ints*,
so every field after it landed one slot late: userland read f_frsize
out of f_namelen, got 255, and df reported a 32 MB root filesystem
as 1992 KB. It hid for as long as it did because the fields used to
be invented constants, so wrong numbers looked like the usual ones.
- struct inode's nlink was a short against SchFS's uint32, so a file
past 32767 names came back negative -- and iput frees an object
whose count reaches zero. It is an int now, signed on purpose, so an
underflow lands somewhere a check can see rather than wrapping.
- filewrite split every write to fit v1's log. That limit belongs to
the journal, not to writing, and SchFS makes one transaction of
whatever it is handed: a 1 MB write was becoming 170 commits and
170 flushes for nothing. fs_ops carries max_write now, and SchFS
sets no limit.
Also here: a real mount table, so `mount -t schfs /dev/hdb /mnt` shows
up in /proc/mounts -- df looks a path up there before it calls statfs at
all, so a mount missing from that list was invisible to it -- several
volumes mounted at once, snapshots mounted by name, and /proc/fsstat
with an rdtsc-based counter, which is the only instrument fine enough to
see inside a single syscall.
Verified: `make schfs-check` passes in full, including the sweep that
crashes the volume at each of 119 points mid-transaction and requires
every one to mount with exactly its committed data, and the case where
both newest uberblocks are rubbish and the volume still comes back. The
headless boot check passes with 27 [ok] and no failures, on a SchFS root.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
62 lines
3 KiB
C
62 lines
3 KiB
C
// Error numbers, matching the Linux x86_64 ABI values so that musl-linked
|
|
// programs interpret syscall failures correctly. Kernel code returns these
|
|
// negated (e.g. `return -ENOSYS;`), exactly like Linux does internally.
|
|
#ifndef SCHOS_ERRNO_H
|
|
#define SCHOS_ERRNO_H
|
|
|
|
#define EPERM 1 // Operation not permitted
|
|
#define ENOENT 2 // No such file or directory
|
|
#define ESRCH 3 // No such process
|
|
#define EINTR 4 // Interrupted system call
|
|
#define EIO 5 // I/O error
|
|
#define ENXIO 6 // No such device or address
|
|
#define E2BIG 7 // Argument list too long
|
|
#define ENOEXEC 8 // Exec format error
|
|
#define EBADF 9 // Bad file descriptor
|
|
#define ECHILD 10 // No child processes
|
|
#define EAGAIN 11 // Try again
|
|
#define ENOMEM 12 // Out of memory
|
|
#define EACCES 13 // Permission denied
|
|
#define EFAULT 14 // Bad address
|
|
#define ENOTBLK 15 // Block device required
|
|
#define EBUSY 16 // Device or resource busy
|
|
#define EEXIST 17 // File exists
|
|
#define EXDEV 18 // Cross-device link
|
|
#define ENODEV 19 // No such device
|
|
#define ENOTDIR 20 // Not a directory
|
|
#define EISDIR 21 // Is a directory
|
|
#define EINVAL 22 // Invalid argument
|
|
#define ENFILE 23 // File table overflow
|
|
#define EMFILE 24 // Too many open files
|
|
#define ENOTTY 25 // Not a typewriter
|
|
#define EFBIG 27 // File too large
|
|
#define ENOSPC 28 // No space left on device
|
|
#define ESPIPE 29 // Illegal seek
|
|
#define EROFS 30 // Read-only file system
|
|
#define EMLINK 31 // Too many links
|
|
#define EPIPE 32 // Broken pipe
|
|
#define ERANGE 34 // Math result not representable
|
|
#define ENAMETOOLONG 36 // File name too long
|
|
#define ENOSYS 38 // Function not implemented
|
|
#define ENOTEMPTY 39 // Directory not empty
|
|
#define ELOOP 40 // Too many symbolic links encountered
|
|
|
|
// Sockets. Same Linux x86_64 values, so strerror() in a musl-linked program
|
|
// prints the message the programmer expected rather than a neighbouring one.
|
|
#define ENOTSOCK 88 // Socket operation on non-socket
|
|
#define EDESTADDRREQ 89 // Destination address required
|
|
#define EMSGSIZE 90 // Message too long
|
|
#define EPROTOTYPE 91 // Protocol wrong type for socket
|
|
#define ENOPROTOOPT 92 // Protocol not available
|
|
#define EPROTONOSUPPORT 93 // Protocol not supported
|
|
#define EOPNOTSUPP 95 // Operation not supported on transport endpoint
|
|
#define EAFNOSUPPORT 97 // Address family not supported by protocol
|
|
#define EADDRINUSE 98 // Address already in use
|
|
#define EADDRNOTAVAIL 99 // Cannot assign requested address
|
|
#define ECONNRESET 104 // Connection reset by peer
|
|
#define EISCONN 106 // Transport endpoint is already connected
|
|
#define ENODATA 61 // No data available -- no such attribute
|
|
#define ENOTCONN 107 // Transport endpoint is not connected
|
|
#define ECONNREFUSED 111 // Connection refused
|
|
|
|
#endif // SCHOS_ERRNO_H
|