Closed max-gotlib closed 4 months ago
This is one impressive issue. Screen caps and detailed hex-level description. Thank you for doing the detailed work.
If I understood the issue correctly, grub is erroring when reading the special directory entries "."
and ".."
, the first ones in any directory created. The lines you referenced here are, indeed, where those are created (and the only such place in fat32).
The directory entry is turned into bytes to be written to the filesystem here, with the important parts being:
createDate, createTime := timeToDateTime(de.createTime)
modifyDate, modifyTime := timeToDateTime(de.modifyTime)
accessDate, _ := timeToDateTime(de.accessTime)
binary.LittleEndian.PutUint16(dosBytes[14:16], createTime)
binary.LittleEndian.PutUint16(dosBytes[16:18], createDate)
binary.LittleEndian.PutUint16(dosBytes[18:20], accessDate)
binary.LittleEndian.PutUint16(dosBytes[22:24], modifyTime)
``
So the key is `timeToDateTime()`:
```go
func timeToDateTime(t time.Time) (datePart, timePart uint16) {
year := t.Year()
month := int(t.Month())
day := t.Day()
second := t.Second()
minute := t.Minute()
hour := t.Hour()
retDate := (year-1980)<<9 + (month << 5) + day
retTime := hour<<11 + minute<<5 + (second / 2)
return uint16(retDate), uint16(retTime)
}
I ran a quick test of timeToDateTime(t)
where t
is the default time.Time
(i.e. the case for the new entries, and we get time: 0001-01-01 00:00:00 +0000 UTC, outDate 35361, outTime 0
, outDate is 35361, or 0x8A21
, exactly what you found, and hence the source of the problem. The relevant logic for date is:
year := t.Year()
month := int(t.Month())
day := t.Day()
retDate := (year-1980)<<9 + (month << 5) + day
In the 0 case, that becomes year, month, date = 1, 1, 1
, or 1 January 1
. That then turns into -1013215
by the last line, and, when converted to uint16, becomes 35361. Clearly, the FAT32 logic was not built to handle dates of 1970 years before the epoch!
I see a few possible fixes:
.
would be the date we created the subdirectory (which effectively is now); the entry for ..
would be the date from the parent.timeToDateTime()
to handle zero-date properly, perhaps set to the epoch or the earliest possible acceptable FAT32 date.I think both of these are correct, but for the immediate issue, your approach is the correct one.
Do you want to open a Pull Request with it?
Do you want to open a Pull Request with it?
Yep. Onto it.
Symptoms:
go-diskfs
having a FAT-32 GPT partitionThis problem prevents some scripting working in the grub config files.
The grub loader has strict rules for the timestamps (
<grub-2.12>/include/grub/datetime.h:54
):What's generated with
go-diskfs
:Apparently, the
0x8a21
value corresponds to2049/01/01
date (according to the FAT rules) which is far in the future related to the image file generation date (2024 May 5
).Possible fix (verified, makes grub happy):