Merge pull request #1738 from dashpole/docker_godep

Update docker godeps to be the same as k8s
This commit is contained in:
David Ashpole 2017-08-29 14:20:55 -07:00 committed by GitHub
commit 4a77163406
29 changed files with 979 additions and 103 deletions

21
Godeps/Godeps.json generated
View File

@ -30,6 +30,11 @@
"Comment": "v1.8.0", "Comment": "v1.8.0",
"Rev": "4ba9bba6adb6697bcec3841e1ecdfecf5227c3b9" "Rev": "4ba9bba6adb6697bcec3841e1ecdfecf5227c3b9"
}, },
{
"ImportPath": "github.com/Sirupsen/logrus",
"Comment": "v0.7.3-2-g26709e2",
"Rev": "26709e2714106fb8ad40b773b711ebce25b78914"
},
{ {
"ImportPath": "github.com/abbot/go-http-auth", "ImportPath": "github.com/abbot/go-http-auth",
"Rev": "c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35" "Rev": "c0ef4539dfab4d21c8ef20ba2924f9fc6f186d35"
@ -185,23 +190,23 @@
}, },
{ {
"ImportPath": "github.com/docker/docker/pkg/longpath", "ImportPath": "github.com/docker/docker/pkg/longpath",
"Comment": "v1.11.2", "Comment": "v1.13.1-rc2",
"Rev": "b9f10c951893f9a00865890a5232e85d770c1087" "Rev": "54f71fd84a0dabab9d45f5fe7543a028b1200ca1"
}, },
{ {
"ImportPath": "github.com/docker/docker/pkg/mount", "ImportPath": "github.com/docker/docker/pkg/mount",
"Comment": "v1.11.2", "Comment": "v1.13.1-rc2",
"Rev": "b9f10c951893f9a00865890a5232e85d770c1087" "Rev": "54f71fd84a0dabab9d45f5fe7543a028b1200ca1"
}, },
{ {
"ImportPath": "github.com/docker/docker/pkg/symlink", "ImportPath": "github.com/docker/docker/pkg/symlink",
"Comment": "v1.11.2", "Comment": "v1.13.1-rc2",
"Rev": "b9f10c951893f9a00865890a5232e85d770c1087" "Rev": "54f71fd84a0dabab9d45f5fe7543a028b1200ca1"
}, },
{ {
"ImportPath": "github.com/docker/docker/pkg/system", "ImportPath": "github.com/docker/docker/pkg/system",
"Comment": "v1.11.2", "Comment": "v1.13.1-rc2",
"Rev": "b9f10c951893f9a00865890a5232e85d770c1087" "Rev": "54f71fd84a0dabab9d45f5fe7543a028b1200ca1"
}, },
{ {
"ImportPath": "github.com/docker/engine-api/client", "ImportPath": "github.com/docker/engine-api/client",

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,112 @@ import (
"strings" "strings"
) )
var flags = map[string]struct {
clear bool
flag int
}{
"defaults": {false, 0},
"ro": {false, RDONLY},
"rw": {true, RDONLY},
"suid": {true, NOSUID},
"nosuid": {false, NOSUID},
"dev": {true, NODEV},
"nodev": {false, NODEV},
"exec": {true, NOEXEC},
"noexec": {false, NOEXEC},
"sync": {false, SYNCHRONOUS},
"async": {true, SYNCHRONOUS},
"dirsync": {false, DIRSYNC},
"remount": {false, REMOUNT},
"mand": {false, MANDLOCK},
"nomand": {true, MANDLOCK},
"atime": {true, NOATIME},
"noatime": {false, NOATIME},
"diratime": {true, NODIRATIME},
"nodiratime": {false, NODIRATIME},
"bind": {false, BIND},
"rbind": {false, RBIND},
"unbindable": {false, UNBINDABLE},
"runbindable": {false, RUNBINDABLE},
"private": {false, PRIVATE},
"rprivate": {false, RPRIVATE},
"shared": {false, SHARED},
"rshared": {false, RSHARED},
"slave": {false, SLAVE},
"rslave": {false, RSLAVE},
"relatime": {false, RELATIME},
"norelatime": {true, RELATIME},
"strictatime": {false, STRICTATIME},
"nostrictatime": {true, STRICTATIME},
}
var validFlags = map[string]bool{
"": true,
"size": true,
"mode": true,
"uid": true,
"gid": true,
"nr_inodes": true,
"nr_blocks": true,
"mpol": true,
}
var propagationFlags = map[string]bool{
"bind": true,
"rbind": true,
"unbindable": true,
"runbindable": true,
"private": true,
"rprivate": true,
"shared": true,
"rshared": true,
"slave": true,
"rslave": true,
}
// MergeTmpfsOptions merge mount options to make sure there is no duplicate.
func MergeTmpfsOptions(options []string) ([]string, error) {
// We use collisions maps to remove duplicates.
// For flag, the key is the flag value (the key for propagation flag is -1)
// For data=value, the key is the data
flagCollisions := map[int]bool{}
dataCollisions := map[string]bool{}
var newOptions []string
// We process in reverse order
for i := len(options) - 1; i >= 0; i-- {
option := options[i]
if option == "defaults" {
continue
}
if f, ok := flags[option]; ok && f.flag != 0 {
// There is only one propagation mode
key := f.flag
if propagationFlags[option] {
key = -1
}
// Check to see if there is collision for flag
if !flagCollisions[key] {
// We prepend the option and add to collision map
newOptions = append([]string{option}, newOptions...)
flagCollisions[key] = true
}
continue
}
opt := strings.SplitN(option, "=", 2)
if len(opt) != 2 || !validFlags[opt[0]] {
return nil, fmt.Errorf("Invalid tmpfs option %q", opt)
}
if !dataCollisions[opt[0]] {
// We prepend the option and add to collision map
newOptions = append([]string{option}, newOptions...)
dataCollisions[opt[0]] = true
}
}
return newOptions, nil
}
// Parse fstab type mount options into mount() flags // Parse fstab type mount options into mount() flags
// and device specific data // and device specific data
func parseOptions(options string) (int, string) { func parseOptions(options string) (int, string) {
@ -13,45 +119,6 @@ func parseOptions(options string) (int, string) {
data []string data []string
) )
flags := map[string]struct {
clear bool
flag int
}{
"defaults": {false, 0},
"ro": {false, RDONLY},
"rw": {true, RDONLY},
"suid": {true, NOSUID},
"nosuid": {false, NOSUID},
"dev": {true, NODEV},
"nodev": {false, NODEV},
"exec": {true, NOEXEC},
"noexec": {false, NOEXEC},
"sync": {false, SYNCHRONOUS},
"async": {true, SYNCHRONOUS},
"dirsync": {false, DIRSYNC},
"remount": {false, REMOUNT},
"mand": {false, MANDLOCK},
"nomand": {true, MANDLOCK},
"atime": {true, NOATIME},
"noatime": {false, NOATIME},
"diratime": {true, NODIRATIME},
"nodiratime": {false, NODIRATIME},
"bind": {false, BIND},
"rbind": {false, RBIND},
"unbindable": {false, UNBINDABLE},
"runbindable": {false, RUNBINDABLE},
"private": {false, PRIVATE},
"rprivate": {false, RPRIVATE},
"shared": {false, SHARED},
"rshared": {false, RSHARED},
"slave": {false, SLAVE},
"rslave": {false, RSLAVE},
"relatime": {false, RELATIME},
"norelatime": {true, RELATIME},
"strictatime": {false, STRICTATIME},
"nostrictatime": {true, STRICTATIME},
}
for _, o := range strings.Split(options, ",") { for _, o := range strings.Split(options, ",") {
// If the option does not exist in the flags table or the flag // If the option does not exist in the flags table or the flag
// is not supported on the platform, // is not supported on the platform,
@ -72,16 +139,6 @@ func parseOptions(options string) (int, string) {
// ParseTmpfsOptions parse fstab type mount options into flags and data // ParseTmpfsOptions parse fstab type mount options into flags and data
func ParseTmpfsOptions(options string) (int, string, error) { func ParseTmpfsOptions(options string) (int, string, error) {
flags, data := parseOptions(options) flags, data := parseOptions(options)
validFlags := map[string]bool{
"": true,
"size": true,
"mode": true,
"uid": true,
"gid": true,
"nr_inodes": true,
"nr_blocks": true,
"mpol": true,
}
for _, o := range strings.Split(data, ",") { for _, o := range strings.Split(data, ",") {
opt := strings.SplitN(o, "=", 2) opt := strings.SplitN(o, "=", 2)
if !validFlags[opt[0]] { if !validFlags[opt[0]] {

View File

@ -1,4 +1,4 @@
// +build !linux,!freebsd freebsd,!cgo // +build !linux,!freebsd freebsd,!cgo solaris,!cgo
package mount package mount

View File

@ -9,8 +9,8 @@ func GetMounts() ([]*Info, error) {
return parseMountTable() return parseMountTable()
} }
// Mounted looks at /proc/self/mountinfo to determine of the specified // Mounted determines if a specified mountpoint has been mounted.
// mountpoint has been mounted // On Linux it looks at /proc/self/mountinfo and on Solaris at mnttab.
func Mounted(mountpoint string) (bool, error) { func Mounted(mountpoint string) (bool, error) {
entries, err := parseMountTable() entries, err := parseMountTable()
if err != nil { if err != nil {

View File

@ -0,0 +1,33 @@
// +build solaris,cgo
package mount
import (
"golang.org/x/sys/unix"
"unsafe"
)
// #include <stdlib.h>
// #include <stdio.h>
// #include <sys/mount.h>
// int Mount(const char *spec, const char *dir, int mflag,
// char *fstype, char *dataptr, int datalen, char *optptr, int optlen) {
// return mount(spec, dir, mflag, fstype, dataptr, datalen, optptr, optlen);
// }
import "C"
func mount(device, target, mType string, flag uintptr, data string) error {
spec := C.CString(device)
dir := C.CString(target)
fstype := C.CString(mType)
_, err := C.Mount(spec, dir, C.int(flag), fstype, nil, 0, nil, 0)
C.free(unsafe.Pointer(spec))
C.free(unsafe.Pointer(dir))
C.free(unsafe.Pointer(fstype))
return err
}
func unmount(target string, flag int) error {
err := unix.Unmount(target, flag)
return err
}

View File

@ -1,4 +1,4 @@
// +build !linux,!freebsd freebsd,!cgo // +build !linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
package mount package mount

View File

@ -0,0 +1,37 @@
// +build solaris,cgo
package mount
/*
#include <stdio.h>
#include <sys/mnttab.h>
*/
import "C"
import (
"fmt"
)
func parseMountTable() ([]*Info, error) {
mnttab := C.fopen(C.CString(C.MNTTAB), C.CString("r"))
if mnttab == nil {
return nil, fmt.Errorf("Failed to open %s", C.MNTTAB)
}
var out []*Info
var mp C.struct_mnttab
ret := C.getmntent(mnttab, &mp)
for ret == 0 {
var mountinfo Info
mountinfo.Mountpoint = C.GoString(mp.mnt_mountp)
mountinfo.Source = C.GoString(mp.mnt_special)
mountinfo.Fstype = C.GoString(mp.mnt_fstype)
mountinfo.Opts = C.GoString(mp.mnt_mntopts)
out = append(out, &mountinfo)
ret = C.getmntent(mnttab, &mp)
}
C.fclose(mnttab)
return out, nil
}

View File

@ -1,4 +1,4 @@
// +build !windows,!linux,!freebsd freebsd,!cgo // +build !windows,!linux,!freebsd,!solaris freebsd,!cgo solaris,!cgo
package mount package mount

View File

@ -0,0 +1,58 @@
// +build solaris
package mount
// MakeShared ensures a mounted filesystem has the SHARED mount option enabled.
// See the supported options in flags.go for further reference.
func MakeShared(mountPoint string) error {
return ensureMountedAs(mountPoint, "shared")
}
// MakeRShared ensures a mounted filesystem has the RSHARED mount option enabled.
// See the supported options in flags.go for further reference.
func MakeRShared(mountPoint string) error {
return ensureMountedAs(mountPoint, "rshared")
}
// MakePrivate ensures a mounted filesystem has the PRIVATE mount option enabled.
// See the supported options in flags.go for further reference.
func MakePrivate(mountPoint string) error {
return ensureMountedAs(mountPoint, "private")
}
// MakeRPrivate ensures a mounted filesystem has the RPRIVATE mount option
// enabled. See the supported options in flags.go for further reference.
func MakeRPrivate(mountPoint string) error {
return ensureMountedAs(mountPoint, "rprivate")
}
// MakeSlave ensures a mounted filesystem has the SLAVE mount option enabled.
// See the supported options in flags.go for further reference.
func MakeSlave(mountPoint string) error {
return ensureMountedAs(mountPoint, "slave")
}
// MakeRSlave ensures a mounted filesystem has the RSLAVE mount option enabled.
// See the supported options in flags.go for further reference.
func MakeRSlave(mountPoint string) error {
return ensureMountedAs(mountPoint, "rslave")
}
// MakeUnbindable ensures a mounted filesystem has the UNBINDABLE mount option
// enabled. See the supported options in flags.go for further reference.
func MakeUnbindable(mountPoint string) error {
return ensureMountedAs(mountPoint, "unbindable")
}
// MakeRUnbindable ensures a mounted filesystem has the RUNBINDABLE mount
// option enabled. See the supported options in flags.go for further reference.
func MakeRUnbindable(mountPoint string) error {
return ensureMountedAs(mountPoint, "runbindable")
}
func ensureMountedAs(mountPoint, options string) error {
// TODO: Solaris does not support bind mounts.
// Evaluate lofs and also look at the relevant
// mount flags to be supported.
return nil
}

View File

@ -95,8 +95,8 @@ func evalSymlinksInScope(path, root string) (string, error) {
// root gets prepended and we Clean again (to remove any trailing slash // root gets prepended and we Clean again (to remove any trailing slash
// if the first Clean gave us just "/") // if the first Clean gave us just "/")
cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p) cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p)
if cleanP == string(filepath.Separator) { if isDriveOrRoot(cleanP) {
// never Lstat "/" itself // never Lstat "/" itself, or drive letters on Windows
b.Reset() b.Reset()
continue continue
} }
@ -113,7 +113,8 @@ func evalSymlinksInScope(path, root string) (string, error) {
return "", err return "", err
} }
if fi.Mode()&os.ModeSymlink == 0 { if fi.Mode()&os.ModeSymlink == 0 {
b.WriteString(p + string(filepath.Separator)) b.WriteString(p)
b.WriteRune(filepath.Separator)
continue continue
} }

View File

@ -9,3 +9,7 @@ import (
func evalSymlinks(path string) (string, error) { func evalSymlinks(path string) (string, error) {
return filepath.EvalSymlinks(path) return filepath.EvalSymlinks(path)
} }
func isDriveOrRoot(p string) bool {
return p == string(filepath.Separator)
}

View File

@ -153,3 +153,17 @@ func walkSymlinks(path string) (string, error) {
} }
return filepath.Clean(b.String()), nil return filepath.Clean(b.String()), nil
} }
func isDriveOrRoot(p string) bool {
if p == string(filepath.Separator) {
return true
}
length := len(p)
if length >= 2 {
if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) {
return true
}
}
return false
}

View File

@ -6,6 +6,8 @@ package system
import ( import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows"
) )
var ( var (
@ -67,7 +69,7 @@ func PulseEvent(handle syscall.Handle) (err error) {
return setResetPulse(handle, procPulseEvent) return setResetPulse(handle, procPulseEvent)
} }
func setResetPulse(handle syscall.Handle, proc *syscall.LazyProc) (err error) { func setResetPulse(handle syscall.Handle, proc *windows.LazyProc) (err error) {
r0, _, _ := proc.Call(uintptr(handle)) r0, _, _ := proc.Call(uintptr(handle))
if r0 != 0 { if r0 != 0 {
err = syscall.Errno(r0) err = syscall.Errno(r0)

33
vendor/github.com/docker/docker/pkg/system/exitcode.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
package system
import (
"fmt"
"os/exec"
"syscall"
)
// GetExitCode returns the ExitStatus of the specified error if its type is
// exec.ExitError, returns 0 and an error otherwise.
func GetExitCode(err error) (int, error) {
exitCode := 0
if exiterr, ok := err.(*exec.ExitError); ok {
if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok {
return procExit.ExitStatus(), nil
}
}
return exitCode, fmt.Errorf("failed to get exit code")
}
// ProcessExitCode process the specified error and returns the exit status code
// if the error was of type exec.ExitError, returns nothing otherwise.
func ProcessExitCode(err error) (exitCode int) {
if err != nil {
var exiterr error
if exitCode, exiterr = GetExitCode(err); exiterr != nil {
// TODO: Fix this so we check the error's text.
// we've failed to retrieve exit code, so we set it to 127
exitCode = 127
}
}
return
}

View File

@ -7,6 +7,12 @@ import (
"path/filepath" "path/filepath"
) )
// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
// ACL'd for Builtin Administrators and Local System.
func MkdirAllWithACL(path string, perm os.FileMode) error {
return MkdirAll(path, perm)
}
// MkdirAll creates a directory named path along with any necessary parents, // MkdirAll creates a directory named path along with any necessary parents,
// with permission specified by attribute perm for all dir created. // with permission specified by attribute perm for all dir created.
func MkdirAll(path string, perm os.FileMode) error { func MkdirAll(path string, perm os.FileMode) error {
@ -17,3 +23,32 @@ func MkdirAll(path string, perm os.FileMode) error {
func IsAbs(path string) bool { func IsAbs(path string) bool {
return filepath.IsAbs(path) return filepath.IsAbs(path)
} }
// The functions below here are wrappers for the equivalents in the os package.
// They are passthrough on Unix platforms, and only relevant on Windows.
// CreateSequential creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func CreateSequential(name string) (*os.File, error) {
return os.Create(name)
}
// OpenSequential opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func OpenSequential(name string) (*os.File, error) {
return os.Open(name)
}
// OpenFileSequential is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
return os.OpenFile(name, flag, perm)
}

View File

@ -8,15 +8,31 @@ import (
"regexp" "regexp"
"strings" "strings"
"syscall" "syscall"
"unsafe"
winio "github.com/Microsoft/go-winio"
) )
// MkdirAllWithACL is a wrapper for MkdirAll that creates a directory
// ACL'd for Builtin Administrators and Local System.
func MkdirAllWithACL(path string, perm os.FileMode) error {
return mkdirall(path, true)
}
// MkdirAll implementation that is volume path aware for Windows. // MkdirAll implementation that is volume path aware for Windows.
func MkdirAll(path string, perm os.FileMode) error { func MkdirAll(path string, _ os.FileMode) error {
return mkdirall(path, false)
}
// mkdirall is a custom version of os.MkdirAll modified for use on Windows
// so that it is both volume path aware, and can create a directory with
// a DACL.
func mkdirall(path string, adminAndLocalSystem bool) error {
if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) { if re := regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}$`); re.MatchString(path) {
return nil return nil
} }
// The rest of this method is copied from os.MkdirAll and should be kept // The rest of this method is largely copied from os.MkdirAll and should be kept
// as-is to ensure compatibility. // as-is to ensure compatibility.
// Fast path: if we can tell whether path is a directory or file, stop with success or error. // Fast path: if we can tell whether path is a directory or file, stop with success or error.
@ -45,14 +61,19 @@ func MkdirAll(path string, perm os.FileMode) error {
if j > 1 { if j > 1 {
// Create parent // Create parent
err = MkdirAll(path[0:j-1], perm) err = mkdirall(path[0:j-1], false)
if err != nil { if err != nil {
return err return err
} }
} }
// Parent now exists; invoke Mkdir and use its result. // Parent now exists; invoke os.Mkdir or mkdirWithACL and use its result.
err = os.Mkdir(path, perm) if adminAndLocalSystem {
err = mkdirWithACL(path)
} else {
err = os.Mkdir(path, 0)
}
if err != nil { if err != nil {
// Handle arguments like "foo/." by // Handle arguments like "foo/." by
// double-checking that directory doesn't exist. // double-checking that directory doesn't exist.
@ -65,6 +86,36 @@ func MkdirAll(path string, perm os.FileMode) error {
return nil return nil
} }
// mkdirWithACL creates a new directory. If there is an error, it will be of
// type *PathError. .
//
// This is a modified and combined version of os.Mkdir and syscall.Mkdir
// in golang to cater for creating a directory am ACL permitting full
// access, with inheritance, to any subfolder/file for Built-in Administrators
// and Local System.
func mkdirWithACL(name string) error {
sa := syscall.SecurityAttributes{Length: 0}
sddl := "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)"
sd, err := winio.SddlToSecurityDescriptor(sddl)
if err != nil {
return &os.PathError{Op: "mkdir", Path: name, Err: err}
}
sa.Length = uint32(unsafe.Sizeof(sa))
sa.InheritHandle = 1
sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0]))
namep, err := syscall.UTF16PtrFromString(name)
if err != nil {
return &os.PathError{Op: "mkdir", Path: name, Err: err}
}
e := syscall.CreateDirectory(namep, &sa)
if e != nil {
return &os.PathError{Op: "mkdir", Path: name, Err: e}
}
return nil
}
// IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows, // IsAbs is a platform-specific wrapper for filepath.IsAbs. On Windows,
// golang filepath.IsAbs does not consider a path \windows\system32 as absolute // golang filepath.IsAbs does not consider a path \windows\system32 as absolute
// as it doesn't start with a drive-letter/colon combination. However, in // as it doesn't start with a drive-letter/colon combination. However, in
@ -80,3 +131,106 @@ func IsAbs(path string) bool {
} }
return true return true
} }
// The origin of the functions below here are the golang OS and syscall packages,
// slightly modified to only cope with files, not directories due to the
// specific use case.
//
// The alteration is to allow a file on Windows to be opened with
// FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating
// the standby list, particularly when accessing large files such as layer.tar.
// CreateSequential creates the named file with mode 0666 (before umask), truncating
// it if it already exists. If successful, methods on the returned
// File can be used for I/O; the associated file descriptor has mode
// O_RDWR.
// If there is an error, it will be of type *PathError.
func CreateSequential(name string) (*os.File, error) {
return OpenFileSequential(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0)
}
// OpenSequential opens the named file for reading. If successful, methods on
// the returned file can be used for reading; the associated file
// descriptor has mode O_RDONLY.
// If there is an error, it will be of type *PathError.
func OpenSequential(name string) (*os.File, error) {
return OpenFileSequential(name, os.O_RDONLY, 0)
}
// OpenFileSequential is the generalized open call; most users will use Open
// or Create instead.
// If there is an error, it will be of type *PathError.
func OpenFileSequential(name string, flag int, _ os.FileMode) (*os.File, error) {
if name == "" {
return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT}
}
r, errf := syscallOpenFileSequential(name, flag, 0)
if errf == nil {
return r, nil
}
return nil, &os.PathError{Op: "open", Path: name, Err: errf}
}
func syscallOpenFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
r, e := syscallOpenSequential(name, flag|syscall.O_CLOEXEC, 0)
if e != nil {
return nil, e
}
return os.NewFile(uintptr(r), name), nil
}
func makeInheritSa() *syscall.SecurityAttributes {
var sa syscall.SecurityAttributes
sa.Length = uint32(unsafe.Sizeof(sa))
sa.InheritHandle = 1
return &sa
}
func syscallOpenSequential(path string, mode int, _ uint32) (fd syscall.Handle, err error) {
if len(path) == 0 {
return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND
}
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return syscall.InvalidHandle, err
}
var access uint32
switch mode & (syscall.O_RDONLY | syscall.O_WRONLY | syscall.O_RDWR) {
case syscall.O_RDONLY:
access = syscall.GENERIC_READ
case syscall.O_WRONLY:
access = syscall.GENERIC_WRITE
case syscall.O_RDWR:
access = syscall.GENERIC_READ | syscall.GENERIC_WRITE
}
if mode&syscall.O_CREAT != 0 {
access |= syscall.GENERIC_WRITE
}
if mode&syscall.O_APPEND != 0 {
access &^= syscall.GENERIC_WRITE
access |= syscall.FILE_APPEND_DATA
}
sharemode := uint32(syscall.FILE_SHARE_READ | syscall.FILE_SHARE_WRITE)
var sa *syscall.SecurityAttributes
if mode&syscall.O_CLOEXEC == 0 {
sa = makeInheritSa()
}
var createmode uint32
switch {
case mode&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
createmode = syscall.CREATE_NEW
case mode&(syscall.O_CREAT|syscall.O_TRUNC) == (syscall.O_CREAT | syscall.O_TRUNC):
createmode = syscall.CREATE_ALWAYS
case mode&syscall.O_CREAT == syscall.O_CREAT:
createmode = syscall.OPEN_ALWAYS
case mode&syscall.O_TRUNC == syscall.O_TRUNC:
createmode = syscall.TRUNCATE_EXISTING
default:
createmode = syscall.OPEN_EXISTING
}
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN
h, e := syscall.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0)
return h, e
}

View File

@ -0,0 +1,128 @@
// +build solaris,cgo
package system
import (
"fmt"
"unsafe"
)
// #cgo LDFLAGS: -lkstat
// #include <unistd.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <kstat.h>
// #include <sys/swap.h>
// #include <sys/param.h>
// struct swaptable *allocSwaptable(int num) {
// struct swaptable *st;
// struct swapent *swapent;
// st = (struct swaptable *)malloc(num * sizeof(swapent_t) + sizeof (int));
// swapent = st->swt_ent;
// for (int i = 0; i < num; i++,swapent++) {
// swapent->ste_path = (char *)malloc(MAXPATHLEN * sizeof (char));
// }
// st->swt_n = num;
// return st;
//}
// void freeSwaptable (struct swaptable *st) {
// struct swapent *swapent = st->swt_ent;
// for (int i = 0; i < st->swt_n; i++,swapent++) {
// free(swapent->ste_path);
// }
// free(st);
// }
// swapent_t getSwapEnt(swapent_t *ent, int i) {
// return ent[i];
// }
// int64_t getPpKernel() {
// int64_t pp_kernel = 0;
// kstat_ctl_t *ksc;
// kstat_t *ks;
// kstat_named_t *knp;
// kid_t kid;
//
// if ((ksc = kstat_open()) == NULL) {
// return -1;
// }
// if ((ks = kstat_lookup(ksc, "unix", 0, "system_pages")) == NULL) {
// return -1;
// }
// if (((kid = kstat_read(ksc, ks, NULL)) == -1) ||
// ((knp = kstat_data_lookup(ks, "pp_kernel")) == NULL)) {
// return -1;
// }
// switch (knp->data_type) {
// case KSTAT_DATA_UINT64:
// pp_kernel = knp->value.ui64;
// break;
// case KSTAT_DATA_UINT32:
// pp_kernel = knp->value.ui32;
// break;
// }
// pp_kernel *= sysconf(_SC_PAGESIZE);
// return (pp_kernel > 0 ? pp_kernel : -1);
// }
import "C"
// Get the system memory info using sysconf same as prtconf
func getTotalMem() int64 {
pagesize := C.sysconf(C._SC_PAGESIZE)
npages := C.sysconf(C._SC_PHYS_PAGES)
return int64(pagesize * npages)
}
func getFreeMem() int64 {
pagesize := C.sysconf(C._SC_PAGESIZE)
npages := C.sysconf(C._SC_AVPHYS_PAGES)
return int64(pagesize * npages)
}
// ReadMemInfo retrieves memory statistics of the host system and returns a
// MemInfo type.
func ReadMemInfo() (*MemInfo, error) {
ppKernel := C.getPpKernel()
MemTotal := getTotalMem()
MemFree := getFreeMem()
SwapTotal, SwapFree, err := getSysSwap()
if ppKernel < 0 || MemTotal < 0 || MemFree < 0 || SwapTotal < 0 ||
SwapFree < 0 {
return nil, fmt.Errorf("error getting system memory info %v\n", err)
}
meminfo := &MemInfo{}
// Total memory is total physical memory less than memory locked by kernel
meminfo.MemTotal = MemTotal - int64(ppKernel)
meminfo.MemFree = MemFree
meminfo.SwapTotal = SwapTotal
meminfo.SwapFree = SwapFree
return meminfo, nil
}
func getSysSwap() (int64, int64, error) {
var tSwap int64
var fSwap int64
var diskblksPerPage int64
num, err := C.swapctl(C.SC_GETNSWP, nil)
if err != nil {
return -1, -1, err
}
st := C.allocSwaptable(num)
_, err = C.swapctl(C.SC_LIST, unsafe.Pointer(st))
if err != nil {
C.freeSwaptable(st)
return -1, -1, err
}
diskblksPerPage = int64(C.sysconf(C._SC_PAGESIZE) >> C.DEV_BSHIFT)
for i := 0; i < int(num); i++ {
swapent := C.getSwapEnt(&st.swt_ent[0], C.int(i))
tSwap += int64(swapent.ste_pages) * diskblksPerPage
fSwap += int64(swapent.ste_free) * diskblksPerPage
}
C.freeSwaptable(st)
return tSwap, fSwap, nil
}

View File

@ -1,4 +1,4 @@
// +build !linux,!windows // +build !linux,!windows,!solaris
package system package system

View File

@ -1,12 +1,13 @@
package system package system
import ( import (
"syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows"
) )
var ( var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
) )

View File

@ -6,3 +6,9 @@ package system
// executables. Each directory is separated from the next by a colon // executables. Each directory is separated from the next by a colon
// ':' character . // ':' character .
const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" const DefaultPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
// CheckSystemDriveAndRemoveDriveLetter verifies that a path, if it includes a drive letter,
// is the system drive. This is a no-op on Linux.
func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
return path, nil
}

View File

@ -2,6 +2,36 @@
package system package system
import (
"fmt"
"path/filepath"
"strings"
)
// DefaultPathEnv is deliberately empty on Windows as the default path will be set by // DefaultPathEnv is deliberately empty on Windows as the default path will be set by
// the container. Docker has no context of what the default path should be. // the container. Docker has no context of what the default path should be.
const DefaultPathEnv = "" const DefaultPathEnv = ""
// CheckSystemDriveAndRemoveDriveLetter verifies and manipulates a Windows path.
// This is used, for example, when validating a user provided path in docker cp.
// If a drive letter is supplied, it must be the system drive. The drive letter
// is always removed. Also, it translates it to OS semantics (IOW / to \). We
// need the path in this syntax so that it can ultimately be contatenated with
// a Windows long-path which doesn't support drive-letters. Examples:
// C: --> Fail
// C:\ --> \
// a --> a
// /a --> \a
// d:\ --> Fail
func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) {
if len(path) == 2 && string(path[1]) == ":" {
return "", fmt.Errorf("No relative path specified in %q", path)
}
if !filepath.IsAbs(path) || len(path) < 2 {
return filepath.FromSlash(path), nil
}
if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") {
return "", fmt.Errorf("The specified path is not on the system drive (C:)")
}
return filepath.FromSlash(path[2:]), nil
}

View File

@ -0,0 +1,32 @@
package system
import (
"syscall"
)
// fromStatT creates a system.StatT type from a syscall.Stat_t type
func fromStatT(s *syscall.Stat_t) (*StatT, error) {
return &StatT{size: s.Size,
mode: uint32(s.Mode),
uid: s.Uid,
gid: s.Gid,
rdev: uint64(s.Rdev),
mtim: s.Mtimespec}, nil
}
// FromStatT loads a system.StatT from a syscall.Stat_t.
func FromStatT(s *syscall.Stat_t) (*StatT, error) {
return fromStatT(s)
}
// Stat takes a path to a file and returns
// a system.StatT type pertaining to that file.
//
// Throws an error if the file does not exist
func Stat(path string) (*StatT, error) {
s := &syscall.Stat_t{}
if err := syscall.Stat(path, s); err != nil {
return nil, err
}
return fromStatT(s)
}

View File

@ -15,3 +15,20 @@ func fromStatT(s *syscall.Stat_t) (*StatT, error) {
rdev: uint64(s.Rdev), rdev: uint64(s.Rdev),
mtim: s.Mtim}, nil mtim: s.Mtim}, nil
} }
// FromStatT loads a system.StatT from a syscal.Stat_t.
func FromStatT(s *syscall.Stat_t) (*StatT, error) {
return fromStatT(s)
}
// Stat takes a path to a file and returns
// a system.StatT type pertaining to that file.
//
// Throws an error if the file does not exist
func Stat(path string) (*StatT, error) {
s := &syscall.Stat_t{}
if err := syscall.Stat(path, s); err != nil {
return nil, err
}
return fromStatT(s)
}

View File

@ -1,4 +1,4 @@
// +build !linux,!windows,!freebsd,!solaris,!openbsd // +build !linux,!windows,!freebsd,!solaris,!openbsd,!darwin
package system package system

View File

@ -1,9 +1,15 @@
package system package system
import ( import (
"fmt"
"syscall" "syscall"
"unsafe" "unsafe"
"github.com/Sirupsen/logrus"
)
var (
ntuserApiset = syscall.NewLazyDLL("ext-ms-win-ntuser-window-l1-1-0")
procGetVersionExW = modkernel32.NewProc("GetVersionExW")
) )
// OSVersion is a wrapper for Windows version information // OSVersion is a wrapper for Windows version information
@ -15,19 +21,49 @@ type OSVersion struct {
Build uint16 Build uint16
} }
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
type osVersionInfoEx struct {
OSVersionInfoSize uint32
MajorVersion uint32
MinorVersion uint32
BuildNumber uint32
PlatformID uint32
CSDVersion [128]uint16
ServicePackMajor uint16
ServicePackMinor uint16
SuiteMask uint16
ProductType byte
Reserve byte
}
// GetOSVersion gets the operating system version on Windows. Note that // GetOSVersion gets the operating system version on Windows. Note that
// docker.exe must be manifested to get the correct version information. // docker.exe must be manifested to get the correct version information.
func GetOSVersion() (OSVersion, error) { func GetOSVersion() OSVersion {
var err error var err error
osv := OSVersion{} osv := OSVersion{}
osv.Version, err = syscall.GetVersion() osv.Version, err = syscall.GetVersion()
if err != nil { if err != nil {
return osv, fmt.Errorf("Failed to call GetVersion()") // GetVersion never fails.
panic(err)
} }
osv.MajorVersion = uint8(osv.Version & 0xFF) osv.MajorVersion = uint8(osv.Version & 0xFF)
osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF)
osv.Build = uint16(osv.Version >> 16) osv.Build = uint16(osv.Version >> 16)
return osv, nil return osv
}
// IsWindowsClient returns true if the SKU is client
// @engine maintainers - this function should not be removed or modified as it
// is used to enforce licensing restrictions on Windows.
func IsWindowsClient() bool {
osviex := &osVersionInfoEx{OSVersionInfoSize: 284}
r1, _, err := procGetVersionExW.Call(uintptr(unsafe.Pointer(osviex)))
if r1 == 0 {
logrus.Warnf("GetVersionExW failed - assuming server SKU: %v", err)
return false
}
const verNTWorkstation = 0x00000001
return osviex.ProductType == verNTWorkstation
} }
// Unmount is a platform-specific helper function to call // Unmount is a platform-specific helper function to call
@ -58,3 +94,12 @@ func CommandLineToArgv(commandLine string) ([]string, error) {
return newArgs, nil return newArgs, nil
} }
// HasWin32KSupport determines whether containers that depend on win32k can
// run on this machine. Win32k is the driver used to implement windowing.
func HasWin32KSupport() bool {
// For now, check for ntuser API support on the host. In the future, a host
// may support win32k in containers even if the host does not support ntuser
// APIs.
return ntuserApiset.Load() == nil
}

View File

@ -7,7 +7,7 @@ import (
) )
// Umask sets current process's file mode creation mask to newmask // Umask sets current process's file mode creation mask to newmask
// and return oldmask. // and returns oldmask.
func Umask(newmask int) (oldmask int, err error) { func Umask(newmask int) (oldmask int, err error) {
return syscall.Umask(newmask), nil return syscall.Umask(newmask), nil
} }

View File

@ -1,8 +0,0 @@
package system
import "syscall"
// LUtimesNano is not supported by darwin platform.
func LUtimesNano(path string, ts []syscall.Timespec) error {
return ErrNotSupportedPlatform
}

View File

@ -1,10 +1,10 @@
// +build !linux,!freebsd,!darwin // +build !linux,!freebsd
package system package system
import "syscall" import "syscall"
// LUtimesNano is not supported on platforms other than linux, freebsd and darwin. // LUtimesNano is only supported on linux and freebsd.
func LUtimesNano(path string, ts []syscall.Timespec) error { func LUtimesNano(path string, ts []syscall.Timespec) error {
return ErrNotSupportedPlatform return ErrNotSupportedPlatform
} }