213 lines
4.5 KiB
Go
213 lines
4.5 KiB
Go
// +build windows
|
|
|
|
package 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)
|
|
}
|