blob: 309ecf6525d9444b3cdeaf7d74a44f33d63a5695 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +00004package fat32
5
6import (
7 "fmt"
8 "io"
9 "time"
10)
11
12// Wraps a writer and provides support for writing padding up to a specified
13// alignment.
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +000014type blockWriter struct {
15 w io.Writer
16 n int64
17}
18
19func newBlockWriter(w io.Writer) *blockWriter {
20 return &blockWriter{w: w}
21}
22
23func (b *blockWriter) Write(p []byte) (n int, err error) {
24 n, err = b.w.Write(p)
25 b.n += int64(n)
26 return
27}
28
Jan Schäre0db72c2025-06-18 18:14:07 +000029func (b *blockWriter) ReadFrom(r io.Reader) (n int64, err error) {
30 // io.Copy forwards to b.w.ReadFrom if available.
31 n, err = io.Copy(b.w, r)
32 b.n += n
33 return
34}
35
Lorenz Brunbd2ce6d2022-07-22 00:00:13 +000036func (b *blockWriter) FinishBlock(alignment int64, mustZero bool) (err error) {
37 requiredBytes := (alignment - (b.n % alignment)) % alignment
38 if requiredBytes == 0 {
39 return nil
40 }
41 // Do not actually write out zeroes if not necessary
42 if s, ok := b.w.(io.Seeker); ok && !mustZero {
43 if _, err := s.Seek(requiredBytes-1, io.SeekCurrent); err != nil {
44 return fmt.Errorf("failed to seek to create hole for empty block: %w", err)
45 }
46 if _, err := b.w.Write([]byte{0x00}); err != nil {
47 return fmt.Errorf("failed to write last byte to create hole: %w", err)
48 }
49 b.n += requiredBytes
50 return
51 }
52 emptyBuf := make([]byte, 1*1024*1024)
53 for requiredBytes > 0 {
54 curBlockBytes := requiredBytes
55 if curBlockBytes > int64(len(emptyBuf)) {
56 curBlockBytes = int64(len(emptyBuf))
57 }
58 _, err = b.Write(emptyBuf[:curBlockBytes])
59 if err != nil {
60 return
61 }
62 requiredBytes -= curBlockBytes
63 }
64 return
65}
66
67// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
68// The resolution is 2s with fTime and 10ms if fTenMils is also used.
69// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
70func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16, fTenMils uint8) {
71 t = t.In(time.UTC)
72 if t.Year() < 1980 {
73 t = time.Date(1980, 1, 1, 0, 0, 0, 0, time.UTC)
74 }
75 if t.Year() > 2107 {
76 t = time.Date(2107, 12, 31, 23, 59, 59, 0, time.UTC)
77 }
78 fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
79 fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
80 fTenMils = uint8(t.Nanosecond()/1e7 + (t.Second()%2)*100)
81 return
82}