1// +build windows
2
3package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow"
4
5import (
6	"bytes"
7	"fmt"
8	"io"
9	"runtime"
10	"strings"
11	"sync"
12
13	"github.com/Microsoft/hcsshim"
14	"github.com/Microsoft/opengcs/service/gcsutils/remotefs"
15	"github.com/docker/docker/pkg/archive"
16	"github.com/docker/docker/pkg/containerfs"
17	"github.com/sirupsen/logrus"
18)
19
20type lcowfs struct {
21	root        string
22	d           *Driver
23	mappedDisks []hcsshim.MappedVirtualDisk
24	vmID        string
25	currentSVM  *serviceVM
26	sync.Mutex
27}
28
29var _ containerfs.ContainerFS = &lcowfs{}
30
31// ErrNotSupported is an error for unsupported operations in the remotefs
32var ErrNotSupported = fmt.Errorf("not supported")
33
34// Functions to implement the ContainerFS interface
35func (l *lcowfs) Path() string {
36	return l.root
37}
38
39func (l *lcowfs) ResolveScopedPath(path string, rawPath bool) (string, error) {
40	logrus.Debugf("remotefs.resolvescopedpath inputs: %s %s ", path, l.root)
41
42	arg1 := l.Join(l.root, path)
43	if !rawPath {
44		// The l.Join("/", path) will make path an absolute path and then clean it
45		// so if path = ../../X, it will become /X.
46		arg1 = l.Join(l.root, l.Join("/", path))
47	}
48	arg2 := l.root
49
50	output := &bytes.Buffer{}
51	if err := l.runRemoteFSProcess(nil, output, remotefs.ResolvePathCmd, arg1, arg2); err != nil {
52		return "", err
53	}
54
55	logrus.Debugf("remotefs.resolvescopedpath success. Output: %s\n", output.String())
56	return output.String(), nil
57}
58
59func (l *lcowfs) OS() string {
60	return "linux"
61}
62
63func (l *lcowfs) Architecture() string {
64	return runtime.GOARCH
65}
66
67// Other functions that are used by docker like the daemon Archiver/Extractor
68func (l *lcowfs) ExtractArchive(src io.Reader, dst string, opts *archive.TarOptions) error {
69	logrus.Debugf("remotefs.ExtractArchve inputs: %s %+v", dst, opts)
70
71	tarBuf := &bytes.Buffer{}
72	if err := remotefs.WriteTarOptions(tarBuf, opts); err != nil {
73		return fmt.Errorf("failed to marshall tar opts: %s", err)
74	}
75
76	input := io.MultiReader(tarBuf, src)
77	if err := l.runRemoteFSProcess(input, nil, remotefs.ExtractArchiveCmd, dst); err != nil {
78		return fmt.Errorf("failed to extract archive to %s: %s", dst, err)
79	}
80	return nil
81}
82
83func (l *lcowfs) ArchivePath(src string, opts *archive.TarOptions) (io.ReadCloser, error) {
84	logrus.Debugf("remotefs.ArchivePath: %s %+v", src, opts)
85
86	tarBuf := &bytes.Buffer{}
87	if err := remotefs.WriteTarOptions(tarBuf, opts); err != nil {
88		return nil, fmt.Errorf("failed to marshall tar opts: %s", err)
89	}
90
91	r, w := io.Pipe()
92	go func() {
93		defer w.Close()
94		if err := l.runRemoteFSProcess(tarBuf, w, remotefs.ArchivePathCmd, src); err != nil {
95			logrus.Debugf("REMOTEFS: Failed to extract archive: %s %+v %s", src, opts, err)
96		}
97	}()
98	return r, nil
99}
100
101// Helper functions
102func (l *lcowfs) startVM() error {
103	l.Lock()
104	defer l.Unlock()
105	if l.currentSVM != nil {
106		return nil
107	}
108
109	svm, err := l.d.startServiceVMIfNotRunning(l.vmID, l.mappedDisks, fmt.Sprintf("lcowfs.startVM"))
110	if err != nil {
111		return err
112	}
113
114	if err = svm.createUnionMount(l.root, l.mappedDisks...); err != nil {
115		return err
116	}
117	l.currentSVM = svm
118	return nil
119}
120
121func (l *lcowfs) runRemoteFSProcess(stdin io.Reader, stdout io.Writer, args ...string) error {
122	if err := l.startVM(); err != nil {
123		return err
124	}
125
126	// Append remotefs prefix and setup as a command line string
127	cmd := fmt.Sprintf("%s %s", remotefs.RemotefsCmd, strings.Join(args, " "))
128	stderr := &bytes.Buffer{}
129	if err := l.currentSVM.runProcess(cmd, stdin, stdout, stderr); err != nil {
130		return err
131	}
132
133	eerr, err := remotefs.ReadError(stderr)
134	if eerr != nil {
135		// Process returned an error so return that.
136		return remotefs.ExportedToError(eerr)
137	}
138	return err
139}
140