mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.0 KiB
128 lines
4.0 KiB
/* |
|
Copyright The containerd Authors. |
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); |
|
you may not use this file except in compliance with the License. |
|
You may obtain a copy of the License at |
|
|
|
http://www.apache.org/licenses/LICENSE-2.0 |
|
|
|
Unless required by applicable law or agreed to in writing, software |
|
distributed under the License is distributed on an "AS IS" BASIS, |
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
See the License for the specific language governing permissions and |
|
limitations under the License. |
|
*/ |
|
|
|
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{Op: "readlink", Path: name, Err: 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]) |
|
}
|
|
|