fat: FAT12, FAT16 and FAT32 with long names, read and write
The filesystem every other system can read: a USB stick, an SD card, the EFI system partition. It is here first for the hardware report, which has to leave a machine on something that can be read on another one. mount -t vfat /dev/usb0p1 /mnt (msdos and fat work too; -o ro) Written from Microsoft's specification, and matched to Linux vfat where the specification leaves a choice: 8.3 names with the NT lowercase bits, long-name entries otherwise, Linux's alias rule (REPORTS, then BIGNUM~1), case-insensitive lookup, UTC times, the boot-sector dirty bit, every FAT copy and FSInfo kept up to date. It uses its own sector cache over the new blkdev_xfer, since a partition need not be a whole number of 4 KB blocks, and every operation that changes something reaches the disk before it returns. Inode numbers come from the entry position, or the first cluster for a directory, plus a small table for entries that moved. Around it: - /dev lists every disk and partition the block layer found (major 8, minor = block device), and raw reads and writes on them go by sector. - The ESP and MBR FAT partitions are registered, typed so the root probe passes over them. MBR entries with a bad status byte are skipped, so a superfloppy's boot code is not read as a partition table. - statfs, sync, fsync, umount (EBUSY while in use), shutdown, reboot and the power button all know about FAT. - create undoes and reports a failed ialloc or dirlink (ENOSPC, EINVAL, EPERM) instead of panicking; fs_ops gains refuse_types and no_hardlinks. - filewrite returns a short write instead of panicking on it. - utimensat is implemented. As a stub returning 0 it made toybox touch believe the file existed, so `touch newfile` created nothing on any filesystem. Tested in QEMU against mkfs.vfat images (FAT12 floppy, FAT16 on MBR, FAT32 superfloppy and GPT ESP) by writing, renaming, deleting, filling to ENOSPC and truncating there, then checking with fsck.fat -n and mtools on the host: all clean, contents match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
d90a72e6d3
commit
edb560de66
15 changed files with 2580 additions and 50 deletions
|
|
@ -30,11 +30,28 @@ struct fs_ops {
|
|||
|
||||
struct inode *(*ialloc)(uint dev, short type);
|
||||
int (*getdents)(struct inode *dp, char *out, int cap, uint64 *off);
|
||||
|
||||
uint max_write; // largest write in one call; 0 for no limit
|
||||
uint refuse_types; // 1 << T_* the filesystem cannot store: EPERM
|
||||
int no_hardlinks; // link() fails with EPERM
|
||||
};
|
||||
```
|
||||
|
||||
Two implementations exist: `schfs_ops` (`kernel/fs/schfs/vfs.c`) and `procfs_ops`
|
||||
(`kernel/fs/procfs.c`).
|
||||
The implementations: `schfs_ops` (`kernel/fs/schfs/vfs.c`), `fat_ops`
|
||||
(`kernel/fs/fat/fat.c`, see [fat.md](fat.md)), `procfs_ops`
|
||||
(`kernel/fs/procfs.c`) and `devfs_ops` (`kernel/fs/devfs.c`).
|
||||
|
||||
**Creation can fail, and is undone when it does.** `ialloc` and `dirlink` used
|
||||
to panic on failure, because on v1 and SchFS they cannot fail for a reason the
|
||||
caller could have avoided. FAT can: the volume fills, the fixed root directory
|
||||
of FAT16 holds 512 names, a name holds a character FAT does not allow. So
|
||||
`createat_x()` gives the object back and reports `ENOSPC`, `EINVAL`, `EPERM` or
|
||||
`EROFS`, and `open`, `mkdir`, `mknod` and `symlink` return that errno instead of
|
||||
`ENOENT`.
|
||||
|
||||
**A short write is a full disk.** `filewrite()` panicked on one, which held as
|
||||
long as no filesystem ever wrote short. Now `write(2)` returns what went in, and
|
||||
the next call gets the error.
|
||||
|
||||
`poff` is deliberately **an opaque cookie** rather than a byte offset —
|
||||
meaningful only to the same filesystem's `dirunlink`. A directory does not have
|
||||
|
|
@ -102,6 +119,10 @@ during path resolution. `/proc` is mounted by init:
|
|||
mount("proc", "/proc", "proc", 0, 0);
|
||||
```
|
||||
|
||||
The filesystem types are `proc`, `devtmpfs`, `schfs` and `vfat` (also accepted
|
||||
as `msdos` and `fat`). Unmounting a FAT volume while anything on it is open, or
|
||||
is some process's working directory, fails with `EBUSY`.
|
||||
|
||||
## procfs
|
||||
|
||||
`kernel/fs/procfs.c`. A real filesystem behind the same vtable, not a set of
|
||||
|
|
@ -165,7 +186,14 @@ A node exists because the kernel has the device, or it does not exist.
|
|||
|
||||
`console`, `null`, `keyboard`, `zero`, `full`, `random`, `urandom`, `tty`,
|
||||
`stdin`, `stdout`, `stderr`, `fb0`, `mouse`, `hda`, `hdb`, and `input/event0`
|
||||
and `input/event1` in one subdirectory. Minors under major 1 follow Linux's
|
||||
and `input/event1` in one subdirectory.
|
||||
|
||||
Every disk and partition the block layer registered is there too, under the
|
||||
name it was given (`sata0`, `sata0p2`, `nvme0p1`, `usb0p1`): major 8, minor its
|
||||
block device number, listed only while it is present. Reads and writes go
|
||||
through `blkdev_xfer` in whole sectors, end at the end of the device, and do
|
||||
not pass through the buffer cache. A partition need not be a whole number of
|
||||
4 KB blocks, and the buffer cache's last block of one would run into the next. Minors under major 1 follow Linux's
|
||||
mem-major layout where it costs nothing.
|
||||
|
||||
The raw disks are created whether or not a drive is present: opening one with
|
||||
|
|
|
|||
Loading…
Reference in a new issue