mirror of
https://github.com/containerd/containerd.git
synced 2026-11-06 05:17:02 +00:00
Merge pull request #14499 from containerd/dependabot/go_modules/github.com/erofs/go-erofs-0.3.2
build(deps): bump github.com/erofs/go-erofs from 0.3.1 to 0.3.2
This commit is contained in:
13 files changed
+697
-236
No files matched your search
@@ -37,7 +37,7 @@ require (
|
||||
github.com/docker/go-events v0.1.0
|
||||
github.com/docker/go-metrics v0.1.0
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/erofs/go-erofs v0.3.1
|
||||
github.com/erofs/go-erofs v0.3.2
|
||||
github.com/fsnotify/fsnotify v1.10.1
|
||||
github.com/google/certtostore v1.0.7
|
||||
github.com/google/go-cmp v0.7.0
|
||||
|
||||
@@ -100,8 +100,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
|
||||
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/erofs/go-erofs v0.3.1 h1:Sux82Jq9yvyYhIoLgSHDp741p/+370HsOj9dAh1+VVs=
|
||||
github.com/erofs/go-erofs v0.3.1/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs=
|
||||
github.com/erofs/go-erofs v0.3.2 h1:GBWc9wyDOBVMLpA9OC0MkFfs4AbiEAAEhNH8SpQwD/4=
|
||||
github.com/erofs/go-erofs v0.3.2/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs=
|
||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
|
||||
+10
-2
@@ -66,6 +66,11 @@ var (
|
||||
// a directory.
|
||||
ErrIsDirectory = errors.New("is a directory")
|
||||
|
||||
// ErrDirNotEmpty is returned by Remove when the named path is a
|
||||
// non-empty directory. Mirrors the behavior of os.Remove (which returns
|
||||
// ENOTEMPTY).
|
||||
ErrDirNotEmpty = errors.New("directory not empty")
|
||||
|
||||
// ErrLoop is returned when too many symlinks are encountered during
|
||||
// path resolution.
|
||||
ErrLoop = fmt.Errorf("too many symlinks: %w", ErrInvalid)
|
||||
@@ -83,6 +88,9 @@ var (
|
||||
// InodeInfo: Ino() uint64, Nlink() uint64
|
||||
// DeviceInfo: Rdev() uint64
|
||||
// Xattrs: GetAllXattr() map[string]string, GetXattr(string) (string, bool)
|
||||
//
|
||||
// Mode has no accessor interface: use [fs.FileInfo.Mode], which returns the
|
||||
// same value, including the setuid, setgid and sticky bits.
|
||||
type Stat struct {
|
||||
Mode fs.FileMode
|
||||
Size int64
|
||||
@@ -1005,7 +1013,7 @@ func (b *file) readInfo() (ino *inode, err error) {
|
||||
inodeLayout: layout,
|
||||
inodeData: di.InodeData,
|
||||
size: int64(di.Size),
|
||||
mode: (fs.FileMode(di.Mode) & ^fs.ModeType) | b.ftype,
|
||||
mode: (disk.EroFSModeToGoFileMode(di.Mode) & ^fs.ModeType) | b.ftype,
|
||||
rawMode: di.Mode,
|
||||
uid: uint32(di.UID),
|
||||
gid: uint32(di.GID),
|
||||
@@ -1026,7 +1034,7 @@ func (b *file) readInfo() (ino *inode, err error) {
|
||||
inodeLayout: layout,
|
||||
inodeData: di.InodeData,
|
||||
size: int64(di.Size),
|
||||
mode: (fs.FileMode(di.Mode) & ^fs.ModeType) | b.ftype,
|
||||
mode: (disk.EroFSModeToGoFileMode(di.Mode) & ^fs.ModeType) | b.ftype,
|
||||
rawMode: di.Mode,
|
||||
uid: di.UID,
|
||||
gid: di.GID,
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ var xattrPrefixes = [...]struct {
|
||||
prefix string
|
||||
}{
|
||||
{1, "user."},
|
||||
{2, "system.posix_acl_access."},
|
||||
{3, "system.posix_acl_default."},
|
||||
{2, "system.posix_acl_access"},
|
||||
{3, "system.posix_acl_default"},
|
||||
{4, "trusted."},
|
||||
{5, "lustre."},
|
||||
{6, "security."},
|
||||
|
||||
+9
@@ -17,6 +17,15 @@ type Entry struct {
|
||||
Chunks []Chunk // physical block refs (metadata-only mode)
|
||||
Contiguous bool // data blocks are contiguous; flat-plain is sufficient
|
||||
MetadataOnly bool // chunk-based layout even without chunks
|
||||
|
||||
// Dev and Ino identify the source inode (e.g. syscall.Stat_t.Dev/Ino, or
|
||||
// the NID within a source EROFS image). When Nlink > 1, the writer uses
|
||||
// (Dev, Ino) to detect additional names that refer to the same source
|
||||
// file and share a single fsInode for them, mirroring an explicit Link()
|
||||
// call. Ino == 0 means "unknown"; such entries never participate in hard
|
||||
// link detection. Inode numbers are only unique per device, so Dev must
|
||||
// be included in the comparison.
|
||||
Dev, Ino uint64
|
||||
}
|
||||
|
||||
// NullPhysicalBlock is the sentinel value for Chunk.PhysicalBlock that marks
|
||||
|
||||
+59
-24
@@ -26,17 +26,27 @@ func (w *erofsWriter) planLayout(root *erofsEntry) {
|
||||
}
|
||||
walk(root)
|
||||
|
||||
w.totalInodes = uint64(len(w.entries))
|
||||
// Secondary hard-link entries share the primary's inode; count only primaries.
|
||||
var inodeCount uint64
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary == nil {
|
||||
inodeCount++
|
||||
}
|
||||
}
|
||||
w.totalInodes = inodeCount
|
||||
|
||||
// Block 0 holds: 1024-byte pad + 128-byte superblock + device slot(s) + padding
|
||||
// MetaBlkAddr is set later by write() depending on the on-disk layout.
|
||||
|
||||
// Assign NIDs sequentially.
|
||||
// NID = byte offset from metaStartPos / 32.
|
||||
// Each extended inode is 64 bytes = 2 NID slots.
|
||||
// Trailing data follows and is padded to 32-byte boundary.
|
||||
// Assign NIDs sequentially. NID = byte offset from metaStartPos / 32.
|
||||
// Secondary hard-link entries are skipped here; a second pass copies the
|
||||
// primary's finalized NID to them.
|
||||
currentOff := 0 // byte offset from metaStartPos
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue // skip; NID assigned in second pass below
|
||||
}
|
||||
|
||||
e.nid = uint64(currentOff / 32)
|
||||
e.xattrSize = calcXattrSize(e)
|
||||
|
||||
@@ -67,7 +77,7 @@ func (w *erofsWriter) planLayout(root *erofsEntry) {
|
||||
}
|
||||
default:
|
||||
// Full-image mode: decide inline vs plain
|
||||
if int(e.size) <= w.blockSize-headerSize {
|
||||
if !w.noInlineData && int(e.size) <= w.blockSize-headerSize {
|
||||
inBlockOff := (currentOff + headerSize) % w.blockSize
|
||||
if inBlockOff+int(e.size) <= w.blockSize {
|
||||
e.layout = disk.LayoutFlatInline
|
||||
@@ -100,8 +110,9 @@ func (w *erofsWriter) planLayout(root *erofsEntry) {
|
||||
|
||||
// Recalculate trailing size now that layout is decided
|
||||
e.trailingSize = w.calcTrailingSize(e)
|
||||
e.chunkPad = chunkIndexPad(e)
|
||||
|
||||
totalInodeSize := headerSize + e.trailingSize
|
||||
totalInodeSize := headerSize + e.chunkPad + e.trailingSize
|
||||
// Pad to 32-byte boundary
|
||||
if totalInodeSize%32 != 0 {
|
||||
totalInodeSize = (totalInodeSize + 31) & ^31
|
||||
@@ -122,7 +133,8 @@ func (w *erofsWriter) planLayout(root *erofsEntry) {
|
||||
// Fall back to flat-plain (data would cross block boundary)
|
||||
e.layout = disk.LayoutFlatPlain
|
||||
e.trailingSize = w.calcTrailingSize(e)
|
||||
totalInodeSize = headerSize + e.trailingSize
|
||||
e.chunkPad = chunkIndexPad(e)
|
||||
totalInodeSize = headerSize + e.chunkPad + e.trailingSize
|
||||
if totalInodeSize%32 != 0 {
|
||||
totalInodeSize = (totalInodeSize + 31) & ^31
|
||||
}
|
||||
@@ -132,9 +144,36 @@ func (w *erofsWriter) planLayout(root *erofsEntry) {
|
||||
currentOff += totalInodeSize
|
||||
}
|
||||
|
||||
// Second pass: copy the primary's finalized NID to secondary entries.
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
e.nid = e.hardLinkPrimary.nid
|
||||
}
|
||||
}
|
||||
|
||||
w.rootNid = root.nid
|
||||
}
|
||||
|
||||
// chunkIndexPad returns the padding inserted between an inode's xattr area and
|
||||
// its chunk-index map so the map begins aligned to the chunk-index unit, as the
|
||||
// kernel and reader both expect:
|
||||
//
|
||||
// pos = ALIGN(iloc + inode_isize + xattr_isize, unit) + unit*chunknr
|
||||
//
|
||||
// (see fs/erofs/data.c and loadBlock's LayoutChunkBased case). inodeCoreSize is
|
||||
// always a multiple of the unit, so only the xattr area can push the map off
|
||||
// alignment. Non-chunk layouts (inline data) are read without this alignment
|
||||
// and so must not be padded.
|
||||
func chunkIndexPad(e *erofsEntry) int {
|
||||
if e.layout != disk.LayoutChunkBased || e.trailingSize == 0 {
|
||||
return 0
|
||||
}
|
||||
if r := (inodeCoreSize(e) + e.xattrSize) % disk.SizeChunkIndex; r != 0 {
|
||||
return disk.SizeChunkIndex - r
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// calcTrailingSize returns the number of bytes following the 64-byte inode.
|
||||
func (w *erofsWriter) calcTrailingSize(e *erofsEntry) int {
|
||||
switch e.mode & disk.StatTypeMask {
|
||||
@@ -166,29 +205,25 @@ func (w *erofsWriter) calcTrailingSize(e *erofsEntry) int {
|
||||
}
|
||||
}
|
||||
|
||||
// direntNames returns the sorted list of dirent names for a directory,
|
||||
// including "." and "..". EROFS requires dirents within each block to
|
||||
// be sorted alphabetically.
|
||||
func direntNames(e *erofsEntry) []string {
|
||||
// direntDataSize calculates the serialized EROFS dirent data size for a directory.
|
||||
// For multi-block directories, this includes inter-block padding.
|
||||
func (w *erofsWriter) direntDataSize(e *erofsEntry) int {
|
||||
if len(e.children) == 0 {
|
||||
// Empty dir still needs "." and ".." entries.
|
||||
return 2*disk.SizeDirent + 1 + 2
|
||||
}
|
||||
|
||||
// Build sorted name lengths matching the order writeDirents produces.
|
||||
// "." and ".." are not guaranteed to sort before all real names (any
|
||||
// name with an ASCII value below '.' such as '-' sorts before them).
|
||||
names := make([]string, 0, len(e.children)+2)
|
||||
names = append(names, ".", "..")
|
||||
for _, c := range e.children {
|
||||
names = append(names, c.name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// direntDataSize calculates the serialized EROFS dirent data size for a directory.
|
||||
// For multi-block directories, this includes inter-block padding.
|
||||
func (w *erofsWriter) direntDataSize(e *erofsEntry) int {
|
||||
names := direntNames(e)
|
||||
nEntries := len(names)
|
||||
if len(e.children) == 0 {
|
||||
// Empty dir still needs "." and ".." entries
|
||||
return 2*disk.SizeDirent + 1 + 2
|
||||
}
|
||||
|
||||
totalSize := 0
|
||||
i := 0
|
||||
for i < nEntries {
|
||||
@@ -209,7 +244,7 @@ func (w *erofsWriter) direntDataSize(e *erofsEntry) int {
|
||||
blockUsed = disk.SizeDirent + len(names[i])
|
||||
i++
|
||||
}
|
||||
// Pad non-final blocks to block boundary
|
||||
// Pad non-final blocks to block boundary.
|
||||
if i < nEntries && blockUsed%w.blockSize != 0 {
|
||||
blockUsed = (blockUsed + w.blockSize - 1) & ^(w.blockSize - 1)
|
||||
}
|
||||
|
||||
+507
-163
File diff suppressed because it is too large.
Load diff
+2
@@ -19,6 +19,8 @@ func entryFromSys(info fs.FileInfo) *builder.Entry {
|
||||
MtimeNs: uint32(sys.Mtimespec.Nsec),
|
||||
Nlink: uint32(sys.Nlink),
|
||||
Rdev: uint32(sys.Rdev),
|
||||
Dev: uint64(sys.Dev),
|
||||
Ino: sys.Ino,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
|
||||
+47
-29
@@ -66,7 +66,9 @@ type imgQEntry struct {
|
||||
// syscalls), it reads the entire metadata area into memory and parses
|
||||
// inodes, directory entries, xattrs, and chunk indexes directly from the
|
||||
// buffer. This reduces thousands of syscalls to a single ReadAt.
|
||||
func (fsys *Writer) copyFromImage(img *image) error {
|
||||
// hardlinks is the current CopyFrom call's (Dev, Ino) → fsInode map, used to
|
||||
// detect additional names that refer to a source inode already seen.
|
||||
func (fsys *Writer) copyFromImage(img *image, hardlinks map[hardlinkKey]*fsInode) error {
|
||||
metaStart := img.metaStartPos()
|
||||
totalBytes := int64(img.sb.Blocks) << img.sb.BlkSizeBits
|
||||
if totalBytes <= 0 {
|
||||
@@ -198,9 +200,8 @@ func (fsys *Writer) copyFromImage(img *image) error {
|
||||
trailingAddr := inodeAddr + int64(icSize) + int64(xattrSize)
|
||||
typ := mode & disk.StatTypeMask
|
||||
|
||||
// Build fsEntry directly, bypassing builder.Entry + add() overhead.
|
||||
fe := &fsEntry{
|
||||
path: cur.path,
|
||||
// Build fsInode directly, bypassing builder.Entry + add() overhead.
|
||||
ino := &fsInode{
|
||||
mode: mode,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
@@ -208,16 +209,38 @@ func (fsys *Writer) copyFromImage(img *image) error {
|
||||
mtimeNs: mtimeNs,
|
||||
size: size,
|
||||
xattrs: xattrs,
|
||||
fileClosed: true,
|
||||
}
|
||||
if nlink > 0 {
|
||||
fe.nlink = nlink
|
||||
fe.nlinkSet = true
|
||||
ino.nlink = nlink
|
||||
ino.nlinkSet = true
|
||||
}
|
||||
fe.fileClosed = true
|
||||
if fsys.copyMetadataOnly {
|
||||
fe.metadataOnly = true
|
||||
ino.metadataOnly = true
|
||||
}
|
||||
|
||||
// Detect hard links: if this nid was already registered under an
|
||||
// earlier directory entry in this CopyFrom call, share that fsInode
|
||||
// instead of duplicating its data. Directories are excluded — a
|
||||
// directory's nlink reflects its child-directory count, not a real
|
||||
// hard link, and BFS only ever visits a given directory nid once.
|
||||
// nid is only unique within this source image, but hardlinks is
|
||||
// created fresh for each CopyFrom call, so keying on nid alone
|
||||
// (dev 0) is safe here.
|
||||
secondaryHardlink := false
|
||||
if nlink > 1 && typ != disk.StatTypeDir {
|
||||
key := hardlinkKey{0, cur.nid}
|
||||
if prior, ok := hardlinks[key]; ok {
|
||||
ino = prior
|
||||
secondaryHardlink = true
|
||||
} else {
|
||||
hardlinks[key] = ino
|
||||
}
|
||||
}
|
||||
|
||||
fe := &fsEntry{path: cur.path, ino: ino}
|
||||
|
||||
if !secondaryHardlink {
|
||||
switch typ {
|
||||
case disk.StatTypeDir:
|
||||
dirSize := int(size)
|
||||
@@ -258,7 +281,7 @@ func (fsys *Writer) copyFromImage(img *image) error {
|
||||
linkData = at(trailingAddr)
|
||||
}
|
||||
if linkData != nil && int(size) <= len(linkData) {
|
||||
fe.linkTarget = string(linkData[:size])
|
||||
ino.linkTarget = string(linkData[:size])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,41 +293,36 @@ func (fsys *Writer) copyFromImage(img *image) error {
|
||||
if chunkAddr%8 != 0 {
|
||||
chunkAddr = (chunkAddr + 7) & ^int64(7)
|
||||
}
|
||||
fe.chunks = fsys.parseChunks(at(chunkAddr), chunkFmt, size, blkBits, img.deviceIDMask)
|
||||
fe.contiguous = true
|
||||
ino.chunks = fsys.parseChunks(at(chunkAddr), chunkFmt, size, blkBits, img.deviceIDMask)
|
||||
ino.contiguous = true
|
||||
}
|
||||
}
|
||||
|
||||
case disk.StatTypeChrdev, disk.StatTypeBlkdev:
|
||||
fe.rdev = disk.RdevFromMode(mode, idata)
|
||||
ino.rdev = disk.RdevFromMode(mode, idata)
|
||||
}
|
||||
|
||||
// Remap chunk DeviceIDs for metadata-only sources.
|
||||
if fsys.copyMetadataOnly && fsys.copyDeviceID > 0 {
|
||||
offset := fsys.copyDeviceID - 1
|
||||
for i := range fe.chunks {
|
||||
fe.chunks[i].DeviceID += offset
|
||||
for i := range ino.chunks {
|
||||
ino.chunks[i].DeviceID += offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Register in the tree.
|
||||
if cur.path == "/" {
|
||||
// Update root metadata.
|
||||
fsys.root.mode = fe.mode
|
||||
fsys.root.uid = fe.uid
|
||||
fsys.root.gid = fe.gid
|
||||
fsys.root.mtime = fe.mtime
|
||||
fsys.root.mtimeNs = fe.mtimeNs
|
||||
fsys.root.nlink = fe.nlink
|
||||
fsys.root.nlinkSet = fe.nlinkSet
|
||||
fsys.root.xattrs = fe.xattrs
|
||||
// Update root fsInode.
|
||||
fsys.root.ino = ino
|
||||
} else if existing, ok := fsys.byPath[cur.path]; ok {
|
||||
// Merge overwrites: preserve tree linkage.
|
||||
savedParent := existing.parent
|
||||
savedChildren := existing.children
|
||||
*existing = *fe
|
||||
existing.parent = savedParent
|
||||
existing.children = savedChildren
|
||||
// Merge overwrites: replace fsInode but preserve tree linkage.
|
||||
// Detach the old fsInode so any remaining hard-linked names get a
|
||||
// correct nlink.
|
||||
if existing.ino != ino {
|
||||
unlinkInode(existing.ino)
|
||||
}
|
||||
existing.ino = ino
|
||||
} else {
|
||||
fsys.addChild(fe)
|
||||
}
|
||||
|
||||
+2
@@ -23,6 +23,8 @@ func entryFromSys(info fs.FileInfo) *builder.Entry {
|
||||
MtimeNs: uint32(sys.Mtim.Nsec),
|
||||
Nlink: uint32(sys.Nlink),
|
||||
Rdev: uint32(sys.Rdev),
|
||||
Dev: sys.Dev,
|
||||
Ino: sys.Ino,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
|
||||
+53
-10
@@ -34,6 +34,7 @@ type erofsWriter struct {
|
||||
devices []uint64 // per-device block counts (one slot per entry)
|
||||
blockSize int
|
||||
chunkBits uint8 // log2(chunkSize / blockSize); chunkSize = blockSize << chunkBits
|
||||
noInlineData bool // disable tail-packing for regular files
|
||||
copyBuf []byte // reusable buffer for io.CopyBuffer
|
||||
zeroBuf []byte // blockSize-length zero buffer for padding
|
||||
inodeBuf [disk.SizeInodeExtended]byte // scratch buffer for writeInode
|
||||
@@ -116,11 +117,14 @@ func (w *erofsWriter) writeSeekable(out io.WriteSeeker) error {
|
||||
func (w *erofsWriter) newMetaBuffer() *bytes.Buffer {
|
||||
totalMetaBytes := 0
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue // secondary entries share the primary's on-disk inode
|
||||
}
|
||||
isz := disk.SizeInodeExtended
|
||||
if e.compact {
|
||||
isz = disk.SizeInodeCompact
|
||||
}
|
||||
sz := isz + e.xattrSize + e.trailingSize
|
||||
sz := isz + e.xattrSize + e.chunkPad + e.trailingSize
|
||||
if sz%32 != 0 {
|
||||
sz = (sz + 31) & ^31
|
||||
}
|
||||
@@ -141,8 +145,11 @@ func (w *erofsWriter) assignDataBlocks() {
|
||||
// Metadata-first: data blocks come after metadata.
|
||||
totalMetaBytes := 0
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue
|
||||
}
|
||||
expectedOff := int(e.nid) * 32
|
||||
sz := inodeCoreSize(e) + e.xattrSize + e.trailingSize
|
||||
sz := inodeCoreSize(e) + e.xattrSize + e.chunkPad + e.trailingSize
|
||||
if sz%32 != 0 {
|
||||
sz = (sz + 31) & ^31
|
||||
}
|
||||
@@ -154,6 +161,9 @@ func (w *erofsWriter) assignDataBlocks() {
|
||||
metaBlocks := (totalMetaBytes + w.blockSize - 1) / w.blockSize
|
||||
addr := uint32(w.sbAreaBlocks() + metaBlocks)
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue
|
||||
}
|
||||
if ds := w.flatPlainDataSize(e); ds > 0 {
|
||||
e.dataBlkAddr = addr
|
||||
addr += uint32((ds + w.blockSize - 1) / w.blockSize)
|
||||
@@ -163,6 +173,9 @@ func (w *erofsWriter) assignDataBlocks() {
|
||||
// Data-first: data starts after superblock area.
|
||||
addr := uint32(w.sbAreaBlocks())
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue // secondary entries share the primary's data blocks
|
||||
}
|
||||
if ds := w.flatPlainDataSize(e); ds > 0 {
|
||||
e.dataBlkAddr = addr
|
||||
addr += uint32((ds + w.blockSize - 1) / w.blockSize)
|
||||
@@ -194,11 +207,14 @@ func (w *erofsWriter) sbAreaBlocks() int {
|
||||
func (w *erofsWriter) metadataBytes() int {
|
||||
curOff := 0
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue // secondary entries have no on-disk inode
|
||||
}
|
||||
expectedOff := int(e.nid) * 32
|
||||
if curOff < expectedOff {
|
||||
curOff = expectedOff
|
||||
}
|
||||
sz := inodeCoreSize(e) + e.xattrSize + e.trailingSize
|
||||
sz := inodeCoreSize(e) + e.xattrSize + e.chunkPad + e.trailingSize
|
||||
if rem := sz % 32; rem != 0 {
|
||||
sz += 32 - rem
|
||||
}
|
||||
@@ -213,9 +229,12 @@ func (w *erofsWriter) writeBlock0(buf io.Writer) error {
|
||||
totalMetaBytes := w.metadataBytes()
|
||||
metaBlocks := (totalMetaBytes + w.blockSize - 1) / w.blockSize
|
||||
|
||||
// Count data blocks.
|
||||
// Count data blocks (skip secondary hard-link entries).
|
||||
dataBlocks := 0
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue
|
||||
}
|
||||
if ds := w.flatPlainDataSize(e); ds > 0 {
|
||||
dataBlocks += (ds + w.blockSize - 1) / w.blockSize
|
||||
}
|
||||
@@ -283,6 +302,11 @@ func (w *erofsWriter) writeBlock0(buf io.Writer) error {
|
||||
func (w *erofsWriter) writeMetadataInodes(buf io.Writer) error {
|
||||
metaStart := 0
|
||||
for _, e := range w.entries {
|
||||
// Secondary hard-link entries share the primary's on-disk inode.
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
expectedOff := int(e.nid) * 32
|
||||
if expectedOff > metaStart {
|
||||
if _, err := buf.Write(w.zeroBuf[:expectedOff-metaStart]); err != nil {
|
||||
@@ -312,18 +336,31 @@ func (w *erofsWriter) writeMetadataInodes(buf io.Writer) error {
|
||||
switch e.mode & disk.StatTypeMask {
|
||||
case disk.StatTypeReg:
|
||||
if e.layout == disk.LayoutChunkBased && (e.size > 0 || len(e.chunks) > 0) {
|
||||
// Align the chunk-index map to the chunk-index unit.
|
||||
if e.chunkPad > 0 {
|
||||
if _, err := buf.Write(w.zeroBuf[:e.chunkPad]); err != nil {
|
||||
return err
|
||||
}
|
||||
metaStart += e.chunkPad
|
||||
}
|
||||
if err := w.writeChunkIndexes(buf, e); err != nil {
|
||||
return fmt.Errorf("write chunks for %s: %w", e.path, err)
|
||||
}
|
||||
metaStart += e.trailingSize
|
||||
} else if e.layout == disk.LayoutFlatInline && e.size > 0 && e.data != nil {
|
||||
n, err := io.CopyBuffer(onlyWriter{buf}, io.LimitReader(e.data, int64(e.size)), w.copyBuf)
|
||||
// e.data may be an unbounded reader (e.g. directData from CopyFrom);
|
||||
// limit to e.size bytes to prevent overwriting subsequent metadata.
|
||||
expected := int64(e.size)
|
||||
n, err := io.CopyBuffer(onlyWriter{buf}, io.LimitReader(e.data, expected), w.copyBuf)
|
||||
if c, ok := e.data.(io.Closer); ok {
|
||||
_ = c.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("write inline data for %s: %w", e.path, err)
|
||||
}
|
||||
if n != expected {
|
||||
return fmt.Errorf("write inline data for %s: short read: got %d bytes, expected %d", e.path, n, expected)
|
||||
}
|
||||
metaStart += int(n)
|
||||
}
|
||||
case disk.StatTypeDir:
|
||||
@@ -348,7 +385,7 @@ func (w *erofsWriter) writeMetadataInodes(buf io.Writer) error {
|
||||
if e.compact {
|
||||
inodeSize = disk.SizeInodeCompact
|
||||
}
|
||||
totalWritten := inodeSize + e.xattrSize + e.trailingSize
|
||||
totalWritten := inodeSize + e.xattrSize + e.chunkPad + e.trailingSize
|
||||
if totalWritten%32 != 0 {
|
||||
padSize := 32 - (totalWritten % 32)
|
||||
if _, err := buf.Write(w.zeroBuf[:padSize]); err != nil {
|
||||
@@ -530,11 +567,14 @@ func (w *erofsWriter) writeDirents(buf io.Writer, e *erofsEntry) (int, error) {
|
||||
}
|
||||
|
||||
// Build the full entry list including "." and ".." then sort
|
||||
// alphabetically. EROFS requires dirents to be sorted within
|
||||
// each block; "." and ".." are not guaranteed to be first.
|
||||
// alphabetically. EROFS requires dirents to be sorted within each block;
|
||||
// "." and ".." are not guaranteed to sort first (filenames with ASCII
|
||||
// values below '.' such as '-' or ',' sort before them).
|
||||
allEnts := make([]direntInfo, 0, len(e.children)+2)
|
||||
allEnts = append(allEnts, direntInfo{".", e.nid, disk.FileTypeDir})
|
||||
allEnts = append(allEnts, direntInfo{"..", e.parentNid, disk.FileTypeDir})
|
||||
allEnts = append(allEnts,
|
||||
direntInfo{".", e.nid, disk.FileTypeDir},
|
||||
direntInfo{"..", e.parentNid, disk.FileTypeDir},
|
||||
)
|
||||
for _, c := range e.children {
|
||||
allEnts = append(allEnts, direntInfo{
|
||||
name: c.name,
|
||||
@@ -614,6 +654,9 @@ func (w *erofsWriter) writeDirents(buf io.Writer, e *erofsEntry) (int, error) {
|
||||
// writeDataBlocks writes data blocks for flat-plain entries directly to out.
|
||||
func (w *erofsWriter) writeDataBlocks(out io.Writer) error {
|
||||
for _, e := range w.entries {
|
||||
if e.hardLinkPrimary != nil {
|
||||
continue // secondary hard-link entries share the primary's data blocks
|
||||
}
|
||||
ds := w.flatPlainDataSize(e)
|
||||
if ds == 0 {
|
||||
continue
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ func (idx xattrIndex) String() string {
|
||||
case 1:
|
||||
return "user."
|
||||
case 2:
|
||||
return "system.posix_acl_access."
|
||||
return "system.posix_acl_access"
|
||||
case 3:
|
||||
return "system.posix_acl_default."
|
||||
return "system.posix_acl_default"
|
||||
case 4:
|
||||
return "trusted."
|
||||
case 5:
|
||||
|
||||
Vendored
+1
-1
@@ -345,7 +345,7 @@ github.com/docker/go-units
|
||||
## explicit; go 1.13
|
||||
github.com/emicklei/go-restful/v3
|
||||
github.com/emicklei/go-restful/v3/log
|
||||
# github.com/erofs/go-erofs v0.3.1
|
||||
# github.com/erofs/go-erofs v0.3.2
|
||||
## explicit; go 1.23
|
||||
github.com/erofs/go-erofs
|
||||
github.com/erofs/go-erofs/internal/builder
|
||||
|
||||
Reference in new issue
Block a user