mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
7a7357dae1
This enables docker cp and ADD/COPY docker build support for LCOW. Originally, the graphdriver.Get() interface returned a local path to the container root filesystem. This does not work for LCOW, so the Get() method now returns an interface that LCOW implements to support copying to and from the container. Signed-off-by: Akash Gupta <akagup@microsoft.com>
211 lines
4.6 KiB
Go
211 lines
4.6 KiB
Go
// +build windows
|
|
|
|
package lcow
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/Microsoft/hcsshim"
|
|
"github.com/Microsoft/opengcs/service/gcsutils/remotefs"
|
|
"github.com/containerd/continuity/driver"
|
|
)
|
|
|
|
type lcowfile struct {
|
|
process hcsshim.Process
|
|
stdin io.WriteCloser
|
|
stdout io.ReadCloser
|
|
stderr io.ReadCloser
|
|
fs *lcowfs
|
|
guestPath string
|
|
}
|
|
|
|
func (l *lcowfs) Open(path string) (driver.File, error) {
|
|
return l.OpenFile(path, os.O_RDONLY, 0)
|
|
}
|
|
|
|
func (l *lcowfs) OpenFile(path string, flag int, perm os.FileMode) (_ driver.File, err error) {
|
|
flagStr := strconv.FormatInt(int64(flag), 10)
|
|
permStr := strconv.FormatUint(uint64(perm), 8)
|
|
|
|
commandLine := fmt.Sprintf("%s %s %s %s", remotefs.RemotefsCmd, remotefs.OpenFileCmd, flagStr, permStr)
|
|
env := make(map[string]string)
|
|
env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:"
|
|
processConfig := &hcsshim.ProcessConfig{
|
|
EmulateConsole: false,
|
|
CreateStdInPipe: true,
|
|
CreateStdOutPipe: true,
|
|
CreateStdErrPipe: true,
|
|
CreateInUtilityVm: true,
|
|
WorkingDirectory: "/bin",
|
|
Environment: env,
|
|
CommandLine: commandLine,
|
|
}
|
|
|
|
process, err := l.currentSVM.config.Uvm.CreateProcess(processConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open file %s: %s", path, err)
|
|
}
|
|
|
|
stdin, stdout, stderr, err := process.Stdio()
|
|
if err != nil {
|
|
process.Kill()
|
|
process.Close()
|
|
return nil, fmt.Errorf("failed to open file pipes %s: %s", path, err)
|
|
}
|
|
|
|
lf := &lcowfile{
|
|
process: process,
|
|
stdin: stdin,
|
|
stdout: stdout,
|
|
stderr: stderr,
|
|
fs: l,
|
|
guestPath: path,
|
|
}
|
|
|
|
if _, err := lf.getResponse(); err != nil {
|
|
return nil, fmt.Errorf("failed to open file %s: %s", path, err)
|
|
}
|
|
return lf, nil
|
|
}
|
|
|
|
func (l *lcowfile) Read(b []byte) (int, error) {
|
|
hdr := &remotefs.FileHeader{
|
|
Cmd: remotefs.Read,
|
|
Size: uint64(len(b)),
|
|
}
|
|
|
|
if err := remotefs.WriteFileHeader(l.stdin, hdr, nil); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
buf, err := l.getResponse()
|
|
if err != nil {
|
|
return 0, nil
|
|
}
|
|
|
|
n := copy(b, buf)
|
|
return n, nil
|
|
}
|
|
|
|
func (l *lcowfile) Write(b []byte) (int, error) {
|
|
hdr := &remotefs.FileHeader{
|
|
Cmd: remotefs.Write,
|
|
Size: uint64(len(b)),
|
|
}
|
|
|
|
if err := remotefs.WriteFileHeader(l.stdin, hdr, b); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
_, err := l.getResponse()
|
|
if err != nil {
|
|
return 0, nil
|
|
}
|
|
|
|
return len(b), nil
|
|
}
|
|
|
|
func (l *lcowfile) Seek(offset int64, whence int) (int64, error) {
|
|
seekHdr := &remotefs.SeekHeader{
|
|
Offset: offset,
|
|
Whence: int32(whence),
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
if err := binary.Write(buf, binary.BigEndian, seekHdr); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
hdr := &remotefs.FileHeader{
|
|
Cmd: remotefs.Write,
|
|
Size: uint64(buf.Len()),
|
|
}
|
|
if err := remotefs.WriteFileHeader(l.stdin, hdr, buf.Bytes()); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
resBuf, err := l.getResponse()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var res int64
|
|
if err := binary.Read(bytes.NewBuffer(resBuf), binary.BigEndian, &res); err != nil {
|
|
return 0, err
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (l *lcowfile) Close() error {
|
|
hdr := &remotefs.FileHeader{
|
|
Cmd: remotefs.Close,
|
|
Size: 0,
|
|
}
|
|
|
|
if err := remotefs.WriteFileHeader(l.stdin, hdr, nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err := l.getResponse()
|
|
return err
|
|
}
|
|
|
|
func (l *lcowfile) Readdir(n int) ([]os.FileInfo, error) {
|
|
nStr := strconv.FormatInt(int64(n), 10)
|
|
|
|
// Unlike the other File functions, this one can just be run without maintaining state,
|
|
// so just do the normal runRemoteFSProcess way.
|
|
buf := &bytes.Buffer{}
|
|
if err := l.fs.runRemoteFSProcess(nil, buf, remotefs.ReadDirCmd, l.guestPath, nStr); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var info []remotefs.FileInfo
|
|
if err := json.Unmarshal(buf.Bytes(), &info); err != nil {
|
|
return nil, nil
|
|
}
|
|
|
|
osInfo := make([]os.FileInfo, len(info))
|
|
for i := range info {
|
|
osInfo[i] = &info[i]
|
|
}
|
|
return osInfo, nil
|
|
}
|
|
|
|
func (l *lcowfile) getResponse() ([]byte, error) {
|
|
hdr, err := remotefs.ReadFileHeader(l.stdout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if hdr.Cmd != remotefs.CmdOK {
|
|
// Something went wrong during the openfile in the server.
|
|
// Parse stderr and return that as an error
|
|
eerr, err := remotefs.ReadError(l.stderr)
|
|
if eerr != nil {
|
|
return nil, remotefs.ExportedToError(eerr)
|
|
}
|
|
|
|
// Maybe the parsing went wrong?
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// At this point, we know something went wrong in the remotefs program, but
|
|
// we we don't know why.
|
|
return nil, fmt.Errorf("unknown error")
|
|
}
|
|
|
|
// Successful command, we might have some data to read (for Read + Seek)
|
|
buf := make([]byte, hdr.Size, hdr.Size)
|
|
if _, err := io.ReadFull(l.stdout, buf); err != nil {
|
|
return nil, err
|
|
}
|
|
return buf, nil
|
|
}
|