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