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