150 lines
3.8 KiB
Go
150 lines
3.8 KiB
Go
/*
|
|
* Copyright (c) SAS Institute, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package cpio
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/sassoftware/go-rpmutils/fileutil"
|
|
)
|
|
|
|
// Standard set of permission bit masks.
|
|
const (
|
|
S_ISUID = 04000 // Set uid
|
|
S_ISGID = 02000 // Set gid
|
|
S_ISVTX = 01000 // Save text (sticky bit)
|
|
S_ISDIR = 040000 // Directory
|
|
S_ISFIFO = 010000 // FIFO
|
|
S_ISREG = 0100000 // Regular file
|
|
S_ISLNK = 0120000 // Symbolic link
|
|
S_ISBLK = 060000 // Block special file
|
|
S_ISCHR = 020000 // Character special file
|
|
S_ISSOCK = 0140000 // Socket
|
|
)
|
|
|
|
func Extract(rs io.Reader, dest string) error {
|
|
linkMap := make(map[int][]string)
|
|
|
|
stream := NewCpioStream(rs)
|
|
|
|
for {
|
|
entry, err := stream.ReadNextEntry()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if entry.Header.filename == TRAILER {
|
|
break
|
|
}
|
|
|
|
target := path.Join(dest, path.Clean(entry.Header.filename))
|
|
parent := path.Dir(target)
|
|
|
|
// Create the parent directory if it doesn't exist.
|
|
if _, err := os.Stat(parent); os.IsNotExist(err) {
|
|
if err := os.MkdirAll(parent, 0755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// FIXME: Need a makedev implementation in go.
|
|
|
|
switch entry.Header.Mode() &^ 07777 {
|
|
case S_ISCHR:
|
|
logger.Debug("unpacking char device")
|
|
// FIXME: skipping due to lack of makedev.
|
|
continue
|
|
case S_ISBLK:
|
|
logger.Debug("unpacking block device")
|
|
// FIXME: skipping due to lack of makedev.
|
|
continue
|
|
case S_ISDIR:
|
|
logger.Debug("unpacking dir")
|
|
m := os.FileMode(entry.Header.Mode()).Perm()
|
|
if err := os.Mkdir(target, m); err != nil && !os.IsExist(err) {
|
|
return err
|
|
}
|
|
case S_ISFIFO:
|
|
logger.Debug("unpacking named pipe")
|
|
if err := fileutil.Mkfifo(target, uint32(entry.Header.Mode())); err != nil {
|
|
return err
|
|
}
|
|
case S_ISLNK:
|
|
logger.Debug("unpacking symlink")
|
|
buf := make([]byte, entry.Header.c_filesize)
|
|
if _, err := entry.payload.Read(buf); err != nil {
|
|
return err
|
|
}
|
|
if err := os.Symlink(string(buf), target); err != nil {
|
|
return err
|
|
}
|
|
case S_ISREG:
|
|
logger.Debug("unpacking regular file")
|
|
// save hardlinks until after the taget is written
|
|
if entry.Header.c_nlink > 1 && entry.Header.c_filesize == 0 {
|
|
logger.Debug("regular file is a hard link")
|
|
l, ok := linkMap[entry.Header.c_ino]
|
|
if !ok {
|
|
l = make([]string, 0)
|
|
}
|
|
l = append(l, target)
|
|
linkMap[entry.Header.c_ino] = l
|
|
continue
|
|
}
|
|
|
|
// FIXME: Set permissions on files when creating.
|
|
f, err := os.Create(target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
written, err := io.Copy(f, entry.payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if written != int64(entry.Header.c_filesize) {
|
|
logger.Debugf("written: %d, filesize: %d", written, entry.Header.c_filesize)
|
|
return fmt.Errorf("short write")
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create hardlinks after the file content is written.
|
|
if entry.Header.c_nlink > 1 && entry.Header.c_filesize > 0 {
|
|
l, ok := linkMap[entry.Header.c_ino]
|
|
if !ok {
|
|
return fmt.Errorf("hardlinks missing")
|
|
}
|
|
|
|
for _, t := range l {
|
|
if err := os.Link(target, t); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown file mode 0%o for %s",
|
|
entry.Header.c_mode, entry.Header.filename)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|