daemon/graphdriver/lcow/remotefs_file.go
7a7357da
 // +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)
 
141b9a74
 	commandLine := fmt.Sprintf("%s %s %s %s %s", remotefs.RemotefsCmd, remotefs.OpenFileCmd, path, flagStr, permStr)
7a7357da
 	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 {
6112ad6e
 		return 0, err
7a7357da
 	}
 
 	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 {
6112ad6e
 		return 0, err
7a7357da
 	}
 
 	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 {
6112ad6e
 		return nil, err
7a7357da
 	}
 
 	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
 }