Merge pull request #42451 from thaJeztah/remove_lcow_step1

Remove LCOW code (step 1)
This commit is contained in:
Brian Goff
2021-06-08 13:41:45 -07:00
committed by GitHub
31 changed files with 191 additions and 5546 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,411 +0,0 @@
package format
type SuperBlock struct {
InodesCount uint32
BlocksCountLow uint32
RootBlocksCountLow uint32
FreeBlocksCountLow uint32
FreeInodesCount uint32
FirstDataBlock uint32
LogBlockSize uint32
LogClusterSize uint32
BlocksPerGroup uint32
ClustersPerGroup uint32
InodesPerGroup uint32
Mtime uint32
Wtime uint32
MountCount uint16
MaxMountCount uint16
Magic uint16
State uint16
Errors uint16
MinorRevisionLevel uint16
LastCheck uint32
CheckInterval uint32
CreatorOS uint32
RevisionLevel uint32
DefaultReservedUid uint16
DefaultReservedGid uint16
FirstInode uint32
InodeSize uint16
BlockGroupNr uint16
FeatureCompat CompatFeature
FeatureIncompat IncompatFeature
FeatureRoCompat RoCompatFeature
UUID [16]uint8
VolumeName [16]byte
LastMounted [64]byte
AlgorithmUsageBitmap uint32
PreallocBlocks uint8
PreallocDirBlocks uint8
ReservedGdtBlocks uint16
JournalUUID [16]uint8
JournalInum uint32
JournalDev uint32
LastOrphan uint32
HashSeed [4]uint32
DefHashVersion uint8
JournalBackupType uint8
DescSize uint16
DefaultMountOpts uint32
FirstMetaBg uint32
MkfsTime uint32
JournalBlocks [17]uint32
BlocksCountHigh uint32
RBlocksCountHigh uint32
FreeBlocksCountHigh uint32
MinExtraIsize uint16
WantExtraIsize uint16
Flags uint32
RaidStride uint16
MmpInterval uint16
MmpBlock uint64
RaidStripeWidth uint32
LogGroupsPerFlex uint8
ChecksumType uint8
ReservedPad uint16
KbytesWritten uint64
SnapshotInum uint32
SnapshotID uint32
SnapshotRBlocksCount uint64
SnapshotList uint32
ErrorCount uint32
FirstErrorTime uint32
FirstErrorInode uint32
FirstErrorBlock uint64
FirstErrorFunc [32]uint8
FirstErrorLine uint32
LastErrorTime uint32
LastErrorInode uint32
LastErrorLine uint32
LastErrorBlock uint64
LastErrorFunc [32]uint8
MountOpts [64]uint8
UserQuotaInum uint32
GroupQuotaInum uint32
OverheadBlocks uint32
BackupBgs [2]uint32
EncryptAlgos [4]uint8
EncryptPwSalt [16]uint8
LpfInode uint32
ProjectQuotaInum uint32
ChecksumSeed uint32
WtimeHigh uint8
MtimeHigh uint8
MkfsTimeHigh uint8
LastcheckHigh uint8
FirstErrorTimeHigh uint8
LastErrorTimeHigh uint8
Pad [2]uint8
Reserved [96]uint32
Checksum uint32
}
const SuperBlockMagic uint16 = 0xef53
type CompatFeature uint32
type IncompatFeature uint32
type RoCompatFeature uint32
const (
CompatDirPrealloc CompatFeature = 0x1
CompatImagicInodes CompatFeature = 0x2
CompatHasJournal CompatFeature = 0x4
CompatExtAttr CompatFeature = 0x8
CompatResizeInode CompatFeature = 0x10
CompatDirIndex CompatFeature = 0x20
CompatLazyBg CompatFeature = 0x40
CompatExcludeInode CompatFeature = 0x80
CompatExcludeBitmap CompatFeature = 0x100
CompatSparseSuper2 CompatFeature = 0x200
IncompatCompression IncompatFeature = 0x1
IncompatFiletype IncompatFeature = 0x2
IncompatRecover IncompatFeature = 0x4
IncompatJournalDev IncompatFeature = 0x8
IncompatMetaBg IncompatFeature = 0x10
IncompatExtents IncompatFeature = 0x40
Incompat_64Bit IncompatFeature = 0x80
IncompatMmp IncompatFeature = 0x100
IncompatFlexBg IncompatFeature = 0x200
IncompatEaInode IncompatFeature = 0x400
IncompatDirdata IncompatFeature = 0x1000
IncompatCsumSeed IncompatFeature = 0x2000
IncompatLargedir IncompatFeature = 0x4000
IncompatInlineData IncompatFeature = 0x8000
IncompatEncrypt IncompatFeature = 0x10000
RoCompatSparseSuper RoCompatFeature = 0x1
RoCompatLargeFile RoCompatFeature = 0x2
RoCompatBtreeDir RoCompatFeature = 0x4
RoCompatHugeFile RoCompatFeature = 0x8
RoCompatGdtCsum RoCompatFeature = 0x10
RoCompatDirNlink RoCompatFeature = 0x20
RoCompatExtraIsize RoCompatFeature = 0x40
RoCompatHasSnapshot RoCompatFeature = 0x80
RoCompatQuota RoCompatFeature = 0x100
RoCompatBigalloc RoCompatFeature = 0x200
RoCompatMetadataCsum RoCompatFeature = 0x400
RoCompatReplica RoCompatFeature = 0x800
RoCompatReadonly RoCompatFeature = 0x1000
RoCompatProject RoCompatFeature = 0x2000
)
type BlockGroupFlag uint16
const (
BlockGroupInodeUninit BlockGroupFlag = 0x1
BlockGroupBlockUninit BlockGroupFlag = 0x2
BlockGroupInodeZeroed BlockGroupFlag = 0x4
)
type GroupDescriptor struct {
BlockBitmapLow uint32
InodeBitmapLow uint32
InodeTableLow uint32
FreeBlocksCountLow uint16
FreeInodesCountLow uint16
UsedDirsCountLow uint16
Flags BlockGroupFlag
ExcludeBitmapLow uint32
BlockBitmapCsumLow uint16
InodeBitmapCsumLow uint16
ItableUnusedLow uint16
Checksum uint16
}
type GroupDescriptor64 struct {
GroupDescriptor
BlockBitmapHigh uint32
InodeBitmapHigh uint32
InodeTableHigh uint32
FreeBlocksCountHigh uint16
FreeInodesCountHigh uint16
UsedDirsCountHigh uint16
ItableUnusedHigh uint16
ExcludeBitmapHigh uint32
BlockBitmapCsumHigh uint16
InodeBitmapCsumHigh uint16
Reserved uint32
}
const (
S_IXOTH = 0x1
S_IWOTH = 0x2
S_IROTH = 0x4
S_IXGRP = 0x8
S_IWGRP = 0x10
S_IRGRP = 0x20
S_IXUSR = 0x40
S_IWUSR = 0x80
S_IRUSR = 0x100
S_ISVTX = 0x200
S_ISGID = 0x400
S_ISUID = 0x800
S_IFIFO = 0x1000
S_IFCHR = 0x2000
S_IFDIR = 0x4000
S_IFBLK = 0x6000
S_IFREG = 0x8000
S_IFLNK = 0xA000
S_IFSOCK = 0xC000
TypeMask uint16 = 0xF000
)
type InodeNumber uint32
const (
InodeRoot = 2
)
type Inode struct {
Mode uint16
Uid uint16
SizeLow uint32
Atime uint32
Ctime uint32
Mtime uint32
Dtime uint32
Gid uint16
LinksCount uint16
BlocksLow uint32
Flags InodeFlag
Version uint32
Block [60]byte
Generation uint32
XattrBlockLow uint32
SizeHigh uint32
ObsoleteFragmentAddr uint32
BlocksHigh uint16
XattrBlockHigh uint16
UidHigh uint16
GidHigh uint16
ChecksumLow uint16
Reserved uint16
ExtraIsize uint16
ChecksumHigh uint16
CtimeExtra uint32
MtimeExtra uint32
AtimeExtra uint32
Crtime uint32
CrtimeExtra uint32
VersionHigh uint32
Projid uint32
}
type InodeFlag uint32
const (
InodeFlagSecRm InodeFlag = 0x1
InodeFlagUnRm InodeFlag = 0x2
InodeFlagCompressed InodeFlag = 0x4
InodeFlagSync InodeFlag = 0x8
InodeFlagImmutable InodeFlag = 0x10
InodeFlagAppend InodeFlag = 0x20
InodeFlagNoDump InodeFlag = 0x40
InodeFlagNoAtime InodeFlag = 0x80
InodeFlagDirtyCompressed InodeFlag = 0x100
InodeFlagCompressedClusters InodeFlag = 0x200
InodeFlagNoCompress InodeFlag = 0x400
InodeFlagEncrypted InodeFlag = 0x800
InodeFlagHashedIndex InodeFlag = 0x1000
InodeFlagMagic InodeFlag = 0x2000
InodeFlagJournalData InodeFlag = 0x4000
InodeFlagNoTail InodeFlag = 0x8000
InodeFlagDirSync InodeFlag = 0x10000
InodeFlagTopDir InodeFlag = 0x20000
InodeFlagHugeFile InodeFlag = 0x40000
InodeFlagExtents InodeFlag = 0x80000
InodeFlagEaInode InodeFlag = 0x200000
InodeFlagEOFBlocks InodeFlag = 0x400000
InodeFlagSnapfile InodeFlag = 0x01000000
InodeFlagSnapfileDeleted InodeFlag = 0x04000000
InodeFlagSnapfileShrunk InodeFlag = 0x08000000
InodeFlagInlineData InodeFlag = 0x10000000
InodeFlagProjectIDInherit InodeFlag = 0x20000000
InodeFlagReserved InodeFlag = 0x80000000
)
const (
MaxLinks = 65000
)
type ExtentHeader struct {
Magic uint16
Entries uint16
Max uint16
Depth uint16
Generation uint32
}
const ExtentHeaderMagic uint16 = 0xf30a
type ExtentIndexNode struct {
Block uint32
LeafLow uint32
LeafHigh uint16
Unused uint16
}
type ExtentLeafNode struct {
Block uint32
Length uint16
StartHigh uint16
StartLow uint32
}
type ExtentTail struct {
Checksum uint32
}
type DirectoryEntry struct {
Inode InodeNumber
RecordLength uint16
NameLength uint8
FileType FileType
//Name []byte
}
type FileType uint8
const (
FileTypeUnknown FileType = 0x0
FileTypeRegular FileType = 0x1
FileTypeDirectory FileType = 0x2
FileTypeCharacter FileType = 0x3
FileTypeBlock FileType = 0x4
FileTypeFIFO FileType = 0x5
FileTypeSocket FileType = 0x6
FileTypeSymbolicLink FileType = 0x7
)
type DirectoryEntryTail struct {
ReservedZero1 uint32
RecordLength uint16
ReservedZero2 uint8
FileType uint8
Checksum uint32
}
type DirectoryTreeRoot struct {
Dot DirectoryEntry
DotName [4]byte
DotDot DirectoryEntry
DotDotName [4]byte
ReservedZero uint32
HashVersion uint8
InfoLength uint8
IndirectLevels uint8
UnusedFlags uint8
Limit uint16
Count uint16
Block uint32
//Entries []DirectoryTreeEntry
}
type DirectoryTreeNode struct {
FakeInode uint32
FakeRecordLength uint16
NameLength uint8
FileType uint8
Limit uint16
Count uint16
Block uint32
//Entries []DirectoryTreeEntry
}
type DirectoryTreeEntry struct {
Hash uint32
Block uint32
}
type DirectoryTreeTail struct {
Reserved uint32
Checksum uint32
}
type XAttrInodeBodyHeader struct {
Magic uint32
}
type XAttrHeader struct {
Magic uint32
ReferenceCount uint32
Blocks uint32
Hash uint32
Checksum uint32
Reserved [3]uint32
}
const XAttrHeaderMagic uint32 = 0xea020000
type XAttrEntry struct {
NameLength uint8
NameIndex uint8
ValueOffset uint16
ValueInum uint32
ValueSize uint32
Hash uint32
//Name []byte
}

View File

@@ -1,209 +0,0 @@
package tar2ext4
import (
"archive/tar"
"bufio"
"encoding/binary"
"io"
"os"
"path"
"strings"
"github.com/Microsoft/hcsshim/ext4/internal/compactext4"
"github.com/Microsoft/hcsshim/ext4/internal/format"
)
type params struct {
convertWhiteout bool
appendVhdFooter bool
ext4opts []compactext4.Option
}
// Option is the type for optional parameters to Convert.
type Option func(*params)
// ConvertWhiteout instructs the converter to convert OCI-style whiteouts
// (beginning with .wh.) to overlay-style whiteouts.
func ConvertWhiteout(p *params) {
p.convertWhiteout = true
}
// AppendVhdFooter instructs the converter to add a fixed VHD footer to the
// file.
func AppendVhdFooter(p *params) {
p.appendVhdFooter = true
}
// InlineData instructs the converter to write small files into the inode
// structures directly. This creates smaller images but currently is not
// compatible with DAX.
func InlineData(p *params) {
p.ext4opts = append(p.ext4opts, compactext4.InlineData)
}
// MaximumDiskSize instructs the writer to limit the disk size to the specified
// value. This also reserves enough metadata space for the specified disk size.
// If not provided, then 16GB is the default.
func MaximumDiskSize(size int64) Option {
return func(p *params) {
p.ext4opts = append(p.ext4opts, compactext4.MaximumDiskSize(size))
}
}
const (
whiteoutPrefix = ".wh."
opaqueWhiteout = ".wh..wh..opq"
)
// Convert writes a compact ext4 file system image that contains the files in the
// input tar stream.
func Convert(r io.Reader, w io.ReadWriteSeeker, options ...Option) error {
var p params
for _, opt := range options {
opt(&p)
}
t := tar.NewReader(bufio.NewReader(r))
fs := compactext4.NewWriter(w, p.ext4opts...)
for {
hdr, err := t.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
if p.convertWhiteout {
dir, name := path.Split(hdr.Name)
if strings.HasPrefix(name, whiteoutPrefix) {
if name == opaqueWhiteout {
// Update the directory with the appropriate xattr.
f, err := fs.Stat(dir)
if err != nil {
return err
}
f.Xattrs["trusted.overlay.opaque"] = []byte("y")
err = fs.Create(dir, f)
if err != nil {
return err
}
} else {
// Create an overlay-style whiteout.
f := &compactext4.File{
Mode: compactext4.S_IFCHR,
Devmajor: 0,
Devminor: 0,
}
err = fs.Create(path.Join(dir, name[len(whiteoutPrefix):]), f)
if err != nil {
return err
}
}
continue
}
}
if hdr.Typeflag == tar.TypeLink {
err = fs.Link(hdr.Linkname, hdr.Name)
if err != nil {
return err
}
} else {
f := &compactext4.File{
Mode: uint16(hdr.Mode),
Atime: hdr.AccessTime,
Mtime: hdr.ModTime,
Ctime: hdr.ChangeTime,
Crtime: hdr.ModTime,
Size: hdr.Size,
Uid: uint32(hdr.Uid),
Gid: uint32(hdr.Gid),
Linkname: hdr.Linkname,
Devmajor: uint32(hdr.Devmajor),
Devminor: uint32(hdr.Devminor),
Xattrs: make(map[string][]byte),
}
for key, value := range hdr.PAXRecords {
const xattrPrefix = "SCHILY.xattr."
if strings.HasPrefix(key, xattrPrefix) {
f.Xattrs[key[len(xattrPrefix):]] = []byte(value)
}
}
var typ uint16
switch hdr.Typeflag {
case tar.TypeReg, tar.TypeRegA:
typ = compactext4.S_IFREG
case tar.TypeSymlink:
typ = compactext4.S_IFLNK
case tar.TypeChar:
typ = compactext4.S_IFCHR
case tar.TypeBlock:
typ = compactext4.S_IFBLK
case tar.TypeDir:
typ = compactext4.S_IFDIR
case tar.TypeFifo:
typ = compactext4.S_IFIFO
}
f.Mode &= ^compactext4.TypeMask
f.Mode |= typ
err = fs.CreateWithParents(hdr.Name, f)
if err != nil {
return err
}
_, err = io.Copy(fs, t)
if err != nil {
return err
}
}
}
err := fs.Close()
if err != nil {
return err
}
if p.appendVhdFooter {
size, err := w.Seek(0, io.SeekEnd)
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, makeFixedVHDFooter(size))
if err != nil {
return err
}
}
return nil
}
// ReadExt4SuperBlock reads and returns ext4 super block from VHD
//
// The layout on disk is as follows:
// | Group 0 padding | - 1024 bytes
// | ext4 SuperBlock | - 1 block
// | Group Descriptors | - many blocks
// | Reserved GDT Blocks | - many blocks
// | Data Block Bitmap | - 1 block
// | inode Bitmap | - 1 block
// | inode Table | - many blocks
// | Data Blocks | - many blocks
//
// More details can be found here https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
//
// Our goal is to skip the Group 0 padding, read and return the ext4 SuperBlock
func ReadExt4SuperBlock(vhdPath string) (*format.SuperBlock, error) {
vhd, err := os.OpenFile(vhdPath, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
defer vhd.Close()
// Skip padding at the start
if _, err := vhd.Seek(1024, io.SeekStart); err != nil {
return nil, err
}
var sb format.SuperBlock
if err := binary.Read(vhd, binary.LittleEndian, &sb); err != nil {
return nil, err
}
return &sb, nil
}

View File

@@ -1,76 +0,0 @@
package tar2ext4
import (
"bytes"
"crypto/rand"
"encoding/binary"
)
// Constants for the VHD footer
const (
cookieMagic = "conectix"
featureMask = 0x2
fileFormatVersionMagic = 0x00010000
fixedDataOffset = -1
creatorVersionMagic = 0x000a0000
diskTypeFixed = 2
)
type vhdFooter struct {
Cookie [8]byte
Features uint32
FileFormatVersion uint32
DataOffset int64
TimeStamp uint32
CreatorApplication [4]byte
CreatorVersion uint32
CreatorHostOS [4]byte
OriginalSize int64
CurrentSize int64
DiskGeometry uint32
DiskType uint32
Checksum uint32
UniqueID [16]uint8
SavedState uint8
Reserved [427]uint8
}
func makeFixedVHDFooter(size int64) *vhdFooter {
footer := &vhdFooter{
Features: featureMask,
FileFormatVersion: fileFormatVersionMagic,
DataOffset: fixedDataOffset,
CreatorVersion: creatorVersionMagic,
OriginalSize: size,
CurrentSize: size,
DiskType: diskTypeFixed,
UniqueID: generateUUID(),
}
copy(footer.Cookie[:], cookieMagic)
footer.Checksum = calculateCheckSum(footer)
return footer
}
func calculateCheckSum(footer *vhdFooter) uint32 {
oldchk := footer.Checksum
footer.Checksum = 0
buf := &bytes.Buffer{}
_ = binary.Write(buf, binary.BigEndian, footer)
var chk uint32
bufBytes := buf.Bytes()
for i := 0; i < len(bufBytes); i++ {
chk += uint32(bufBytes[i])
}
footer.Checksum = oldchk
return uint32(^chk)
}
func generateUUID() [16]byte {
res := [16]byte{}
if _, err := rand.Read(res[:]); err != nil {
panic(err)
}
return res
}

View File

@@ -1,109 +0,0 @@
package remotefs
import (
"errors"
"os"
"time"
)
// RemotefsCmd is the name of the remotefs meta command
const RemotefsCmd = "remotefs"
// Name of the commands when called from the cli context (remotefs <CMD> ...)
const (
StatCmd = "stat"
LstatCmd = "lstat"
ReadlinkCmd = "readlink"
MkdirCmd = "mkdir"
MkdirAllCmd = "mkdirall"
RemoveCmd = "remove"
RemoveAllCmd = "removeall"
LinkCmd = "link"
SymlinkCmd = "symlink"
LchmodCmd = "lchmod"
LchownCmd = "lchown"
MknodCmd = "mknod"
MkfifoCmd = "mkfifo"
OpenFileCmd = "openfile"
ReadFileCmd = "readfile"
WriteFileCmd = "writefile"
ReadDirCmd = "readdir"
ResolvePathCmd = "resolvepath"
ExtractArchiveCmd = "extractarchive"
ArchivePathCmd = "archivepath"
)
// ErrInvalid is returned if the parameters are invalid
var ErrInvalid = errors.New("invalid arguments")
// ErrUnknown is returned for an unknown remotefs command
var ErrUnknown = errors.New("unkown command")
// ExportedError is the serialized version of the a Go error.
// It also provides a trivial implementation of the error interface.
type ExportedError struct {
ErrString string
ErrNum int `json:",omitempty"`
}
// Error returns an error string
func (ee *ExportedError) Error() string {
return ee.ErrString
}
// FileInfo is the stat struct returned by the remotefs system. It
// fulfills the os.FileInfo interface.
type FileInfo struct {
NameVar string
SizeVar int64
ModeVar os.FileMode
ModTimeVar int64 // Serialization of time.Time breaks in travis, so use an int
IsDirVar bool
}
var _ os.FileInfo = &FileInfo{}
// Name returns the filename from a FileInfo structure
func (f *FileInfo) Name() string { return f.NameVar }
// Size returns the size from a FileInfo structure
func (f *FileInfo) Size() int64 { return f.SizeVar }
// Mode returns the mode from a FileInfo structure
func (f *FileInfo) Mode() os.FileMode { return f.ModeVar }
// ModTime returns the modification time from a FileInfo structure
func (f *FileInfo) ModTime() time.Time { return time.Unix(0, f.ModTimeVar) }
// IsDir returns the is-directory indicator from a FileInfo structure
func (f *FileInfo) IsDir() bool { return f.IsDirVar }
// Sys provides an interface to a FileInfo structure
func (f *FileInfo) Sys() interface{} { return nil }
// FileHeader is a header for remote *os.File operations for remotefs.OpenFile
type FileHeader struct {
Cmd uint32
Size uint64
}
const (
// Read request command.
Read uint32 = iota
// Write request command.
Write
// Seek request command.
Seek
// Close request command.
Close
// CmdOK is a response meaning request succeeded.
CmdOK
// CmdFailed is a response meaning request failed.
CmdFailed
)
// SeekHeader is header for the Seek operation for remotefs.OpenFile
type SeekHeader struct {
Offset int64
Whence int32
}

View File

@@ -1,578 +0,0 @@
// +build !windows
package remotefs
import (
"bytes"
"encoding/binary"
"encoding/json"
"io"
"os"
"path/filepath"
"strconv"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/symlink"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
// Func is the function definition for a generic remote fs function
// The input to the function is any serialized structs / data from in and the string slice
// from args. The output of the function will be serialized and written to out.
type Func func(stdin io.Reader, stdout io.Writer, args []string) error
// Commands provide a string -> remotefs function mapping.
// This is useful for commandline programs that will receive a string
// as the function to execute.
var Commands = map[string]Func{
StatCmd: Stat,
LstatCmd: Lstat,
ReadlinkCmd: Readlink,
MkdirCmd: Mkdir,
MkdirAllCmd: MkdirAll,
RemoveCmd: Remove,
RemoveAllCmd: RemoveAll,
LinkCmd: Link,
SymlinkCmd: Symlink,
LchmodCmd: Lchmod,
LchownCmd: Lchown,
MknodCmd: Mknod,
MkfifoCmd: Mkfifo,
OpenFileCmd: OpenFile,
ReadFileCmd: ReadFile,
WriteFileCmd: WriteFile,
ReadDirCmd: ReadDir,
ResolvePathCmd: ResolvePath,
ExtractArchiveCmd: ExtractArchive,
ArchivePathCmd: ArchivePath,
}
// Stat functions like os.Stat.
// Args:
// - args[0] is the path
// Out:
// - out = FileInfo object
func Stat(in io.Reader, out io.Writer, args []string) error {
return stat(in, out, args, os.Stat)
}
// Lstat functions like os.Lstat.
// Args:
// - args[0] is the path
// Out:
// - out = FileInfo object
func Lstat(in io.Reader, out io.Writer, args []string) error {
return stat(in, out, args, os.Lstat)
}
func stat(in io.Reader, out io.Writer, args []string, statfunc func(string) (os.FileInfo, error)) error {
if len(args) < 1 {
return ErrInvalid
}
fi, err := statfunc(args[0])
if err != nil {
return err
}
info := FileInfo{
NameVar: fi.Name(),
SizeVar: fi.Size(),
ModeVar: fi.Mode(),
ModTimeVar: fi.ModTime().UnixNano(),
IsDirVar: fi.IsDir(),
}
buf, err := json.Marshal(info)
if err != nil {
return err
}
if _, err := out.Write(buf); err != nil {
return err
}
return nil
}
// Readlink works like os.Readlink
// In:
// - args[0] is path
// Out:
// - Write link result to out
func Readlink(in io.Reader, out io.Writer, args []string) error {
if len(args) < 1 {
return ErrInvalid
}
l, err := os.Readlink(args[0])
if err != nil {
return err
}
if _, err := out.Write([]byte(l)); err != nil {
return err
}
return nil
}
// Mkdir works like os.Mkdir
// Args:
// - args[0] is the path
// - args[1] is the permissions in octal (like 0755)
func Mkdir(in io.Reader, out io.Writer, args []string) error {
return mkdir(in, out, args, os.Mkdir)
}
// MkdirAll works like os.MkdirAll.
// Args:
// - args[0] is the path
// - args[1] is the permissions in octal (like 0755)
func MkdirAll(in io.Reader, out io.Writer, args []string) error {
return mkdir(in, out, args, os.MkdirAll)
}
func mkdir(in io.Reader, out io.Writer, args []string, mkdirFunc func(string, os.FileMode) error) error {
if len(args) < 2 {
return ErrInvalid
}
perm, err := strconv.ParseUint(args[1], 8, 32)
if err != nil {
return err
}
return mkdirFunc(args[0], os.FileMode(perm))
}
// Remove works like os.Remove
// Args:
// - args[0] is the path
func Remove(in io.Reader, out io.Writer, args []string) error {
return remove(in, out, args, os.Remove)
}
// RemoveAll works like os.RemoveAll
// Args:
// - args[0] is the path
func RemoveAll(in io.Reader, out io.Writer, args []string) error {
return remove(in, out, args, os.RemoveAll)
}
func remove(in io.Reader, out io.Writer, args []string, removefunc func(string) error) error {
if len(args) < 1 {
return ErrInvalid
}
return removefunc(args[0])
}
// Link works like os.Link
// Args:
// - args[0] = old path name (link source)
// - args[1] = new path name (link dest)
func Link(in io.Reader, out io.Writer, args []string) error {
return link(in, out, args, os.Link)
}
// Symlink works like os.Symlink
// Args:
// - args[0] = old path name (link source)
// - args[1] = new path name (link dest)
func Symlink(in io.Reader, out io.Writer, args []string) error {
return link(in, out, args, os.Symlink)
}
func link(in io.Reader, out io.Writer, args []string, linkfunc func(string, string) error) error {
if len(args) < 2 {
return ErrInvalid
}
return linkfunc(args[0], args[1])
}
// Lchmod changes permission of the given file without following symlinks
// Args:
// - args[0] = path
// - args[1] = permission mode in octal (like 0755)
func Lchmod(in io.Reader, out io.Writer, args []string) error {
if len(args) < 2 {
return ErrInvalid
}
perm, err := strconv.ParseUint(args[1], 8, 32)
if err != nil {
return err
}
path := args[0]
if !filepath.IsAbs(path) {
path, err = filepath.Abs(path)
if err != nil {
return err
}
}
return unix.Fchmodat(0, path, uint32(perm), unix.AT_SYMLINK_NOFOLLOW)
}
// Lchown works like os.Lchown
// Args:
// - args[0] = path
// - args[1] = uid in base 10
// - args[2] = gid in base 10
func Lchown(in io.Reader, out io.Writer, args []string) error {
if len(args) < 3 {
return ErrInvalid
}
uid, err := strconv.ParseInt(args[1], 10, 64)
if err != nil {
return err
}
gid, err := strconv.ParseInt(args[2], 10, 64)
if err != nil {
return err
}
return os.Lchown(args[0], int(uid), int(gid))
}
// Mknod works like syscall.Mknod
// Args:
// - args[0] = path
// - args[1] = permission mode in octal (like 0755)
// - args[2] = major device number in base 10
// - args[3] = minor device number in base 10
func Mknod(in io.Reader, out io.Writer, args []string) error {
if len(args) < 4 {
return ErrInvalid
}
perm, err := strconv.ParseUint(args[1], 8, 32)
if err != nil {
return err
}
major, err := strconv.ParseInt(args[2], 10, 32)
if err != nil {
return err
}
minor, err := strconv.ParseInt(args[3], 10, 32)
if err != nil {
return err
}
dev := unix.Mkdev(uint32(major), uint32(minor))
return unix.Mknod(args[0], uint32(perm), int(dev))
}
// Mkfifo creates a FIFO special file with the given path name and permissions
// Args:
// - args[0] = path
// - args[1] = permission mode in octal (like 0755)
func Mkfifo(in io.Reader, out io.Writer, args []string) error {
if len(args) < 2 {
return ErrInvalid
}
perm, err := strconv.ParseUint(args[1], 8, 32)
if err != nil {
return err
}
return unix.Mkfifo(args[0], uint32(perm))
}
// OpenFile works like os.OpenFile. To manage the file pointer state,
// this function acts as a single file "file server" with Read/Write/Close
// being serialized control codes from in.
// Args:
// - args[0] = path
// - args[1] = flag in base 10
// - args[2] = permission mode in octal (like 0755)
func OpenFile(in io.Reader, out io.Writer, args []string) (err error) {
logrus.Debugf("OpenFile: %v", args)
defer func() {
if err != nil {
logrus.Errorf("OpenFile: return is non-nil, so writing cmdFailed back: %v", err)
// error code will be serialized by the caller, so don't write it here
WriteFileHeader(out, &FileHeader{Cmd: CmdFailed}, nil)
}
}()
if len(args) < 3 {
logrus.Errorf("OpenFile: Not enough parameters")
return ErrInvalid
}
flag, err := strconv.ParseInt(args[1], 10, 32)
if err != nil {
logrus.Errorf("OpenFile: Invalid flag: %v", err)
return err
}
perm, err := strconv.ParseUint(args[2], 8, 32)
if err != nil {
logrus.Errorf("OpenFile: Invalid permission: %v", err)
return err
}
f, err := os.OpenFile(args[0], int(flag), os.FileMode(perm))
if err != nil {
logrus.Errorf("OpenFile: Failed to open: %v", err)
return err
}
// Signal the client that OpenFile succeeded
logrus.Debugf("OpenFile: Sending OK header")
if err := WriteFileHeader(out, &FileHeader{Cmd: CmdOK}, nil); err != nil {
return err
}
for {
logrus.Debugf("OpenFile: reading header")
hdr, err := ReadFileHeader(in)
if err != nil {
logrus.Errorf("OpenFile: Failed to ReadFileHeader: %v", err)
return err
}
logrus.Debugf("OpenFile: Header: %+v", hdr)
var buf []byte
switch hdr.Cmd {
case Read:
logrus.Debugf("OpenFile: Read command")
buf = make([]byte, hdr.Size, hdr.Size)
n, err := f.Read(buf)
logrus.Debugf("OpenFile: Issued a read for %d, got %d bytes and error %v", hdr.Size, n, err)
if err != nil {
logrus.Errorf("OpenFile: Read failed: %v", err)
return err
}
buf = buf[:n]
case Write:
logrus.Debugf("OpenFile: Write command")
if _, err := io.CopyN(f, in, int64(hdr.Size)); err != nil {
logrus.Errorf("OpenFile: Write CopyN() failed: %v", err)
return err
}
case Seek:
logrus.Debugf("OpenFile: Seek command")
seekHdr := &SeekHeader{}
if err := binary.Read(in, binary.BigEndian, seekHdr); err != nil {
logrus.Errorf("OpenFile: Seek Read() failed: %v", err)
return err
}
res, err := f.Seek(seekHdr.Offset, int(seekHdr.Whence))
if err != nil {
logrus.Errorf("OpenFile: Seek Seek() failed: %v", err)
return err
}
buffer := &bytes.Buffer{}
if err := binary.Write(buffer, binary.BigEndian, res); err != nil {
logrus.Errorf("OpenFile: Seek Write() failed: %v", err)
return err
}
buf = buffer.Bytes()
case Close:
logrus.Debugf("OpenFile: Close command")
if err := f.Close(); err != nil {
return err
}
default:
logrus.Errorf("OpenFile: unknown command")
return ErrUnknown
}
logrus.Debugf("OpenFile: Writing back OK header of size %d", len(buf))
retHdr := &FileHeader{
Cmd: CmdOK,
Size: uint64(len(buf)),
}
if err := WriteFileHeader(out, retHdr, buf); err != nil {
logrus.Errorf("OpenFile: WriteFileHeader() failed: %v", err)
return err
}
if hdr.Cmd == Close {
break
}
}
logrus.Debugf("OpenFile: Done, no error")
return nil
}
// ReadFile works like ioutil.ReadFile but instead writes the file to a writer
// Args:
// - args[0] = path
// Out:
// - Write file contents to out
func ReadFile(in io.Reader, out io.Writer, args []string) error {
if len(args) < 1 {
return ErrInvalid
}
f, err := os.Open(args[0])
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(out, f); err != nil {
return nil
}
return nil
}
// WriteFile works like ioutil.WriteFile but instead reads the file from a reader
// Args:
// - args[0] = path
// - args[1] = permission mode in octal (like 0755)
// - input data stream from in
func WriteFile(in io.Reader, out io.Writer, args []string) error {
if len(args) < 2 {
return ErrInvalid
}
perm, err := strconv.ParseUint(args[1], 8, 32)
if err != nil {
return err
}
f, err := os.OpenFile(args[0], os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(perm))
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(f, in); err != nil {
return err
}
return nil
}
// ReadDir works like *os.File.Readdir but instead writes the result to a writer
// Args:
// - args[0] = path
// - args[1] = number of directory entries to return. If <= 0, return all entries in directory
func ReadDir(in io.Reader, out io.Writer, args []string) error {
if len(args) < 2 {
return ErrInvalid
}
n, err := strconv.ParseInt(args[1], 10, 32)
if err != nil {
return err
}
f, err := os.Open(args[0])
if err != nil {
return err
}
defer f.Close()
infos, err := f.Readdir(int(n))
if err != nil {
return err
}
fileInfos := make([]FileInfo, len(infos))
for i := range infos {
fileInfos[i] = FileInfo{
NameVar: infos[i].Name(),
SizeVar: infos[i].Size(),
ModeVar: infos[i].Mode(),
ModTimeVar: infos[i].ModTime().UnixNano(),
IsDirVar: infos[i].IsDir(),
}
}
buf, err := json.Marshal(fileInfos)
if err != nil {
return err
}
if _, err := out.Write(buf); err != nil {
return err
}
return nil
}
// ResolvePath works like docker's symlink.FollowSymlinkInScope.
// It takens in a `path` and a `root` and evaluates symlinks in `path`
// as if they were scoped in `root`. `path` must be a child path of `root`.
// In other words, `path` must have `root` as a prefix.
// Example:
// path=/foo/bar -> /baz
// root=/foo,
// Expected result = /foo/baz
//
// Args:
// - args[0] is `path`
// - args[1] is `root`
// Out:
// - Write resolved path to stdout
func ResolvePath(in io.Reader, out io.Writer, args []string) error {
if len(args) < 2 {
return ErrInvalid
}
res, err := symlink.FollowSymlinkInScope(args[0], args[1])
if err != nil {
return err
}
if _, err = out.Write([]byte(res)); err != nil {
return err
}
return nil
}
// ExtractArchive extracts the archive read from in.
// Args:
// - in = size of json | json of archive.TarOptions | input tar stream
// - args[0] = extract directory name
func ExtractArchive(in io.Reader, out io.Writer, args []string) error {
logrus.Debugln("ExtractArchive:", args)
if len(args) < 1 {
logrus.Errorln("ExtractArchive: invalid args")
return ErrInvalid
}
opts, err := ReadTarOptions(in)
if err != nil {
logrus.Errorf("ExtractArchive: Failed to read tar options: %v", err)
return err
}
logrus.Debugf("ExtractArchive: Tar options: %+v", opts)
if err := archive.Untar(in, args[0], opts); err != nil {
logrus.Errorf("ExtractArchive: Failed to Untar: %v", err)
return err
}
logrus.Debugf("ExtractArchive: Success")
return nil
}
// ArchivePath archives the given directory and writes it to out.
// Args:
// - in = size of json | json of archive.TarOptions
// - args[0] = source directory name
// Out:
// - out = tar file of the archive
func ArchivePath(in io.Reader, out io.Writer, args []string) error {
if len(args) < 1 {
return ErrInvalid
}
opts, err := ReadTarOptions(in)
if err != nil {
return err
}
r, err := archive.TarWithOptions(args[0], opts)
if err != nil {
return err
}
if _, err := io.Copy(out, r); err != nil {
return err
}
return nil
}

View File

@@ -1,170 +0,0 @@
package remotefs
import (
"bytes"
"encoding/binary"
"encoding/json"
"io"
"io/ioutil"
"os"
"syscall"
"github.com/docker/docker/pkg/archive"
)
// ReadError is an utility function that reads a serialized error from the given reader
// and deserializes it.
func ReadError(in io.Reader) (*ExportedError, error) {
b, err := ioutil.ReadAll(in)
if err != nil {
return nil, err
}
// No error
if len(b) == 0 {
return nil, nil
}
var exportedErr ExportedError
if err := json.Unmarshal(b, &exportedErr); err != nil {
return nil, err
}
return &exportedErr, nil
}
// ExportedToError will convert a ExportedError to an error. It will try to match
// the error to any existing known error like os.ErrNotExist. Otherwise, it will just
// return an implementation of the error interface.
func ExportedToError(ee *ExportedError) error {
if ee.Error() == os.ErrNotExist.Error() {
return os.ErrNotExist
} else if ee.Error() == os.ErrExist.Error() {
return os.ErrExist
} else if ee.Error() == os.ErrPermission.Error() {
return os.ErrPermission
} else if ee.Error() == io.EOF.Error() {
return io.EOF
}
return ee
}
// WriteError is an utility function that serializes the error
// and writes it to the output writer.
func WriteError(err error, out io.Writer) error {
if err == nil {
return nil
}
err = fixOSError(err)
var errno int
switch typedError := err.(type) {
case *os.PathError:
if se, ok := typedError.Err.(syscall.Errno); ok {
errno = int(se)
}
case *os.LinkError:
if se, ok := typedError.Err.(syscall.Errno); ok {
errno = int(se)
}
case *os.SyscallError:
if se, ok := typedError.Err.(syscall.Errno); ok {
errno = int(se)
}
}
exportedError := &ExportedError{
ErrString: err.Error(),
ErrNum: errno,
}
b, err1 := json.Marshal(exportedError)
if err1 != nil {
return err1
}
_, err1 = out.Write(b)
if err1 != nil {
return err1
}
return nil
}
// fixOSError converts possible platform dependent error into the portable errors in the
// Go os package if possible.
func fixOSError(err error) error {
// The os.IsExist, os.IsNotExist, and os.IsPermissions functions are platform
// dependent, so sending the raw error might break those functions on a different OS.
// Go defines portable errors for these.
if os.IsExist(err) {
return os.ErrExist
} else if os.IsNotExist(err) {
return os.ErrNotExist
} else if os.IsPermission(err) {
return os.ErrPermission
}
return err
}
// ReadTarOptions reads from the specified reader and deserializes an archive.TarOptions struct.
func ReadTarOptions(r io.Reader) (*archive.TarOptions, error) {
var size uint64
if err := binary.Read(r, binary.BigEndian, &size); err != nil {
return nil, err
}
rawJSON := make([]byte, size)
if _, err := io.ReadFull(r, rawJSON); err != nil {
return nil, err
}
var opts archive.TarOptions
if err := json.Unmarshal(rawJSON, &opts); err != nil {
return nil, err
}
return &opts, nil
}
// WriteTarOptions serializes a archive.TarOptions struct and writes it to the writer.
func WriteTarOptions(w io.Writer, opts *archive.TarOptions) error {
optsBuf, err := json.Marshal(opts)
if err != nil {
return err
}
optsSize := uint64(len(optsBuf))
optsSizeBuf := &bytes.Buffer{}
if err := binary.Write(optsSizeBuf, binary.BigEndian, optsSize); err != nil {
return err
}
if _, err := optsSizeBuf.WriteTo(w); err != nil {
return err
}
if _, err := w.Write(optsBuf); err != nil {
return err
}
return nil
}
// ReadFileHeader reads from r and returns a deserialized FileHeader
func ReadFileHeader(r io.Reader) (*FileHeader, error) {
hdr := &FileHeader{}
if err := binary.Read(r, binary.BigEndian, hdr); err != nil {
return nil, err
}
return hdr, nil
}
// WriteFileHeader serializes a FileHeader and writes it to w, along with any extra data
func WriteFileHeader(w io.Writer, hdr *FileHeader, extraData []byte) error {
if err := binary.Write(w, binary.BigEndian, hdr); err != nil {
return err
}
if _, err := w.Write(extraData); err != nil {
return err
}
return nil
}