123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- // +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
- }
|