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