cpio
API
cpio
packageAPI reference for the cpio
package.
F
function
parseHex8
Parameters
b
[]byte
Returns
uint32
error
pkg/cpio/reader.go:8-26
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
pkg/cpio/reader.go:28-34
func pad4(n uint32) uint32
{
rem := n & 3
if rem == 0 {
return 0
}
return 4 - rem
}
F
function
discardN
Parameters
n
uint32
Returns
error
pkg/cpio/reader.go:36-53
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
pkg/cpio/unpack.go:12-23
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
pkg/cpio/unpack.go:25-27
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
pkg/cpio/unpack.go:30-61
func UnpackToDir(r io.Reader, dst string) error
{
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
}
isDir := (e.Mode & 0170000) == 0040000
if isDir {
if err := os.MkdirAll(outPath, 0755); err != nil {
return err
}
continue
}
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
return err
}
if err := os.WriteFile(outPath, e.Data, permsFromMode(e.Mode)); err != nil {
return err
}
}
}
F
function
hex8To
Parameters
dst
[]byte
v
uint32
Returns
[]byte
pkg/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
pkg/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
pkg/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
pkg/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
Returns
error
pkg/cpio/writer.go:142-180
func PackDir(root string, w io.Writer, opts ...WriterOption) error
{
wr := NewWriter(w, opts...)
defer wr.Close()
root = filepath.Clean(root)
return 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
}
info, err := d.Info()
if err != nil {
return err
}
if d.IsDir() {
return wr.AddDir(rel, 0755)
}
mode := fs.FileMode(0644)
if info.Mode()&0100 != 0 {
mode = 0755
}
b, err := os.ReadFile(path)
if err != nil {
return err
}
return wr.AddFile(rel, mode, b)
})
}
S
struct
Entry
Entry represents a single CPIO newc entry.
The Data field is only set for regular files.
pkg/cpio/cpio.go:13-22
type Entry struct
Methods
IsTrailer
Method
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/crc archives.
pkg/cpio/cpio.go:27-30
type Reader struct
Methods
Next
Method
Next returns the next entry. When the archive ends, it returns io.EOF.
Returns
error
func (*Reader) Next() (*Entry, error)
{
if rd == nil || rd.done {
return nil, io.EOF
}
var hdr [110]byte
n, err := io.ReadFull(rd.r, hdr[:])
if err != nil {
// If we didn't read anything, treat as clean EOF.
if errors.Is(err, io.EOF) && n == 0 {
rd.done = true
return nil, io.EOF
}
return nil, err
}
magic := string(hdr[0:6])
if magic != magicNewc && magic != magicCrc {
return nil, errors.New("cpio: invalid magic")
}
mode, err := parseHex8(hdr[14:22])
if err != nil {
return nil, err
}
uid, err := parseHex8(hdr[22:30])
if err != nil {
return nil, err
}
gid, err := parseHex8(hdr[30:38])
if err != nil {
return nil, err
}
nlink, err := parseHex8(hdr[38:46])
if err != nil {
return nil, err
}
mtime, err := parseHex8(hdr[46:54])
if err != nil {
return nil, err
}
filesize, err := parseHex8(hdr[54:62])
if err != nil {
return nil, err
}
namesize, err := parseHex8(hdr[94:102])
if err != nil {
return nil, err
}
if namesize == 0 {
return nil, errors.New("cpio: invalid namesize")
}
nameb := make([]byte, namesize)
if _, err := io.ReadFull(rd.r, nameb); err != nil {
return nil, err
}
name := ""
for i := 0; i < len(nameb); i++ {
if nameb[i] == 0 {
break
}
name += string([]byte{nameb[i]})
}
// Align after header + name.
if err := discardN(rd.r, pad4(110+namesize)); err != nil {
return nil, err
}
if name == "TRAILER!!!" {
rd.done = true
return nil, io.EOF
}
data := make([]byte, filesize)
if filesize > 0 {
if _, err := io.ReadFull(rd.r, data); err != nil {
return nil, err
}
}
if err := discardN(rd.r, pad4(filesize)); err != nil {
return nil, err
}
return &Entry{
Name: name,
Mode: mode,
UID: uid,
GID: gid,
NLink: nlink,
MTime: mtime,
FileSize: filesize,
Data: data,
}, nil
}
Fields
| Name | Type | Description |
|---|---|---|
| r | io.Reader | |
| done | bool |
S
struct
Writer
Writer writes CPIO newc archives.
pkg/cpio/cpio.go:35-42
type Writer struct
Methods
AddDir
Method
Parameters
name
string
mode
fs.FileMode
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
}
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
}
return writePad4(wr.w, 110+namesize)
}
AddFile
Method
Parameters
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
}
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
}
}
return writePad4(wr.w, filesize)
}
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)
_ = writeNewcHeader(wr.w, wr.ino, 0, 0, 0, 1, wr.mtime, 0, 0, 0, 0, 0, namesize)
_, _ = io.WriteString(wr.w, trail)
_, _ = wr.w.Write([]byte{0})
return writePad4(wr.w, 110+namesize)
}
Fields
| Name | Type | Description |
|---|---|---|
| w | io.Writer | |
| ino | uint32 | |
| uid | uint32 | |
| gid | uint32 | |
| mtime | uint32 | |
| closed | bool |
T
type
WriterOption
pkg/cpio/cpio.go:44-44
type WriterOption func(*Writer)
F
function
WithMTimeUnix
Parameters
mtime
uint32
Returns
pkg/cpio/cpio.go:46-46
func WithMTimeUnix(mtime uint32) WriterOption
{ return func(w *Writer) { w.mtime = mtime } }
Uses
F
function
WithUIDGID
Parameters
uid
uint32
gid
uint32
Returns
pkg/cpio/cpio.go:47-52
func WithUIDGID(uid, gid uint32) WriterOption
{
return func(w *Writer) {
w.uid = uid
w.gid = gid
}
}
Uses
F
function
TestPackAndReadRoundtrip
Parameters
t
pkg/cpio/cpio_test.go:11-41
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
t
pkg/cpio/cpio_test.go:43-68
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))
}
}