| // +build windows |
| |
| package lcow // import "github.com/docker/docker/daemon/graphdriver/lcow" |
| |
| import ( |
| "errors" |
| "os" |
| pathpkg "path" |
| "path/filepath" |
| "sort" |
| "strings" |
| |
| "github.com/containerd/continuity/pathdriver" |
| ) |
| |
| var _ pathdriver.PathDriver = &lcowfs{} |
| |
| // Continuity Path functions can be done locally |
| func (l *lcowfs) Join(path ...string) string { |
| return pathpkg.Join(path...) |
| } |
| |
| func (l *lcowfs) IsAbs(path string) bool { |
| return pathpkg.IsAbs(path) |
| } |
| |
| func sameWord(a, b string) bool { |
| return a == b |
| } |
| |
| // Implementation taken from the Go standard library |
| func (l *lcowfs) Rel(basepath, targpath string) (string, error) { |
| baseVol := "" |
| targVol := "" |
| base := l.Clean(basepath) |
| targ := l.Clean(targpath) |
| if sameWord(targ, base) { |
| return ".", nil |
| } |
| base = base[len(baseVol):] |
| targ = targ[len(targVol):] |
| if base == "." { |
| base = "" |
| } |
| // Can't use IsAbs - `\a` and `a` are both relative in Windows. |
| baseSlashed := len(base) > 0 && base[0] == l.Separator() |
| targSlashed := len(targ) > 0 && targ[0] == l.Separator() |
| if baseSlashed != targSlashed || !sameWord(baseVol, targVol) { |
| return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) |
| } |
| // Position base[b0:bi] and targ[t0:ti] at the first differing elements. |
| bl := len(base) |
| tl := len(targ) |
| var b0, bi, t0, ti int |
| for { |
| for bi < bl && base[bi] != l.Separator() { |
| bi++ |
| } |
| for ti < tl && targ[ti] != l.Separator() { |
| ti++ |
| } |
| if !sameWord(targ[t0:ti], base[b0:bi]) { |
| break |
| } |
| if bi < bl { |
| bi++ |
| } |
| if ti < tl { |
| ti++ |
| } |
| b0 = bi |
| t0 = ti |
| } |
| if base[b0:bi] == ".." { |
| return "", errors.New("Rel: can't make " + targpath + " relative to " + basepath) |
| } |
| if b0 != bl { |
| // Base elements left. Must go up before going down. |
| seps := strings.Count(base[b0:bl], string(l.Separator())) |
| size := 2 + seps*3 |
| if tl != t0 { |
| size += 1 + tl - t0 |
| } |
| buf := make([]byte, size) |
| n := copy(buf, "..") |
| for i := 0; i < seps; i++ { |
| buf[n] = l.Separator() |
| copy(buf[n+1:], "..") |
| n += 3 |
| } |
| if t0 != tl { |
| buf[n] = l.Separator() |
| copy(buf[n+1:], targ[t0:]) |
| } |
| return string(buf), nil |
| } |
| return targ[t0:], nil |
| } |
| |
| func (l *lcowfs) Base(path string) string { |
| return pathpkg.Base(path) |
| } |
| |
| func (l *lcowfs) Dir(path string) string { |
| return pathpkg.Dir(path) |
| } |
| |
| func (l *lcowfs) Clean(path string) string { |
| return pathpkg.Clean(path) |
| } |
| |
| func (l *lcowfs) Split(path string) (dir, file string) { |
| return pathpkg.Split(path) |
| } |
| |
| func (l *lcowfs) Separator() byte { |
| return '/' |
| } |
| |
| func (l *lcowfs) Abs(path string) (string, error) { |
| // Abs is supposed to add the current working directory, which is meaningless in lcow. |
| // So, return an error. |
| return "", ErrNotSupported |
| } |
| |
| // Implementation taken from the Go standard library |
| func (l *lcowfs) Walk(root string, walkFn filepath.WalkFunc) error { |
| info, err := l.Lstat(root) |
| if err != nil { |
| err = walkFn(root, nil, err) |
| } else { |
| err = l.walk(root, info, walkFn) |
| } |
| if err == filepath.SkipDir { |
| return nil |
| } |
| return err |
| } |
| |
| // walk recursively descends path, calling w. |
| func (l *lcowfs) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { |
| err := walkFn(path, info, nil) |
| if err != nil { |
| if info.IsDir() && err == filepath.SkipDir { |
| return nil |
| } |
| return err |
| } |
| |
| if !info.IsDir() { |
| return nil |
| } |
| |
| names, err := l.readDirNames(path) |
| if err != nil { |
| return walkFn(path, info, err) |
| } |
| |
| for _, name := range names { |
| filename := l.Join(path, name) |
| fileInfo, err := l.Lstat(filename) |
| if err != nil { |
| if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { |
| return err |
| } |
| } else { |
| err = l.walk(filename, fileInfo, walkFn) |
| if err != nil { |
| if !fileInfo.IsDir() || err != filepath.SkipDir { |
| return err |
| } |
| } |
| } |
| } |
| return nil |
| } |
| |
| // readDirNames reads the directory named by dirname and returns |
| // a sorted list of directory entries. |
| func (l *lcowfs) readDirNames(dirname string) ([]string, error) { |
| f, err := l.Open(dirname) |
| if err != nil { |
| return nil, err |
| } |
| files, err := f.Readdir(-1) |
| f.Close() |
| if err != nil { |
| return nil, err |
| } |
| |
| names := make([]string, len(files), len(files)) |
| for i := range files { |
| names[i] = files[i].Name() |
| } |
| |
| sort.Strings(names) |
| return names, nil |
| } |
| |
| // Note that Go's filepath.FromSlash/ToSlash convert between OS paths and '/'. Since the path separator |
| // for LCOW (and Unix) is '/', they are no-ops. |
| func (l *lcowfs) FromSlash(path string) string { |
| return path |
| } |
| |
| func (l *lcowfs) ToSlash(path string) string { |
| return path |
| } |
| |
| func (l *lcowfs) Match(pattern, name string) (matched bool, err error) { |
| return pathpkg.Match(pattern, name) |
| } |