blob: 1e0d21c81df963603c1d5907fa9306bbed3e8c49 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Brun378a4452021-01-26 13:47:41 +01002// SPDX-License-Identifier: Apache-2.0
Lorenz Brun378a4452021-01-26 13:47:41 +01003
4package erofs
5
6import (
7 "bytes"
8 "encoding/binary"
9 "errors"
10 "fmt"
11 "math"
12)
13
Serge Bazanski216fe7b2021-05-21 18:36:16 +020014// uncompressedInodeWriter exposes a io.Write-style interface for a single
15// uncompressed inode. It splits the Write-calls into blocks and writes both
16// the blocks and inode metadata. It is required to call Close() to ensure
17// everything is properly written down before writing another inode.
Lorenz Brun378a4452021-01-26 13:47:41 +010018type uncompressedInodeWriter struct {
19 buf bytes.Buffer
20 writer *Writer
21 inode inodeCompact
22 baseBlock uint32 // baseBlock == 0 implies this inode didn't allocate a block (yet).
23 writtenBytes int
24 legacyInodeNumber uint32
25 pathname string
26}
27
28func (i *uncompressedInodeWriter) allocateBlock() error {
29 bb, err := i.writer.allocateBlocks(1)
30 if err != nil {
31 return err
32 }
33 if i.baseBlock == 0 {
34 i.baseBlock = bb
35 }
36 return nil
37}
38
39func (i *uncompressedInodeWriter) flush(n int) error {
40 if err := i.allocateBlock(); err != nil {
41 return err
42 }
43 slice := i.buf.Next(n)
44 if _, err := i.writer.w.Write(slice); err != nil {
45 return err
46 }
47 // Always pad to BlockSize.
48 _, err := i.writer.w.Write(make([]byte, BlockSize-len(slice)))
49 return err
50}
51
52func (i *uncompressedInodeWriter) Write(b []byte) (int, error) {
53 i.writtenBytes += len(b)
54 if _, err := i.buf.Write(b); err != nil {
55 return 0, err
56 }
57 for i.buf.Len() >= BlockSize {
58 if err := i.flush(BlockSize); err != nil {
59 return 0, err
60 }
61 }
62 return len(b), nil
63}
64
65func (i *uncompressedInodeWriter) Close() error {
66 if i.buf.Len() > BlockSize {
67 panic("programming error")
68 }
69 inodeSize := binary.Size(i.inode)
70 if i.buf.Len()+inodeSize > BlockSize {
71 // Can't fit last part of data inline, write it in its own block.
72 if err := i.flush(i.buf.Len()); err != nil {
73 return err
74 }
75 }
76 if i.buf.Len() == 0 {
77 i.inode.Format = inodeFlatPlain << 1
78 } else {
79 // Colocate last part of data with inode.
80 i.inode.Format = inodeFlatInline << 1
81 }
82 if i.writtenBytes > math.MaxUint32 {
83 return errors.New("inodes bigger than 2^32 need the extended inode format which is unsupported by this library")
84 }
85 i.inode.Size = uint32(i.writtenBytes)
86 if i.baseBlock != 0 {
87 i.inode.Union = i.baseBlock
88 }
89 i.inode.HardlinkCount = 1
90 i.inode.InodeNumCompat = i.legacyInodeNumber
91 basePos, err := i.writer.allocateMetadata(inodeSize+i.buf.Len(), 32)
92 if err != nil {
93 return fmt.Errorf("failed to allocate metadata: %w", err)
94 }
95 i.writer.pathInodeMeta[i.pathname] = &uncompressedInodeMeta{
96 nid: uint64(basePos) / 32,
97 ftype: unixModeToFT(i.inode.Mode),
98 blockStart: int64(i.baseBlock),
99 blockLength: (int64(i.writtenBytes) / BlockSize) * BlockSize,
100 inlineStart: basePos + 32,
101 inlineLength: int64(i.buf.Len()),
102 writer: i.writer,
103 }
104 if err := binary.Write(i.writer.w, binary.LittleEndian, &i.inode); err != nil {
105 return err
106 }
107 if i.inode.Format&(inodeFlatInline<<1) != 0 {
108 // Data colocated in inode, if any.
109 _, err := i.writer.w.Write(i.buf.Bytes())
110 return err
111 }
112 return nil
113}