212 lines
4.6 KiB
Go
212 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
|
|
}
|