cpio API

cpio

package

API reference for the cpio package.

F
function

hex8To

Parameters

dst
[]byte
v
uint32

Returns

[]byte
core/cpio/writer.go:14-21
func hex8To(dst []byte, v uint32) []byte

{
	var out [8]byte
	for i := 7; i >= 0; i-- {
		out[i] = hexDigits[v&0xF]
		v >>= 4
	}
	return append(dst, out[:]...)
}
F
function

writeNewcHeader

Parameters

ino
uint32
mode
uint32
uid
uint32
gid
uint32
nlink
uint32
mtime
uint32
filesize
uint32
devmaj
uint32
devmin
uint32
rdevmaj
uint32
rdevmin
uint32
namesize
uint32

Returns

error
core/cpio/writer.go:23-41
func writeNewcHeader(w io.Writer, ino, mode, uid, gid, nlink, mtime, filesize, devmaj, devmin, rdevmaj, rdevmin, namesize uint32) error

{
	b := make([]byte, 0, 110)
	b = append(b, magicNewc...)
	b = hex8To(b, ino)
	b = hex8To(b, mode)
	b = hex8To(b, uid)
	b = hex8To(b, gid)
	b = hex8To(b, nlink)
	b = hex8To(b, mtime)
	b = hex8To(b, filesize)
	b = hex8To(b, devmaj)
	b = hex8To(b, devmin)
	b = hex8To(b, rdevmaj)
	b = hex8To(b, rdevmin)
	b = hex8To(b, namesize)
	b = hex8To(b, 0) // check
	_, err := w.Write(b)
	return err
}
F
function

writePad4

Parameters

n
uint32

Returns

error
core/cpio/writer.go:43-50
func writePad4(w io.Writer, n uint32) error

{
	p := pad4(n)
	if p == 0 {
		return nil
	}
	_, err := w.Write(make([]byte, p))
	return err
}
F
function

normalizeName

Parameters

name
string

Returns

string
error
core/cpio/writer.go:52-68
func normalizeName(name string) (string, error)

{
	name = filepath.ToSlash(name)
	name = strings.TrimPrefix(name, "./")
	name = strings.TrimPrefix(name, "/")
	name = strings.TrimSpace(name)
	if name == "" {
		return "", errors.New("cpio: empty name")
	}

	clean := filepath.Clean(name)
	clean = filepath.ToSlash(clean)
	clean = strings.TrimPrefix(clean, "./")
	if clean == "." || clean == "" || clean == ".." || strings.HasPrefix(clean, "../") || strings.Contains(clean, "/../") {
		return "", errors.New("cpio: invalid name")
	}
	return clean, nil
}
F
function

PackDir

PackDir packs an on-disk directory into a CPIO archive (deterministic order).

Parameters

root
string
opts
...WriterOption

Returns

error
core/cpio/writer.go:170-231
func PackDir(root string, w io.Writer, opts ...WriterOption) error

{
	wr := NewWriter(w, opts...)
	root = filepath.Clean(root)
	source, err := os.OpenRoot(root)
	if err != nil {
		return errors.Join(err, wr.Close())
	}
	defer source.Close()

	walkErr := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(root, path)
		if err != nil {
			return err
		}
		rel = filepath.ToSlash(rel)
		if rel == "." {
			return nil
		}

		if d.IsDir() {
			return wr.AddDir(rel, 0755)
		}
		if d.Type()&fs.ModeSymlink != 0 {
			return errors.New("cpio: symbolic links are not supported")
		}
		file, err := source.Open(filepath.FromSlash(rel))
		if err != nil {
			return err
		}
		info, statErr := file.Stat()
		if statErr != nil {
			file.Close()
			return statErr
		}
		if !info.Mode().IsRegular() {
			file.Close()
			return errors.New("cpio: special files are not supported")
		}
		if info.Size() < 0 || uint64(info.Size()) > defaultMaxFileSize {
			file.Close()
			return errors.New("cpio: file exceeds archive limit")
		}
		b, readErr := io.ReadAll(io.LimitReader(file, int64(defaultMaxFileSize)+1))
		closeErr := file.Close()
		if readErr != nil || closeErr != nil {
			return errors.Join(readErr, closeErr)
		}
		if uint64(len(b)) > defaultMaxFileSize {
			return errors.New("cpio: file exceeds archive limit")
		}

		mode := fs.FileMode(0644)
		if info.Mode()&0100 != 0 {
			mode = 0755
		}
		return wr.AddFile(rel, mode, b)
	})
	return errors.Join(walkErr, wr.Close())
}
S
struct

Entry

Entry represents a single CPIO newc entry.

The Data field is only set for regular files.

core/cpio/cpio.go:17-26
type Entry struct

Methods

IsTrailer
Method

IsTrailer reports whether the entry is the CPIO trailer.

Returns

bool
func (*Entry) IsTrailer() bool
{ return e != nil && e.Name == "TRAILER!!!" }

Fields

Name Type Description
Name string
Mode uint32
UID uint32
GID uint32
NLink uint32
MTime uint32
FileSize uint32
Data []byte
S
struct

Reader

Reader reads CPIO newc archives.

core/cpio/cpio.go:32-41
type Reader struct

Methods

Fields

Name Type Description
r io.Reader
done bool
maxNameSize uint64
maxFileSize uint64
maxTotalSize uint64
totalSize uint64
maxEntries uint64
entries uint64
F
function

WithReaderMaxEntries

WithReaderMaxEntries sets the maximum number of non-trailer entries.

Parameters

maxEntries
uint64

Returns

core/cpio/cpio.go:44-48
func WithReaderMaxEntries(maxEntries uint64) ReaderOption

{
	return func(reader *Reader) {
		reader.maxEntries = maxEntries
	}
}
T
type

ReaderOption

ReaderOption configures archive read limits.

core/cpio/cpio.go:51-51
type ReaderOption func(*Reader)
F
function

WithReaderLimits

WithReaderLimits sets maximum name, entry, and total data sizes.

Parameters

maxNameSize
uint64
maxFileSize
uint64
maxTotalSize
uint64

Returns

core/cpio/cpio.go:54-60
func WithReaderLimits(maxNameSize, maxFileSize, maxTotalSize uint64) ReaderOption

{
	return func(reader *Reader) {
		reader.maxNameSize = maxNameSize
		reader.maxFileSize = maxFileSize
		reader.maxTotalSize = maxTotalSize
	}
}
F
function

NewReader

NewReader creates a new CPIO reader from the given io.Reader.

Parameters

opts
...ReaderOption

Returns

core/cpio/cpio.go:63-75
func NewReader(r io.Reader, opts ...ReaderOption) *Reader

{
	reader := &Reader{
		r:            r,
		maxNameSize:  defaultMaxNameSize,
		maxFileSize:  defaultMaxFileSize,
		maxTotalSize: defaultMaxTotalSize,
		maxEntries:   defaultMaxEntries,
	}
	for _, option := range opts {
		option(reader)
	}
	return reader
}
S
struct

Writer

Writer writes CPIO newc archives.

core/cpio/cpio.go:78-90
type Writer struct

Methods

AddDir
Method

Parameters

name string

Returns

error
func (*Writer) AddDir(name string, mode fs.FileMode) error
{
	if wr == nil || wr.closed {
		return errors.New("cpio: writer closed")
	}
	name, err := normalizeName(name)
	if err != nil {
		return err
	}
	if wr.entries >= wr.maxEntries {
		return errors.New("cpio: archive entry count exceeds limit")
	}
	perm := uint32(mode.Perm())
	cmode := uint32(0040000) | perm
	namesize := uint32(len(name) + 1)
	if err := writeNewcHeader(wr.w, wr.ino, cmode, wr.uid, wr.gid, 2, wr.mtime, 0, 0, 0, 0, 0, namesize); err != nil {
		return err
	}
	wr.ino++
	if _, err := io.WriteString(wr.w, name); err != nil {
		return err
	}
	if _, err := wr.w.Write([]byte{0}); err != nil {
		return err
	}
	if err := writePad4(wr.w, 110+namesize); err != nil {
		return err
	}
	wr.entries++
	return nil
}
AddFile
Method

Parameters

name string
data []byte

Returns

error
func (*Writer) AddFile(name string, mode fs.FileMode, data []byte) error
{
	if wr == nil || wr.closed {
		return errors.New("cpio: writer closed")
	}
	name, err := normalizeName(name)
	if err != nil {
		return err
	}
	size := uint64(len(data))
	if wr.entries >= wr.maxEntries {
		return errors.New("cpio: archive entry count exceeds limit")
	}
	if size > wr.maxFileSize {
		return errors.New("cpio: file data exceeds archive limit")
	}
	if size > wr.maxTotalSize-wr.totalSize {
		return errors.New("cpio: archive data exceeds total limit")
	}
	perm := uint32(mode.Perm())
	cmode := uint32(0100000) | perm
	filesize := uint32(len(data))
	namesize := uint32(len(name) + 1)
	if err := writeNewcHeader(wr.w, wr.ino, cmode, wr.uid, wr.gid, 1, wr.mtime, filesize, 0, 0, 0, 0, namesize); err != nil {
		return err
	}
	wr.ino++
	if _, err := io.WriteString(wr.w, name); err != nil {
		return err
	}
	if _, err := wr.w.Write([]byte{0}); err != nil {
		return err
	}
	if err := writePad4(wr.w, 110+namesize); err != nil {
		return err
	}
	if filesize > 0 {
		if _, err := wr.w.Write(data); err != nil {
			return err
		}
	}
	if err := writePad4(wr.w, filesize); err != nil {
		return err
	}
	wr.entries++
	wr.totalSize += size
	return nil
}
Close
Method

Close writes the TRAILER!!! entry.

Returns

error
func (*Writer) Close() error
{
	if wr == nil || wr.closed {
		return nil
	}
	wr.closed = true
	trail := "TRAILER!!!"
	namesize := uint32(len(trail) + 1)
	if err := writeNewcHeader(wr.w, wr.ino, 0, 0, 0, 1, wr.mtime, 0, 0, 0, 0, 0, namesize); err != nil {
		return err
	}
	if _, err := io.WriteString(wr.w, trail); err != nil {
		return err
	}
	if _, err := wr.w.Write([]byte{0}); err != nil {
		return err
	}
	return writePad4(wr.w, 110+namesize)
}

Fields

Name Type Description
w io.Writer
ino uint32
uid uint32
gid uint32
mtime uint32
closed bool
maxFileSize uint64
maxTotalSize uint64
maxEntries uint64
totalSize uint64
entries uint64
T
type

WriterOption

WriterOption configures a Writer.

core/cpio/cpio.go:93-93
type WriterOption func(*Writer)
F
function

WithMTimeUnix

WithMTimeUnix sets the mtime for the Writer.

Parameters

mtime
uint32

Returns

core/cpio/cpio.go:96-96
func WithMTimeUnix(mtime uint32) WriterOption

{ return func(w *Writer) { w.mtime = mtime } }
F
function

WithUIDGID

WithUIDGID sets the UID and GID for the Writer.

Parameters

uid
uint32
gid
uint32

Returns

core/cpio/cpio.go:99-104
func WithUIDGID(uid, gid uint32) WriterOption

{
	return func(w *Writer) {
		w.uid = uid
		w.gid = gid
	}
}
F
function

WithWriterLimits

WithWriterLimits sets maximum entry, total data, and entry count limits.

Parameters

maxFileSize
uint64
maxTotalSize
uint64
maxEntries
uint64

Returns

core/cpio/cpio.go:107-113
func WithWriterLimits(maxFileSize, maxTotalSize, maxEntries uint64) WriterOption

{
	return func(w *Writer) {
		w.maxFileSize = maxFileSize
		w.maxTotalSize = maxTotalSize
		w.maxEntries = maxEntries
	}
}
F
function

NewWriter

NewWriter creates a new CPIO newc Writer writing to w.

Parameters

opts
...WriterOption

Returns

core/cpio/cpio.go:116-128
func NewWriter(w io.Writer, opts ...WriterOption) *Writer

{
	wr := &Writer{
		w:            w,
		ino:          1,
		maxFileSize:  defaultMaxFileSize,
		maxTotalSize: defaultMaxTotalSize,
		maxEntries:   defaultMaxEntries,
	}
	for _, opt := range opts {
		opt(wr)
	}
	return wr
}
F
function

TestPackAndReadRoundtrip

Parameters

core/cpio/cpio_test.go:12-42
func TestPackAndReadRoundtrip(t *testing.T)

{
	tmp := t.TempDir()
	if err := os.MkdirAll(filepath.Join(tmp, "bin"), 0755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(tmp, "bin", "hello.txt"), []byte("hello"), 0644); err != nil {
		t.Fatal(err)
	}

	var buf bytes.Buffer
	if err := PackDir(tmp, &buf, WithMTimeUnix(0)); err != nil {
		t.Fatal(err)
	}

	rd := NewReader(bytes.NewReader(buf.Bytes()))
	seen := map[string][]byte{}
	for {
		e, err := rd.Next()
		if err != nil {
			if err == io.EOF {
				break
			}
			t.Fatal(err)
		}
		seen[e.Name] = e.Data
	}

	if got := string(seen["bin/hello.txt"]); got != "hello" {
		t.Fatalf("got %q", got)
	}
}
F
function

TestUnpackToDir

Parameters

core/cpio/cpio_test.go:44-69
func TestUnpackToDir(t *testing.T)

{
	tmp := t.TempDir()
	if err := os.MkdirAll(filepath.Join(tmp, "etc"), 0755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(tmp, "etc", "conf"), []byte("x=y"), 0644); err != nil {
		t.Fatal(err)
	}

	var buf bytes.Buffer
	if err := PackDir(tmp, &buf, WithMTimeUnix(0)); err != nil {
		t.Fatal(err)
	}

	out := t.TempDir()
	if err := UnpackToDir(bytes.NewReader(buf.Bytes()), out); err != nil {
		t.Fatal(err)
	}
	b, err := os.ReadFile(filepath.Join(out, "etc", "conf"))
	if err != nil {
		t.Fatal(err)
	}
	if string(b) != "x=y" {
		t.Fatalf("got %q", string(b))
	}
}
F
function

TestReaderEnforcesConfiguredLimits

Parameters

core/cpio/cpio_test.go:71-85
func TestReaderEnforcesConfiguredLimits(t *testing.T)

{
	var archive bytes.Buffer
	writer := NewWriter(&archive)
	if err := writer.AddFile("large", 0644, []byte("0123456789")); err != nil {
		t.Fatal(err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}

	reader := NewReader(bytes.NewReader(archive.Bytes()), WithReaderLimits(32, 4, 4))
	if _, err := reader.Next(); err == nil {
		t.Fatal("Reader accepted an entry larger than the configured limit")
	}
}
F
function

TestReaderEnforcesTotalLimit

Parameters

core/cpio/cpio_test.go:87-107
func TestReaderEnforcesTotalLimit(t *testing.T)

{
	var archive bytes.Buffer
	writer := NewWriter(&archive)
	if err := writer.AddFile("one", 0644, []byte("1234")); err != nil {
		t.Fatal(err)
	}
	if err := writer.AddFile("two", 0644, []byte("5678")); err != nil {
		t.Fatal(err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}

	reader := NewReader(bytes.NewReader(archive.Bytes()), WithReaderLimits(32, 8, 6))
	if _, err := reader.Next(); err != nil {
		t.Fatal(err)
	}
	if _, err := reader.Next(); err == nil {
		t.Fatal("Reader accepted archive data beyond the total limit")
	}
}
F
function

TestReaderEnforcesEntryCountLimit

Parameters

core/cpio/cpio_test.go:109-129
func TestReaderEnforcesEntryCountLimit(t *testing.T)

{
	var archive bytes.Buffer
	writer := NewWriter(&archive)
	if err := writer.AddFile("one", 0644, nil); err != nil {
		t.Fatal(err)
	}
	if err := writer.AddFile("two", 0644, nil); err != nil {
		t.Fatal(err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}

	reader := NewReader(bytes.NewReader(archive.Bytes()), WithReaderMaxEntries(1))
	if _, err := reader.Next(); err != nil {
		t.Fatal(err)
	}
	if _, err := reader.Next(); err == nil {
		t.Fatal("Reader accepted too many empty entries")
	}
}
F
function

TestWriterEnforcesConfiguredLimits

Parameters

core/cpio/cpio_test.go:131-158
func TestWriterEnforcesConfiguredLimits(t *testing.T)

{
	t.Run("file size", func(t *testing.T) {
		writer := NewWriter(io.Discard, WithWriterLimits(2, 10, 10))
		if err := writer.AddFile("large", 0644, []byte("123")); err == nil {
			t.Fatal("Writer accepted a file beyond its entry limit")
		}
	})

	t.Run("total size", func(t *testing.T) {
		writer := NewWriter(io.Discard, WithWriterLimits(4, 5, 10))
		if err := writer.AddFile("one", 0644, []byte("123")); err != nil {
			t.Fatal(err)
		}
		if err := writer.AddFile("two", 0644, []byte("456")); err == nil {
			t.Fatal("Writer accepted data beyond its total limit")
		}
	})

	t.Run("entry count", func(t *testing.T) {
		writer := NewWriter(io.Discard, WithWriterLimits(4, 10, 1))
		if err := writer.AddDir("one", 0755); err != nil {
			t.Fatal(err)
		}
		if err := writer.AddFile("two", 0644, nil); err == nil {
			t.Fatal("Writer accepted too many entries")
		}
	})
}
F
function

TestUnpackRejectsSymlinkEscape

Parameters

core/cpio/cpio_test.go:160-181
func TestUnpackRejectsSymlinkEscape(t *testing.T)

{
	out := t.TempDir()
	outside := t.TempDir()
	if err := os.Symlink(outside, filepath.Join(out, "link")); err != nil {
		t.Fatal(err)
	}

	var archive bytes.Buffer
	writer := NewWriter(&archive)
	if err := writer.AddFile("link/escaped", 0644, []byte("owned")); err != nil {
		t.Fatal(err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	if err := UnpackToDir(bytes.NewReader(archive.Bytes()), out); err == nil {
		t.Fatal("UnpackToDir() followed a symlink outside the destination")
	}
	if _, err := os.Stat(filepath.Join(outside, "escaped")); !os.IsNotExist(err) {
		t.Fatalf("outside file exists: %v", err)
	}
}
S
struct

rejectingWriter

core/cpio/cpio_test.go:199-199
type rejectingWriter struct

Methods

Write
Method

Parameters

[]byte

Returns

int
error
func (rejectingWriter) Write([]byte) (int, error)
{
	return 0, errors.New("write failed")
}
F
function

TestPackDirReturnsTrailerWriteError

Parameters

core/cpio/cpio_test.go:205-209
func TestPackDirReturnsTrailerWriteError(t *testing.T)

{
	if err := PackDir(t.TempDir(), rejectingWriter{}); err == nil {
		t.Fatal("PackDir() ignored trailer write failure")
	}
}
F
function

TestReaderRejectsArchiveWithoutTrailer

Parameters

core/cpio/cpio_test.go:211-225
func TestReaderRejectsArchiveWithoutTrailer(t *testing.T)

{
	var archive bytes.Buffer
	writer := NewWriter(&archive)
	if err := writer.AddFile("file", 0644, []byte("data")); err != nil {
		t.Fatal(err)
	}

	reader := NewReader(bytes.NewReader(archive.Bytes()))
	if _, err := reader.Next(); err != nil {
		t.Fatal(err)
	}
	if _, err := reader.Next(); err == nil || errors.Is(err, io.EOF) {
		t.Fatalf("Next() error = %v, want missing trailer error", err)
	}
}
F
function

TestReaderRejectsCRCArchiveWithoutChecksumValidation

Parameters

core/cpio/cpio_test.go:227-242
func TestReaderRejectsCRCArchiveWithoutChecksumValidation(t *testing.T)

{
	var archive bytes.Buffer
	writer := NewWriter(&archive)
	if err := writer.AddFile("file", 0644, []byte("data")); err != nil {
		t.Fatal(err)
	}
	if err := writer.Close(); err != nil {
		t.Fatal(err)
	}
	data := archive.Bytes()
	copy(data[:6], "070702")

	if _, err := NewReader(bytes.NewReader(data)).Next(); err == nil {
		t.Fatal("Reader accepted an unsupported CRC archive")
	}
}
F
function

parseHex8

Parameters

b
[]byte

Returns

uint32
error
core/cpio/reader.go:9-27
func parseHex8(b []byte) (uint32, error)

{
	var v uint32
	for i := 0; i < 8 && i < len(b); i++ {
		c := b[i]
		var n uint32
		switch {
		case c >= '0' && c <= '9':
			n = uint32(c - '0')
		case c >= 'A' && c <= 'F':
			n = uint32(c-'A') + 10
		case c >= 'a' && c <= 'f':
			n = uint32(c-'a') + 10
		default:
			return 0, errors.New("cpio: invalid hex")
		}
		v = (v << 4) | n
	}
	return v, nil
}
F
function

pad4

Parameters

n
uint32

Returns

uint32
core/cpio/reader.go:29-35
func pad4(n uint32) uint32

{
	rem := n & 3
	if rem == 0 {
		return 0
	}
	return 4 - rem
}
F
function

discardN

Parameters

n
uint32

Returns

error
core/cpio/reader.go:37-54
func discardN(r io.Reader, n uint32) error

{
	if n == 0 {
		return nil
	}
	buf := make([]byte, 128)
	for n > 0 {
		chunk := uint32(len(buf))
		if chunk > n {
			chunk = n
		}
		_, err := io.ReadFull(r, buf[:chunk])
		if err != nil {
			return err
		}
		n -= chunk
	}
	return nil
}
F
function

safeJoin

Parameters

dst
string
name
string

Returns

string
error
core/cpio/unpack.go:15-26
func safeJoin(dst, name string) (string, error)

{
	name = filepath.ToSlash(name)
	name = strings.TrimPrefix(name, "/")
	clean := filepath.Clean(name)
	if clean == "." || clean == "" {
		return "", errors.New("cpio: invalid name")
	}
	if strings.HasPrefix(clean, "..") || strings.Contains(clean, "../") {
		return "", errors.New("cpio: path traversal")
	}
	return filepath.Join(dst, filepath.FromSlash(clean)), nil
}
F
function

permsFromMode

Parameters

mode
uint32

Returns

core/cpio/unpack.go:28-30
func permsFromMode(mode uint32) fs.FileMode

{
	return fs.FileMode(mode & 0777)
}
F
function

UnpackToDir

UnpackToDir unpacks a CPIO archive into dst.

Parameters

dst
string

Returns

error
core/cpio/unpack.go:33-97
func UnpackToDir(r io.Reader, dst string) error

{
	if err := os.MkdirAll(dst, 0755); err != nil {
		return err
	}
	root, err := os.OpenRoot(dst)
	if err != nil {
		return err
	}
	defer root.Close()
	if err := rejectDestinationSymlinks(dst); err != nil {
		return err
	}

	rd := NewReader(r)
	for {
		e, err := rd.Next()
		if err != nil {
			if errors.Is(err, io.EOF) {
				return nil
			}
			return err
		}

		outPath, err := safeJoin(dst, e.Name)
		if err != nil {
			return err
		}
		relative, err := filepath.Rel(dst, outPath)
		if err != nil {
			return err
		}

		isDir := (e.Mode & 0170000) == 0040000
		if isDir {
			if err := ensureDirectories(root, relative); err != nil {
				return err
			}
			continue
		}
		if (e.Mode & 0170000) != 0100000 {
			return errors.New("cpio: unsupported archive entry type")
		}

		if err := ensureDirectories(root, filepath.Dir(relative)); err != nil {
			return err
		}
		tempName, file, err := createTempFile(root, filepath.Dir(relative), permsFromMode(e.Mode))
		if err != nil {
			return err
		}
		if _, err := file.Write(e.Data); err != nil {
			file.Close()
			_ = root.Remove(tempName)
			return err
		}
		if err := file.Close(); err != nil {
			_ = root.Remove(tempName)
			return err
		}
		if err := root.Rename(tempName, relative); err != nil {
			_ = root.Remove(tempName)
			return err
		}
	}
}
F
function

ensureDirectories

Parameters

root
relative
string

Returns

error
core/cpio/unpack.go:111-138
func ensureDirectories(root *os.Root, relative string) error

{
	clean := filepath.Clean(relative)
	if clean == "." {
		return nil
	}
	current := ""
	for _, component := range strings.Split(clean, string(filepath.Separator)) {
		if component == "" || component == "." {
			continue
		}
		current = filepath.Join(current, component)
		info, err := root.Lstat(current)
		switch {
		case err == nil && info.Mode()&fs.ModeSymlink != 0:
			return fmt.Errorf("cpio: refusing symbolic link directory %q", current)
		case err == nil && !info.IsDir():
			return fmt.Errorf("cpio: path component %q is not a directory", current)
		case err == nil:
			continue
		case !errors.Is(err, fs.ErrNotExist):
			return err
		}
		if err := root.Mkdir(current, 0755); err != nil {
			return err
		}
	}
	return nil
}
F
function

createTempFile

Parameters

root
directory
string

Returns

string
error
core/cpio/unpack.go:140-156
func createTempFile(root *os.Root, directory string, mode fs.FileMode) (string, *os.File, error)

{
	for range 100 {
		var random [16]byte
		if _, err := rand.Read(random[:]); err != nil {
			return "", nil, err
		}
		name := filepath.Join(directory, ".foundation-"+hex.EncodeToString(random[:])+".tmp")
		file, err := root.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, mode)
		if err == nil {
			return name, file, nil
		}
		if !errors.Is(err, fs.ErrExist) {
			return "", nil, err
		}
	}
	return "", nil, errors.New("cpio: cannot allocate temporary extraction file")
}