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
|
|
@ -37,12 +37,18 @@
|
|||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "errno.h"
|
||||
#include "blkdev.h"
|
||||
|
||||
// Inode numbering. Two directories and a flat table of nodes, so the number is
|
||||
// either a directory id or an index into devnodes[] offset past them.
|
||||
#define DEVFS_ROOT 1ULL
|
||||
#define DEVFS_INPUT 2ULL /* the /dev/input directory */
|
||||
#define DEVFS_FILE0 16ULL /* devnodes[0] is inode 16 */
|
||||
// Disks and partitions the block layer found: /dev/sata0, /dev/sata0p1,
|
||||
// /dev/usb0p1, named as it named them. Major 8 like hda, minor the block
|
||||
// device number, and inode DEVFS_BLK0 plus that number. Not in devnodes[],
|
||||
// because they come and go: a stick plugged in appears, pulled out vanishes.
|
||||
#define DEVFS_BLK0 4096ULL
|
||||
|
||||
// Which directory a node lives in. Only two exist and a third would want a real
|
||||
// tree rather than a flag, which is the point at which this file should grow
|
||||
|
|
@ -140,6 +146,12 @@ devfs_iload(struct inode *ip)
|
|||
ip->nlink = 1;
|
||||
ip->major = devnodes[i].major;
|
||||
ip->minor = devnodes[i].minor;
|
||||
} else if (ip->inum >= DEVFS_BLK0 &&
|
||||
blkdev_present((uint)(ip->inum - DEVFS_BLK0))) {
|
||||
ip->type = T_DEV;
|
||||
ip->nlink = 1;
|
||||
ip->major = 8;
|
||||
ip->minor = (short)(ip->inum - DEVFS_BLK0);
|
||||
} else {
|
||||
ip->type = 0; /* no such node */
|
||||
ip->nlink = 0;
|
||||
|
|
@ -185,6 +197,11 @@ devfs_dirlookup(struct inode *dp, char *name, uint *poff)
|
|||
if (devnodes[i].dir == want && streq(name, devnodes[i].name))
|
||||
return iget(DEVFS_DEV, DEVFS_FILE0 + (uint64)i);
|
||||
|
||||
if (want == D_ROOT)
|
||||
for (int k = 0, b; (b = blkdev_nth(k)) >= 0; k++)
|
||||
if (streq(name, blkdev_name((uint)b)))
|
||||
return iget(DEVFS_DEV, DEVFS_BLK0 + (uint64)b);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -270,6 +287,16 @@ devfs_getdents(struct inode *dp, char *out, int cap, uint64 *poff)
|
|||
base++;
|
||||
}
|
||||
|
||||
if (want == D_ROOT)
|
||||
for (int k = 0, b; (b = blkdev_nth(k)) >= 0; k++) {
|
||||
if (n > base) { base++; continue; }
|
||||
if (emit(out, cap, &used, DEVFS_BLK0 + (uint64)b, 6 /* DT_BLK */,
|
||||
blkdev_name((uint)b)) < 0)
|
||||
goto out;
|
||||
n++;
|
||||
base++;
|
||||
}
|
||||
|
||||
out:
|
||||
*poff = n;
|
||||
return used;
|
||||
|
|
|
|||
1996
kernel/fs/fat/fat.c
Normal file
1996
kernel/fs/fat/fat.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -276,11 +276,16 @@ filewrite(struct file *f, char *addr, int n)
|
|||
|
||||
if(r < 0)
|
||||
break;
|
||||
if(r != n1)
|
||||
panic("short filewrite");
|
||||
i += r;
|
||||
// Short means full. v1 and SchFS never wrote short, which is why this
|
||||
// was a panic; FAT does, when the volume or its root directory fills.
|
||||
// write(2) reports what went in, and the next call gets the error.
|
||||
if(r != n1)
|
||||
break;
|
||||
}
|
||||
return i == n ? n : -1;
|
||||
if(i > 0)
|
||||
return i;
|
||||
return r < 0 ? r : -1;
|
||||
}
|
||||
panic("filewrite");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include "buf.h"
|
||||
#include "fsstat.h"
|
||||
#include "file.h"
|
||||
#include "fat.h"
|
||||
|
||||
extern uint uptime(void);
|
||||
|
||||
|
|
@ -277,6 +278,7 @@ static const struct fs_ops v1_ops = {
|
|||
|
||||
// Defined in fs/schfs/vfs.c, which is only linked when SchFS is built in.
|
||||
extern const struct fs_ops schfs_ops;
|
||||
extern const struct fs_ops fat_ops;
|
||||
// Defined in fs/procfs.c -- the synthetic /proc.
|
||||
extern const struct fs_ops procfs_ops;
|
||||
extern const struct fs_ops devfs_ops;
|
||||
|
|
@ -290,7 +292,7 @@ extern const struct fs_ops devfs_ops;
|
|||
int
|
||||
fs_readonly(uint dev)
|
||||
{
|
||||
return SCHFS_IS_SNAP_DEV(dev);
|
||||
return SCHFS_IS_SNAP_DEV(dev) || (FAT_IS_DEV(dev) && fat_readonly(dev));
|
||||
}
|
||||
|
||||
static const struct fs_ops *
|
||||
|
|
@ -302,6 +304,7 @@ ops_for(uint dev)
|
|||
if (SCHFS_IS_ANY_DEV(dev)) return &schfs_ops;
|
||||
if (dev == PROCFS_DEV) return &procfs_ops;
|
||||
if (dev == DEVFS_DEV) return &devfs_ops;
|
||||
if (FAT_IS_DEV(dev)) return &fat_ops;
|
||||
return &v1_ops;
|
||||
}
|
||||
|
||||
|
|
@ -468,6 +471,38 @@ iget(uint dev, uint64 inum)
|
|||
return ip;
|
||||
}
|
||||
|
||||
// Is (dev, inum) in the inode cache with a reference? For a filesystem that
|
||||
// keeps its own table of inode numbers and wants to know which it can drop.
|
||||
int
|
||||
iheld(uint dev, uint64 inum)
|
||||
{
|
||||
int held = 0;
|
||||
acquire(&itable.lock);
|
||||
for (struct inode *ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++)
|
||||
if (ip->ref > 0 && ip->dev == dev && ip->inum == inum) {
|
||||
held = 1;
|
||||
break;
|
||||
}
|
||||
release(&itable.lock);
|
||||
return held;
|
||||
}
|
||||
|
||||
// Is anything on dev held, other than `except` (a mounted root, which the
|
||||
// mount itself holds)? For umount.
|
||||
int
|
||||
ibusy(uint dev, uint64 except)
|
||||
{
|
||||
int busy = 0;
|
||||
acquire(&itable.lock);
|
||||
for (struct inode *ip = &itable.inode[0]; ip < &itable.inode[NINODE]; ip++)
|
||||
if (ip->ref > 0 && ip->dev == dev && ip->inum != except) {
|
||||
busy = 1;
|
||||
break;
|
||||
}
|
||||
release(&itable.lock);
|
||||
return busy;
|
||||
}
|
||||
|
||||
struct inode *
|
||||
idup(struct inode *ip)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue