-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resolve context root to follow symlinks as root directories
Signed-off-by: Darren Stahl <darst@microsoft.com>
- Loading branch information
Showing
13 changed files
with
300 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// +build !windows | ||
|
||
package fstest | ||
|
||
var metadataFiles map[string]bool |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package fstest | ||
|
||
// TODO: Any more metadata files generated by Windows layers? | ||
var metadataFiles = map[string]bool{ | ||
"\\System Volume Information": true, | ||
"\\WcSandboxState": true, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// +build !windows | ||
|
||
package syscallx | ||
|
||
import "syscall" | ||
|
||
// Readlink returns the destination of the named symbolic link. | ||
func Readlink(path string, buf []byte) (n int, err error) { | ||
return syscall.Readlink(path, buf) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package syscallx | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
type reparseDataBuffer struct { | ||
ReparseTag uint32 | ||
ReparseDataLength uint16 | ||
Reserved uint16 | ||
|
||
// GenericReparseBuffer | ||
reparseBuffer byte | ||
} | ||
|
||
type mountPointReparseBuffer struct { | ||
SubstituteNameOffset uint16 | ||
SubstituteNameLength uint16 | ||
PrintNameOffset uint16 | ||
PrintNameLength uint16 | ||
PathBuffer [1]uint16 | ||
} | ||
|
||
type symbolicLinkReparseBuffer struct { | ||
SubstituteNameOffset uint16 | ||
SubstituteNameLength uint16 | ||
PrintNameOffset uint16 | ||
PrintNameLength uint16 | ||
Flags uint32 | ||
PathBuffer [1]uint16 | ||
} | ||
|
||
const ( | ||
_IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 | ||
_SYMLINK_FLAG_RELATIVE = 1 | ||
) | ||
|
||
// Readlink returns the destination of the named symbolic link. | ||
func Readlink(path string, buf []byte) (n int, err error) { | ||
fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING, | ||
syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0) | ||
if err != nil { | ||
return -1, err | ||
} | ||
defer syscall.CloseHandle(fd) | ||
|
||
rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE) | ||
var bytesReturned uint32 | ||
err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil) | ||
if err != nil { | ||
return -1, err | ||
} | ||
|
||
rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0])) | ||
var s string | ||
switch rdb.ReparseTag { | ||
case syscall.IO_REPARSE_TAG_SYMLINK: | ||
data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) | ||
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) | ||
s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2]) | ||
if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 { | ||
if len(s) >= 4 && s[:4] == `\??\` { | ||
s = s[4:] | ||
switch { | ||
case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar | ||
// do nothing | ||
case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar | ||
s = `\\` + s[4:] | ||
default: | ||
// unexpected; do nothing | ||
} | ||
} else { | ||
// unexpected; do nothing | ||
} | ||
} | ||
case _IO_REPARSE_TAG_MOUNT_POINT: | ||
data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) | ||
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) | ||
s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2]) | ||
if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar | ||
if len(s) < 48 || s[:11] != `\??\Volume{` { | ||
s = s[4:] | ||
} | ||
} else { | ||
// unexpected; do nothing | ||
} | ||
default: | ||
// the path is not a symlink or junction but another type of reparse | ||
// point | ||
return -1, syscall.ENOENT | ||
} | ||
n = copy(buf, []byte(s)) | ||
|
||
return n, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package sysx | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/containerd/continuity/syscallx" | ||
) | ||
|
||
// Readlink returns the destination of the named symbolic link. | ||
// If there is an error, it will be of type *PathError. | ||
func Readlink(name string) (string, error) { | ||
for len := 128; ; len *= 2 { | ||
b := make([]byte, len) | ||
n, e := fixCount(syscallx.Readlink(fixLongPath(name), b)) | ||
if e != nil { | ||
return "", &os.PathError{"readlink", name, e} | ||
} | ||
if n < len { | ||
return string(b[0:n]), nil | ||
} | ||
} | ||
} | ||
|
||
// Many functions in package syscall return a count of -1 instead of 0. | ||
// Using fixCount(call()) instead of call() corrects the count. | ||
func fixCount(n int, err error) (int, error) { | ||
if n < 0 { | ||
n = 0 | ||
} | ||
return n, err | ||
} | ||
|
||
// fixLongPath returns the extended-length (\\?\-prefixed) form of | ||
// path when needed, in order to avoid the default 260 character file | ||
// path limit imposed by Windows. If path is not easily converted to | ||
// the extended-length form (for example, if path is a relative path | ||
// or contains .. elements), or is short enough, fixLongPath returns | ||
// path unmodified. | ||
// | ||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath | ||
func fixLongPath(path string) string { | ||
// Do nothing (and don't allocate) if the path is "short". | ||
// Empirically (at least on the Windows Server 2013 builder), | ||
// the kernel is arbitrarily okay with < 248 bytes. That | ||
// matches what the docs above say: | ||
// "When using an API to create a directory, the specified | ||
// path cannot be so long that you cannot append an 8.3 file | ||
// name (that is, the directory name cannot exceed MAX_PATH | ||
// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248. | ||
// | ||
// The MSDN docs appear to say that a normal path that is 248 bytes long | ||
// will work; empirically the path must be less then 248 bytes long. | ||
if len(path) < 248 { | ||
// Don't fix. (This is how Go 1.7 and earlier worked, | ||
// not automatically generating the \\?\ form) | ||
return path | ||
} | ||
|
||
// The extended form begins with \\?\, as in | ||
// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt. | ||
// The extended form disables evaluation of . and .. path | ||
// elements and disables the interpretation of / as equivalent | ||
// to \. The conversion here rewrites / to \ and elides | ||
// . elements as well as trailing or duplicate separators. For | ||
// simplicity it avoids the conversion entirely for relative | ||
// paths or paths containing .. elements. For now, | ||
// \\server\share paths are not converted to | ||
// \\?\UNC\server\share paths because the rules for doing so | ||
// are less well-specified. | ||
if len(path) >= 2 && path[:2] == `\\` { | ||
// Don't canonicalize UNC paths. | ||
return path | ||
} | ||
if !filepath.IsAbs(path) { | ||
// Relative path | ||
return path | ||
} | ||
|
||
const prefix = `\\?` | ||
|
||
pathbuf := make([]byte, len(prefix)+len(path)+len(`\`)) | ||
copy(pathbuf, prefix) | ||
n := len(path) | ||
r, w := 0, len(prefix) | ||
for r < n { | ||
switch { | ||
case os.IsPathSeparator(path[r]): | ||
// empty block | ||
r++ | ||
case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])): | ||
// /./ | ||
r++ | ||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])): | ||
// /../ is currently unhandled | ||
return path | ||
default: | ||
pathbuf[w] = '\\' | ||
w++ | ||
for ; r < n && !os.IsPathSeparator(path[r]); r++ { | ||
pathbuf[w] = path[r] | ||
w++ | ||
} | ||
} | ||
} | ||
// A drive's root directory needs a trailing \ | ||
if w == len(`\\?\c:`) { | ||
pathbuf[w] = '\\' | ||
w++ | ||
} | ||
return string(pathbuf[:w]) | ||
} |