dev: raw disks are 64-bit, so a real drive stops wrapping at 4 GB
struct file already carried a uint64 offset and bread already took a uint64 block number. The narrowing was one boundary in the middle: devsw's read and write took a 32-bit uint, so fileread passed a 64-bit f->off into a 32-bit parameter and the top half was discarded. The effect was not a refusal at 4 GB but silent wraparound past it. A read at 5 GB returned the bytes at 1 GB, reporting success. disk.c had a DISK_MAX_BYTES constant documenting the limit, and nothing anywhere referenced it -- the cap was described but never enforced, which is the worst of the three options. The signature is now uint64 across all nine files that carry it, and diskread/diskwrite no longer narrow the block number on the way to bread. Widening this is a prerequisite for NVMe and for any real disk: namespaces start well above 4 GB and the failure mode reads valid data from the wrong place. Verified end to end rather than by inspection. A 6 GB sparse image with "PAST4GB-MARKER-OK" written at byte 5368709120, attached as /dev/hdb to a ramdisk-root kernel so device 1 was free: # dd if=/dev/hdb bs=4096 skip=1310720 count=1 | od -c | head -2 0000000 P A S T 4 G B - M A R K E R - O Under the old code that offset truncates to 1 GB, where the image is zeroes. The whole path is exercised: f->off, devsw, diskread, bread and ide.c's LBA48. Worth recording for next time: `make ramroot` stages the ESP itself, and running `make esp` afterwards rebuilds the kernel without RAMROOT and silently undoes it. The tell is a 855 KB kernel where a 25 MB one is expected. 110 [ok], no failures, on the default build.
This commit is contained in:
parent
f4107177ea
commit
ad2b321d56
9 changed files with 42 additions and 41 deletions
|
|
@ -31,14 +31,15 @@
|
|||
#include "file.h"
|
||||
#include "errno.h"
|
||||
|
||||
// A byte offset arrives as a 32-bit `uint` from the devsw signature, so a raw
|
||||
// device is addressable to 4 GB. Larger disks need the signature widened --
|
||||
// worth doing before anyone installs onto a real one, and called out here
|
||||
// rather than discovered as silent wraparound at the 4 GB mark.
|
||||
#define DISK_MAX_BYTES 0xFFFFFFFFULL
|
||||
// The byte offset is 64-bit, all the way from struct file's off through the
|
||||
// devsw signature to bread's block number. It used to narrow to a 32-bit uint
|
||||
// at the devsw boundary, which capped a raw device at 4 GB -- and the constant
|
||||
// that documented the cap was never referenced by anything, so the failure was
|
||||
// not a refusal at 4 GB but silent wraparound past it. Nothing here may narrow
|
||||
// it again: an NVMe namespace or any modern disk is far beyond that.
|
||||
|
||||
int
|
||||
diskread(struct inode *ip, uint off, char *dst, int n)
|
||||
diskread(struct inode *ip, uint64 off, char *dst, int n)
|
||||
{
|
||||
int dev = ip->minor;
|
||||
|
||||
|
|
@ -49,8 +50,8 @@ diskread(struct inode *ip, uint off, char *dst, int n)
|
|||
|
||||
int done = 0;
|
||||
while (done < n) {
|
||||
uint64 pos = (uint64)off + (uint64)done;
|
||||
uint blk = (uint)(pos / BSIZE);
|
||||
uint64 pos = off + (uint64)done;
|
||||
uint64 blk = pos / BSIZE;
|
||||
uint inblk = (uint)(pos % BSIZE);
|
||||
int want = BSIZE - (int)inblk;
|
||||
if (want > n - done)
|
||||
|
|
@ -66,7 +67,7 @@ diskread(struct inode *ip, uint off, char *dst, int n)
|
|||
}
|
||||
|
||||
int
|
||||
diskwrite(struct inode *ip, uint off, char *src, int n)
|
||||
diskwrite(struct inode *ip, uint64 off, char *src, int n)
|
||||
{
|
||||
int dev = ip->minor;
|
||||
|
||||
|
|
@ -77,8 +78,8 @@ diskwrite(struct inode *ip, uint off, char *src, int n)
|
|||
|
||||
int done = 0;
|
||||
while (done < n) {
|
||||
uint64 pos = (uint64)off + (uint64)done;
|
||||
uint blk = (uint)(pos / BSIZE);
|
||||
uint64 pos = off + (uint64)done;
|
||||
uint64 blk = pos / BSIZE;
|
||||
uint inblk = (uint)(pos % BSIZE);
|
||||
int want = BSIZE - (int)inblk;
|
||||
if (want > n - done)
|
||||
|
|
|
|||
Loading…
Reference in a new issue