diff --git a/cpiocat/cpiocat.go b/cpiocat/cpiocat.go new file mode 100644 index 0000000..85b33b2 --- /dev/null +++ b/cpiocat/cpiocat.go @@ -0,0 +1,181 @@ +package cpiocat + +import ( + "io" + "os" + + "github.com/cavaliergopher/cpio" +) + +// Append: legacy append call. +func Append(out io.Writer, in io.Reader, filesToAppend []string) (err error) { + cat := New(out) + + cat.AppendArch(in) + + for _, file := range filesToAppend { + cat.AppendFile(file, file) + } + + return cat.Close() +} + +type Cat struct { + out io.Writer + cout *cpio.Writer + err error +} + +func New(out io.Writer) *Cat { + cout := cpio.NewWriter(out) + return &Cat{out: out, cout: cout} +} + +func (c *Cat) Err() error { + return c.err +} + +func (c *Cat) Close() error { + err := c.cout.Close() + + if c.err != nil { + return c.err + } + + return err +} + +func (c *Cat) AppendArchFile(srcPath string) (r *Cat) { + r = c + if c.err != nil { + return + } + + f, err := os.Open(srcPath) + if err != nil { + c.err = err + return + } + + defer f.Close() + + return c.AppendArch(f) +} + +func (c *Cat) AppendArch(in io.Reader) (r *Cat) { + r = c + if c.err != nil { + return + } + + cin := cpio.NewReader(in) + + for { + hdr, err := cin.Next() + if err != nil { + if err == io.EOF { + break + } + c.err = err + return + } + + mode := hdr.FileInfo().Mode() + + if mode&os.ModeSymlink != 0 { + // symlink target must be written after + hdr.Size = int64(len(hdr.Linkname)) + } + + err = c.cout.WriteHeader(hdr) + if err != nil { + c.err = err + return + } + + if mode.IsRegular() { + _, err = io.Copy(c.cout, cin) + + } else if mode&os.ModeSymlink != 0 { + _, err = c.cout.Write([]byte(hdr.Linkname)) + } + + if err != nil { + c.err = err + return + } + } + + return +} + +func (c *Cat) AppendFile(srcPath, archPath string) (r *Cat) { + r = c + if c.err != nil { + return + } + + var err error + defer func() { c.err = err }() + + stat, err := os.Lstat(srcPath) + if err != nil { + return + } + + link := "" + if stat.Mode()&os.ModeSymlink != 0 { + link, err = os.Readlink(srcPath) + if err != nil { + return + } + } + + hdr, err := cpio.FileInfoHeader(stat, link) + if err != nil { + return + } + + hdr.Name = archPath + + c.cout.WriteHeader(hdr) + + if stat.Mode().IsRegular() { + var f *os.File + f, err = os.Open(srcPath) + if err != nil { + return + } + defer f.Close() + + _, err = io.Copy(c.cout, f) + + } else if stat.Mode()&os.ModeSymlink != 0 { + _, err = c.cout.Write([]byte(link)) + } + + return +} + +func (c *Cat) AppendBytes(content []byte, archPath string, mode cpio.FileMode) (r *Cat) { + r = c + if c.err != nil { + return + } + + var err error + defer func() { c.err = err }() + + err = c.cout.WriteHeader(&cpio.Header{ + Name: archPath, + Mode: mode, + Size: int64(len(content)), + }) + if err != nil { + return + } + + _, err = c.cout.Write(content) + + return +}