Merge pull request #521 from mtrmac/regsv2-docker

Vendor in vrothberg/image:regsv2-docker
This commit is contained in:
Miloslav Trmač 2018-11-29 14:00:43 +01:00 committed by GitHub
commit fbc2e4f70f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
279 changed files with 11459 additions and 114481 deletions

View File

@ -12,13 +12,10 @@ import (
func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemContext, error) { func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemContext, error) {
ctx := &types.SystemContext{ ctx := &types.SystemContext{
RegistriesDirPath: c.GlobalString("registries.d"), RegistriesDirPath: c.GlobalString("registries.d"),
ArchitectureChoice: c.GlobalString("override-arch"), ArchitectureChoice: c.GlobalString("override-arch"),
OSChoice: c.GlobalString("override-os"), OSChoice: c.GlobalString("override-os"),
DockerCertPath: c.String(flagPrefix + "cert-dir"), DockerCertPath: c.String(flagPrefix + "cert-dir"),
// DEPRECATED: keep this here for backward compatibility, but override
// them if per subcommand flags are provided (see below).
DockerInsecureSkipTLSVerify: !c.GlobalBoolT("tls-verify"),
OSTreeTmpDirPath: c.String(flagPrefix + "ostree-tmp-dir"), OSTreeTmpDirPath: c.String(flagPrefix + "ostree-tmp-dir"),
OCISharedBlobDirPath: c.String(flagPrefix + "shared-blob-dir"), OCISharedBlobDirPath: c.String(flagPrefix + "shared-blob-dir"),
DirForceCompress: c.Bool(flagPrefix + "compress"), DirForceCompress: c.Bool(flagPrefix + "compress"),
@ -27,8 +24,13 @@ func contextFromGlobalOptions(c *cli.Context, flagPrefix string) (*types.SystemC
DockerDaemonCertPath: c.String(flagPrefix + "cert-dir"), DockerDaemonCertPath: c.String(flagPrefix + "cert-dir"),
DockerDaemonInsecureSkipTLSVerify: !c.BoolT(flagPrefix + "tls-verify"), DockerDaemonInsecureSkipTLSVerify: !c.BoolT(flagPrefix + "tls-verify"),
} }
// DEPRECATED: we support --tls-verify for backward compatibility, but override
// it if per-subcommand flags are provided (see below).
if c.GlobalIsSet("tls-verify") {
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.GlobalBoolT("tls-verify"))
}
if c.IsSet(flagPrefix + "tls-verify") { if c.IsSet(flagPrefix + "tls-verify") {
ctx.DockerInsecureSkipTLSVerify = !c.BoolT(flagPrefix + "tls-verify") ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT(flagPrefix + "tls-verify"))
} }
if c.IsSet(flagPrefix + "creds") { if c.IsSet(flagPrefix + "creds") {
var err error var err error

123
cmd/skopeo/utils_test.go Normal file
View File

@ -0,0 +1,123 @@
package main
import (
"flag"
"testing"
"github.com/containers/image/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
// fakeContext creates inputs for contextFromGlobalOptions.
// NOTE: This is QUITE FAKE; none of the urfave/cli normalization and the like happens.
func fakeContext(t *testing.T, cmdName string, globalFlags []string, cmdFlags []string) *cli.Context {
app := createApp()
globalSet := flag.NewFlagSet(app.Name, flag.ContinueOnError)
for _, f := range app.Flags {
f.Apply(globalSet)
}
err := globalSet.Parse(globalFlags)
require.NoError(t, err)
globalCtx := cli.NewContext(app, globalSet, nil)
cmd := app.Command(cmdName)
require.NotNil(t, cmd)
cmdSet := flag.NewFlagSet(cmd.Name, flag.ContinueOnError)
for _, f := range cmd.Flags {
f.Apply(cmdSet)
}
err = cmdSet.Parse(cmdFlags)
require.NoError(t, err)
return cli.NewContext(app, cmdSet, globalCtx)
}
func TestContextFromGlobalOptions(t *testing.T) {
// FIXME: All of this only tests (skopeo copy --dest)
// FIXME FIXME: Apparently BoolT values are set to false if the flag is not declared for the specific subcommand!!
// Default state
c := fakeContext(t, "copy", []string{}, []string{})
res, err := contextFromGlobalOptions(c, "dest-")
require.NoError(t, err)
assert.Equal(t, &types.SystemContext{}, res)
// Explicitly set everything to default, except for when the default is “not present”
c = fakeContext(t, "copy", []string{}, []string{
"--dest-compress=false",
})
res, err = contextFromGlobalOptions(c, "dest-")
require.NoError(t, err)
assert.Equal(t, &types.SystemContext{}, res)
// Set everything to non-default values.
c = fakeContext(t, "copy", []string{
"--registries.d", "/srv/registries.d",
"--override-arch", "overridden-arch",
"--override-os", "overridden-os",
}, []string{
"--authfile", "/srv/authfile",
"--dest-cert-dir", "/srv/cert-dir",
"--dest-ostree-tmp-dir", "/srv/ostree-tmp-dir",
"--dest-shared-blob-dir", "/srv/shared-blob-dir",
"--dest-compress=true",
"--dest-daemon-host", "daemon-host.example.com",
"--dest-tls-verify=false",
"--dest-creds", "creds-user:creds-password",
})
res, err = contextFromGlobalOptions(c, "dest-")
require.NoError(t, err)
assert.Equal(t, &types.SystemContext{
RegistriesDirPath: "/srv/registries.d",
AuthFilePath: "/srv/authfile",
ArchitectureChoice: "overridden-arch",
OSChoice: "overridden-os",
OCISharedBlobDirPath: "/srv/shared-blob-dir",
DockerCertPath: "/srv/cert-dir",
DockerInsecureSkipTLSVerify: types.OptionalBoolTrue,
DockerAuthConfig: &types.DockerAuthConfig{Username: "creds-user", Password: "creds-password"},
OSTreeTmpDirPath: "/srv/ostree-tmp-dir",
DockerDaemonCertPath: "/srv/cert-dir",
DockerDaemonHost: "daemon-host.example.com",
DockerDaemonInsecureSkipTLSVerify: true,
DirForceCompress: true,
}, res)
// Global/per-command tlsVerify behavior
for _, c := range []struct {
global, cmd string
expectedDocker types.OptionalBool
expectedDockerDaemon bool
}{
{"", "", types.OptionalBoolUndefined, false},
{"", "false", types.OptionalBoolTrue, true},
{"", "true", types.OptionalBoolFalse, false},
{"false", "", types.OptionalBoolTrue, false},
{"false", "false", types.OptionalBoolTrue, true},
{"false", "true", types.OptionalBoolFalse, false},
{"true", "", types.OptionalBoolFalse, false},
{"true", "false", types.OptionalBoolTrue, true},
{"true", "true", types.OptionalBoolFalse, false},
} {
globalFlags := []string{}
if c.global != "" {
globalFlags = append(globalFlags, "--tls-verify="+c.global)
}
cmdFlags := []string{}
if c.cmd != "" {
cmdFlags = append(cmdFlags, "--dest-tls-verify="+c.cmd)
}
ctx := fakeContext(t, "copy", globalFlags, cmdFlags)
res, err = contextFromGlobalOptions(ctx, "dest-")
require.NoError(t, err)
assert.Equal(t, c.expectedDocker, res.DockerInsecureSkipTLSVerify, "%#v", c)
assert.Equal(t, c.expectedDockerDaemon, res.DockerDaemonInsecureSkipTLSVerify, "%#v", c)
}
// Invalid option values
c = fakeContext(t, "copy", []string{}, []string{"--dest-creds", ""})
_, err = contextFromGlobalOptions(c, "dest-")
assert.Error(t, err)
}

View File

@ -17,6 +17,7 @@ import (
"github.com/containers/image/docker/reference" "github.com/containers/image/docker/reference"
"github.com/containers/image/pkg/docker/config" "github.com/containers/image/pkg/docker/config"
"github.com/containers/image/pkg/sysregistriesv2"
"github.com/containers/image/pkg/tlsclientconfig" "github.com/containers/image/pkg/tlsclientconfig"
"github.com/containers/image/types" "github.com/containers/image/types"
"github.com/docker/distribution/registry/client" "github.com/docker/distribution/registry/client"
@ -78,11 +79,13 @@ type bearerToken struct {
// dockerClient is configuration for dealing with a single Docker registry. // dockerClient is configuration for dealing with a single Docker registry.
type dockerClient struct { type dockerClient struct {
// The following members are set by newDockerClient and do not change afterwards. // The following members are set by newDockerClient and do not change afterwards.
sys *types.SystemContext sys *types.SystemContext
registry string registry string
client *http.Client
insecureSkipTLSVerify bool
// The following members are not set by newDockerClient and must be set by callers if needed.
username string username string
password string password string
client *http.Client
signatureBase signatureStorageBase signatureBase signatureStorageBase
scope authScope scope authScope
// The following members are detected registry properties: // The following members are detected registry properties:
@ -194,13 +197,26 @@ func newDockerClientFromRef(sys *types.SystemContext, ref dockerReference, write
if err != nil { if err != nil {
return nil, err return nil, err
} }
remoteName := reference.Path(ref.ref)
return newDockerClientWithDetails(sys, registry, username, password, actions, sigBase, remoteName) client, err := newDockerClient(sys, registry, ref.ref.Name())
if err != nil {
return nil, err
}
client.username = username
client.password = password
client.signatureBase = sigBase
client.scope.actions = actions
client.scope.remoteName = reference.Path(ref.ref)
return client, nil
} }
// newDockerClientWithDetails returns a new dockerClient instance for the given parameters // newDockerClient returns a new dockerClient instance for the given registry
func newDockerClientWithDetails(sys *types.SystemContext, registry, username, password, actions string, sigBase signatureStorageBase, remoteName string) (*dockerClient, error) { // and reference. The reference is used to query the registry configuration
// and can either be a registry (e.g, "registry.com[:5000]"), a repository
// (e.g., "registry.com[:5000][/some/namespace]/repo").
// Please note that newDockerClient does not set all members of dockerClient
// (e.g., username and password); those must be set by callers if necessary.
func newDockerClient(sys *types.SystemContext, registry, reference string) (*dockerClient, error) {
hostName := registry hostName := registry
if registry == dockerHostname { if registry == dockerHostname {
registry = dockerRegistry registry = dockerRegistry
@ -221,33 +237,43 @@ func newDockerClientWithDetails(sys *types.SystemContext, registry, username, pa
return nil, err return nil, err
} }
if sys != nil && sys.DockerInsecureSkipTLSVerify { // Check if TLS verification shall be skipped (default=false) which can
tr.TLSClientConfig.InsecureSkipVerify = true // either be specified in the sysregistriesv2 configuration or via the
// SystemContext, whereas the SystemContext is prioritized.
skipVerify := false
if sys != nil && sys.DockerInsecureSkipTLSVerify != types.OptionalBoolUndefined {
// Only use the SystemContext if the actual value is defined.
skipVerify = sys.DockerInsecureSkipTLSVerify == types.OptionalBoolTrue
} else {
reg, err := sysregistriesv2.FindRegistry(sys, reference)
if err != nil {
return nil, errors.Wrapf(err, "error loading registries")
}
if reg != nil {
skipVerify = reg.Insecure
}
} }
tr.TLSClientConfig.InsecureSkipVerify = skipVerify
return &dockerClient{ return &dockerClient{
sys: sys, sys: sys,
registry: registry, registry: registry,
username: username, client: &http.Client{Transport: tr},
password: password, insecureSkipTLSVerify: skipVerify,
client: &http.Client{Transport: tr},
signatureBase: sigBase,
scope: authScope{
actions: actions,
remoteName: remoteName,
},
}, nil }, nil
} }
// CheckAuth validates the credentials by attempting to log into the registry // CheckAuth validates the credentials by attempting to log into the registry
// returns an error if an error occcured while making the http request or the status code received was 401 // returns an error if an error occcured while making the http request or the status code received was 401
func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password, registry string) error { func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password, registry string) error {
newLoginClient, err := newDockerClientWithDetails(sys, registry, username, password, "", nil, "") client, err := newDockerClient(sys, registry, registry)
if err != nil { if err != nil {
return errors.Wrapf(err, "error creating new docker client") return errors.Wrapf(err, "error creating new docker client")
} }
client.username = username
client.password = password
resp, err := newLoginClient.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth) resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth)
if err != nil { if err != nil {
return err return err
} }
@ -299,16 +325,21 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima
return nil, errors.Wrapf(err, "error getting username and password") return nil, errors.Wrapf(err, "error getting username and password")
} }
// The /v2/_catalog endpoint has been disabled for docker.io therefore the call made to that endpoint will fail // The /v2/_catalog endpoint has been disabled for docker.io therefore
// So using the v1 hostname for docker.io for simplicity of implementation and the fact that it returns search results // the call made to that endpoint will fail. So using the v1 hostname
// for docker.io for simplicity of implementation and the fact that it
// returns search results.
hostname := registry
if registry == dockerHostname { if registry == dockerHostname {
registry = dockerV1Hostname hostname = dockerV1Hostname
} }
client, err := newDockerClientWithDetails(sys, registry, username, password, "", nil, "") client, err := newDockerClient(sys, hostname, registry)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error creating new docker client") return nil, errors.Wrapf(err, "error creating new docker client")
} }
client.username = username
client.password = password
// Only try the v1 search endpoint if the search query is not empty. If it is // Only try the v1 search endpoint if the search query is not empty. If it is
// empty skip to the v2 endpoint. // empty skip to the v2 endpoint.
@ -530,7 +561,7 @@ func (c *dockerClient) detectProperties(ctx context.Context) error {
return nil return nil
} }
err := ping("https") err := ping("https")
if err != nil && c.sys != nil && c.sys.DockerInsecureSkipTLSVerify { if err != nil && c.insecureSkipTLSVerify {
err = ping("http") err = ping("http")
} }
if err != nil { if err != nil {
@ -554,7 +585,7 @@ func (c *dockerClient) detectProperties(ctx context.Context) error {
return true return true
} }
isV1 := pingV1("https") isV1 := pingV1("https")
if !isV1 && c.sys != nil && c.sys.DockerInsecureSkipTLSVerify { if !isV1 && c.insecureSkipTLSVerify {
isV1 = pingV1("http") isV1 = pingV1("http")
} }
if isV1 { if isV1 {

View File

@ -275,7 +275,7 @@ func (m *Schema1) ToSchema2Config(diffIDs []digest.Digest) ([]byte, error) {
raw := make(map[string]*json.RawMessage) raw := make(map[string]*json.RawMessage)
err = json.Unmarshal(config, &raw) err = json.Unmarshal(config, &raw)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error re-decoding compat image config %#v: %v", s1) return nil, errors.Wrapf(err, "error re-decoding compat image config %#v", s1)
} }
// Drop some fields. // Drop some fields.
delete(raw, "id") delete(raw, "id")

View File

@ -25,7 +25,7 @@ func newImageDestination(ctx context.Context, sys *types.SystemContext, ref ociA
unpackedDest, err := tempDirRef.ociRefExtracted.NewImageDestination(ctx, sys) unpackedDest, err := tempDirRef.ociRefExtracted.NewImageDestination(ctx, sys)
if err != nil { if err != nil {
if err := tempDirRef.deleteTempDir(); err != nil { if err := tempDirRef.deleteTempDir(); err != nil {
return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory) return nil, errors.Wrapf(err, "error deleting temp directory %q", tempDirRef.tempDirectory)
} }
return nil, err return nil, err
} }

View File

@ -28,7 +28,7 @@ func newImageSource(ctx context.Context, sys *types.SystemContext, ref ociArchiv
unpackedSrc, err := tempDirRef.ociRefExtracted.NewImageSource(ctx, sys) unpackedSrc, err := tempDirRef.ociRefExtracted.NewImageSource(ctx, sys)
if err != nil { if err != nil {
if err := tempDirRef.deleteTempDir(); err != nil { if err := tempDirRef.deleteTempDir(); err != nil {
return nil, errors.Wrapf(err, "error deleting temp directory", tempDirRef.tempDirectory) return nil, errors.Wrapf(err, "error deleting temp directory %q", tempDirRef.tempDirectory)
} }
return nil, err return nil, err
} }

View File

@ -96,7 +96,7 @@ func (c *openshiftClient) doRequest(ctx context.Context, method, path string, re
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
logrus.Debugf("%s %s", method, url) logrus.Debugf("%s %s", method, url.String())
res, err := c.httpClient.Do(req) res, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,367 @@
package sysregistriesv2
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"github.com/BurntSushi/toml"
"github.com/containers/image/types"
)
// systemRegistriesConfPath is the path to the system-wide registry
// configuration file and is used to add/subtract potential registries for
// obtaining images. You can override this at build time with
// -ldflags '-X github.com/containers/image/sysregistries.systemRegistriesConfPath=$your_path'
var systemRegistriesConfPath = builtinRegistriesConfPath
// builtinRegistriesConfPath is the path to the registry configuration file.
// DO NOT change this, instead see systemRegistriesConfPath above.
const builtinRegistriesConfPath = "/etc/containers/registries.conf"
// Mirror represents a mirror. Mirrors can be used as pull-through caches for
// registries.
type Mirror struct {
// The mirror's URL.
URL string `toml:"url"`
// If true, certs verification will be skipped and HTTP (non-TLS)
// connections will be allowed.
Insecure bool `toml:"insecure"`
}
// Registry represents a registry.
type Registry struct {
// Serializable registry URL.
URL string `toml:"url"`
// The registry's mirrors.
Mirrors []Mirror `toml:"mirror"`
// If true, pulling from the registry will be blocked.
Blocked bool `toml:"blocked"`
// If true, certs verification will be skipped and HTTP (non-TLS)
// connections will be allowed.
Insecure bool `toml:"insecure"`
// If true, the registry can be used when pulling an unqualified image.
Search bool `toml:"unqualified-search"`
// Prefix is used for matching images, and to translate one namespace to
// another. If `Prefix="example.com/bar"`, `URL="example.com/foo/bar"`
// and we pull from "example.com/bar/myimage:latest", the image will
// effectively be pulled from "example.com/foo/bar/myimage:latest".
// If no Prefix is specified, it defaults to the specified URL.
Prefix string `toml:"prefix"`
}
// backwards compatability to sysregistries v1
type v1TOMLregistries struct {
Registries []string `toml:"registries"`
}
// tomlConfig is the data type used to unmarshal the toml config.
type tomlConfig struct {
Registries []Registry `toml:"registry"`
// backwards compatability to sysregistries v1
V1Registries struct {
Search v1TOMLregistries `toml:"search"`
Insecure v1TOMLregistries `toml:"insecure"`
Block v1TOMLregistries `toml:"block"`
} `toml:"registries"`
}
// InvalidRegistries represents an invalid registry configurations. An example
// is when "registry.com" is defined multiple times in the configuration but
// with conflicting security settings.
type InvalidRegistries struct {
s string
}
// Error returns the error string.
func (e *InvalidRegistries) Error() string {
return e.s
}
// parseURL parses the input string, performs some sanity checks and returns
// the sanitized input string. An error is returned if the input string is
// empty or if contains an "http{s,}://" prefix.
func parseURL(input string) (string, error) {
trimmed := strings.TrimRight(input, "/")
if trimmed == "" {
return "", &InvalidRegistries{s: "invalid URL: cannot be empty"}
}
if strings.HasPrefix(trimmed, "http://") || strings.HasPrefix(trimmed, "https://") {
msg := fmt.Sprintf("invalid URL '%s': URI schemes are not supported", input)
return "", &InvalidRegistries{s: msg}
}
return trimmed, nil
}
// getV1Registries transforms v1 registries in the config into an array of v2
// registries of type Registry.
func getV1Registries(config *tomlConfig) ([]Registry, error) {
regMap := make(map[string]*Registry)
// We must preserve the order of config.V1Registries.Search.Registries at least. The order of the
// other registries is not really important, but make it deterministic (the same for the same config file)
// to minimize behavior inconsistency and not contribute to difficult-to-reproduce situations.
registryOrder := []string{}
getRegistry := func(url string) (*Registry, error) { // Note: _pointer_ to a long-lived object
var err error
url, err = parseURL(url)
if err != nil {
return nil, err
}
reg, exists := regMap[url]
if !exists {
reg = &Registry{
URL: url,
Mirrors: []Mirror{},
Prefix: url,
}
regMap[url] = reg
registryOrder = append(registryOrder, url)
}
return reg, nil
}
// Note: config.V1Registries.Search needs to be processed first to ensure registryOrder is populated in the right order
// if one of the search registries is also in one of the other lists.
for _, search := range config.V1Registries.Search.Registries {
reg, err := getRegistry(search)
if err != nil {
return nil, err
}
reg.Search = true
}
for _, blocked := range config.V1Registries.Block.Registries {
reg, err := getRegistry(blocked)
if err != nil {
return nil, err
}
reg.Blocked = true
}
for _, insecure := range config.V1Registries.Insecure.Registries {
reg, err := getRegistry(insecure)
if err != nil {
return nil, err
}
reg.Insecure = true
}
registries := []Registry{}
for _, url := range registryOrder {
reg := regMap[url]
registries = append(registries, *reg)
}
return registries, nil
}
// postProcessRegistries checks the consistency of all registries (e.g., set
// the Prefix to URL if not set) and applies conflict checks. It returns an
// array of cleaned registries and error in case of conflicts.
func postProcessRegistries(regs []Registry) ([]Registry, error) {
var registries []Registry
regMap := make(map[string][]Registry)
for _, reg := range regs {
var err error
// make sure URL and Prefix are valid
reg.URL, err = parseURL(reg.URL)
if err != nil {
return nil, err
}
if reg.Prefix == "" {
reg.Prefix = reg.URL
} else {
reg.Prefix, err = parseURL(reg.Prefix)
if err != nil {
return nil, err
}
}
// make sure mirrors are valid
for _, mir := range reg.Mirrors {
mir.URL, err = parseURL(mir.URL)
if err != nil {
return nil, err
}
}
registries = append(registries, reg)
regMap[reg.URL] = append(regMap[reg.URL], reg)
}
// Given a registry can be mentioned multiple times (e.g., to have
// multiple prefixes backed by different mirrors), we need to make sure
// there are no conflicts among them.
//
// Note: we need to iterate over the registries array to ensure a
// deterministic behavior which is not guaranteed by maps.
for _, reg := range registries {
others, _ := regMap[reg.URL]
for _, other := range others {
if reg.Insecure != other.Insecure {
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'insecure' setting", reg.URL)
return nil, &InvalidRegistries{s: msg}
}
if reg.Blocked != other.Blocked {
msg := fmt.Sprintf("registry '%s' is defined multiple times with conflicting 'blocked' setting", reg.URL)
return nil, &InvalidRegistries{s: msg}
}
}
}
return registries, nil
}
// getConfigPath returns the system-registries config path if specified.
// Otherwise, systemRegistriesConfPath is returned.
func getConfigPath(ctx *types.SystemContext) string {
confPath := systemRegistriesConfPath
if ctx != nil {
if ctx.SystemRegistriesConfPath != "" {
confPath = ctx.SystemRegistriesConfPath
} else if ctx.RootForImplicitAbsolutePaths != "" {
confPath = filepath.Join(ctx.RootForImplicitAbsolutePaths, systemRegistriesConfPath)
}
}
return confPath
}
// configMutex is used to synchronize concurrent accesses to configCache.
var configMutex = sync.Mutex{}
// configCache caches already loaded configs with config paths as keys and is
// used to avoid redudantly parsing configs. Concurrent accesses to the cache
// are synchronized via configMutex.
var configCache = make(map[string][]Registry)
// InvalidateCache invalidates the registry cache. This function is meant to be
// used for long-running processes that need to reload potential changes made to
// the cached registry config files.
func InvalidateCache() {
configMutex.Lock()
defer configMutex.Unlock()
configCache = make(map[string][]Registry)
}
// GetRegistries loads and returns the registries specified in the config.
// Note the parsed content of registry config files is cached. For reloading,
// use `InvalidateCache` and re-call `GetRegistries`.
func GetRegistries(ctx *types.SystemContext) ([]Registry, error) {
configPath := getConfigPath(ctx)
configMutex.Lock()
defer configMutex.Unlock()
// if the config has already been loaded, return the cached registries
if registries, inCache := configCache[configPath]; inCache {
return registries, nil
}
// load the config
config, err := loadRegistryConf(configPath)
if err != nil {
// Return an empty []Registry if we use the default config,
// which implies that the config path of the SystemContext
// isn't set. Note: if ctx.SystemRegistriesConfPath points to
// the default config, we will still return an error.
if os.IsNotExist(err) && (ctx == nil || ctx.SystemRegistriesConfPath == "") {
return []Registry{}, nil
}
return nil, err
}
registries := config.Registries
// backwards compatibility for v1 configs
v1Registries, err := getV1Registries(config)
if err != nil {
return nil, err
}
if len(v1Registries) > 0 {
if len(registries) > 0 {
return nil, &InvalidRegistries{s: "mixing sysregistry v1/v2 is not supported"}
}
registries = v1Registries
}
registries, err = postProcessRegistries(registries)
if err != nil {
return nil, err
}
// populate the cache
configCache[configPath] = registries
return registries, err
}
// FindUnqualifiedSearchRegistries returns all registries that are configured
// for unqualified image search (i.e., with Registry.Search == true).
func FindUnqualifiedSearchRegistries(ctx *types.SystemContext) ([]Registry, error) {
registries, err := GetRegistries(ctx)
if err != nil {
return nil, err
}
unqualified := []Registry{}
for _, reg := range registries {
if reg.Search {
unqualified = append(unqualified, reg)
}
}
return unqualified, nil
}
// FindRegistry returns the Registry with the longest prefix for ref. If no
// Registry prefixes the image, nil is returned.
func FindRegistry(ctx *types.SystemContext, ref string) (*Registry, error) {
registries, err := GetRegistries(ctx)
if err != nil {
return nil, err
}
reg := Registry{}
prefixLen := 0
for _, r := range registries {
if strings.HasPrefix(ref, r.Prefix+"/") || ref == r.Prefix {
length := len(r.Prefix)
if length > prefixLen {
reg = r
prefixLen = length
}
}
}
if prefixLen != 0 {
return &reg, nil
}
return nil, nil
}
// Reads the global registry file from the filesystem. Returns a byte array.
func readRegistryConf(configPath string) ([]byte, error) {
configBytes, err := ioutil.ReadFile(configPath)
return configBytes, err
}
// Used in unittests to parse custom configs without a types.SystemContext.
var readConf = readRegistryConf
// Loads the registry configuration file from the filesystem and then unmarshals
// it. Returns the unmarshalled object.
func loadRegistryConf(configPath string) (*tomlConfig, error) {
config := &tomlConfig{}
configBytes, err := readConf(configPath)
if err != nil {
return nil, err
}
err = toml.Unmarshal(configBytes, &config)
return config, err
}

View File

@ -107,7 +107,7 @@ func (s *storageTransport) DefaultGIDMap() []idtools.IDMap {
// relative to the given store, and returns it in a reference object. // relative to the given store, and returns it in a reference object.
func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (*storageReference, error) { func (s storageTransport) ParseStoreReference(store storage.Store, ref string) (*storageReference, error) {
if ref == "" { if ref == "" {
return nil, errors.Wrapf(ErrInvalidReference, "%q is an empty reference") return nil, errors.Wrapf(ErrInvalidReference, "%q is an empty reference", ref)
} }
if ref[0] == '[' { if ref[0] == '[' {
// Ignore the store specifier. // Ignore the store specifier.

View File

@ -324,6 +324,30 @@ type DockerAuthConfig struct {
Password string Password string
} }
// OptionalBool is a boolean with an additional undefined value, which is meant
// to be used in the context of user input to distinguish between a
// user-specified value and a default value.
type OptionalBool byte
const (
// OptionalBoolUndefined indicates that the OptionalBoolean hasn't been written.
OptionalBoolUndefined OptionalBool = iota
// OptionalBoolTrue represents the boolean true.
OptionalBoolTrue
// OptionalBoolFalse represents the boolean false.
OptionalBoolFalse
)
// NewOptionalBool converts the input bool into either OptionalBoolTrue or
// OptionalBoolFalse. The function is meant to avoid boilerplate code of users.
func NewOptionalBool(b bool) OptionalBool {
o := OptionalBoolFalse
if b == true {
o = OptionalBoolTrue
}
return o
}
// SystemContext allows parameterizing access to implicitly-accessed resources, // SystemContext allows parameterizing access to implicitly-accessed resources,
// like configuration files in /etc and users' login state in their home directory. // like configuration files in /etc and users' login state in their home directory.
// Various components can share the same field only if their semantics is exactly // Various components can share the same field only if their semantics is exactly
@ -376,7 +400,7 @@ type SystemContext struct {
// Ignored if DockerCertPath is non-empty. // Ignored if DockerCertPath is non-empty.
DockerPerHostCertDirPath string DockerPerHostCertDirPath string
// Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections. // Allow contacting docker registries over HTTP, or HTTPS with failed TLS verification. Note that this does not affect other TLS connections.
DockerInsecureSkipTLSVerify bool DockerInsecureSkipTLSVerify OptionalBool
// if nil, the library tries to parse ~/.docker/config.json to retrieve credentials // if nil, the library tries to parse ~/.docker/config.json to retrieve credentials
DockerAuthConfig *DockerAuthConfig DockerAuthConfig *DockerAuthConfig
// if not "", an User-Agent header is added to each request when contacting a registry. // if not "", an User-Agent header is added to each request when contacting a registry.

View File

@ -34,7 +34,7 @@ github.com/xeipuuv/gojsonschema master
github.com/xeipuuv/gojsonreference master github.com/xeipuuv/gojsonreference master
github.com/xeipuuv/gojsonpointer master github.com/xeipuuv/gojsonpointer master
github.com/tchap/go-patricia v2.2.6 github.com/tchap/go-patricia v2.2.6
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d github.com/opencontainers/selinux 077c8b6d1c18456fb7c792bc0de52295a0d1900e
github.com/BurntSushi/toml b26d9c308763d68093482582cea63d69be07a0f0 github.com/BurntSushi/toml b26d9c308763d68093482582cea63d69be07a0f0
github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460 github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460
github.com/gogo/protobuf fcdc5011193ff531a548e9b0301828d5a5b97fd8 github.com/gogo/protobuf fcdc5011193ff531a548e9b0301828d5a5b97fd8

View File

@ -2,7 +2,7 @@
layers, container images, and containers. A `containers-storage` CLI wrapper layers, container images, and containers. A `containers-storage` CLI wrapper
is also included for manual and scripting use. is also included for manual and scripting use.
To build the CLI wrapper, use 'make build-binary'. To build the CLI wrapper, use 'make binary'.
Operations which use VMs expect to launch them using 'vagrant', defaulting to Operations which use VMs expect to launch them using 'vagrant', defaulting to
using its 'libvirt' provider. The boxes used are also available for the using its 'libvirt' provider. The boxes used are also available for the

View File

@ -133,6 +133,27 @@ func copyContainer(c *Container) *Container {
} }
} }
func (c *Container) MountLabel() string {
if label, ok := c.Flags["MountLabel"].(string); ok {
return label
}
return ""
}
func (c *Container) ProcessLabel() string {
if label, ok := c.Flags["ProcessLabel"].(string); ok {
return label
}
return ""
}
func (c *Container) MountOpts() []string {
if mountOpts, ok := c.Flags["MountOpts"].([]string); ok {
return mountOpts
}
return nil
}
func (r *containerStore) Containers() ([]Container, error) { func (r *containerStore) Containers() ([]Container, error) {
containers := make([]Container, len(r.containers)) containers := make([]Container, len(r.containers))
for i := range r.containers { for i := range r.containers {
@ -279,6 +300,9 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
if _, idInUse := r.byid[id]; idInUse { if _, idInUse := r.byid[id]; idInUse {
return nil, ErrDuplicateID return nil, ErrDuplicateID
} }
if options.MountOpts != nil {
options.Flags["MountOpts"] = append([]string{}, options.MountOpts...)
}
names = dedupeNames(names) names = dedupeNames(names)
for _, name := range names { for _, name := range names {
if _, nameInUse := r.byname[name]; nameInUse { if _, nameInUse := r.byname[name]; nameInUse {
@ -297,7 +321,7 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
BigDataSizes: make(map[string]int64), BigDataSizes: make(map[string]int64),
BigDataDigests: make(map[string]digest.Digest), BigDataDigests: make(map[string]digest.Digest),
Created: time.Now().UTC(), Created: time.Now().UTC(),
Flags: make(map[string]interface{}), Flags: copyStringInterfaceMap(options.Flags),
UIDMap: copyIDMap(options.UIDMap), UIDMap: copyIDMap(options.UIDMap),
GIDMap: copyIDMap(options.GIDMap), GIDMap: copyIDMap(options.GIDMap),
} }

View File

@ -1,6 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT. // Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: containers.go // source: containers.go
//
package storage package storage

View File

@ -405,7 +405,7 @@ func atomicRemove(source string) error {
case os.IsExist(err): case os.IsExist(err):
// Got error saying the target dir already exists, maybe the source doesn't exist due to a previous (failed) remove // Got error saying the target dir already exists, maybe the source doesn't exist due to a previous (failed) remove
if _, e := os.Stat(source); !os.IsNotExist(e) { if _, e := os.Stat(source); !os.IsNotExist(e) {
return errors.Wrapf(err, "target rename dir '%s' exists but should not, this needs to be manually cleaned up") return errors.Wrapf(err, "target rename dir '%s' exists but should not, this needs to be manually cleaned up", target)
} }
default: default:
return errors.Wrapf(err, "error preparing atomic delete") return errors.Wrapf(err, "error preparing atomic delete")
@ -416,7 +416,7 @@ func atomicRemove(source string) error {
// Get returns the rootfs path for the id. // Get returns the rootfs path for the id.
// This will mount the dir at its given path // This will mount the dir at its given path
func (a *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (a *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
a.locker.Lock(id) a.locker.Lock(id)
defer a.locker.Unlock(id) defer a.locker.Unlock(id)
parents, err := a.getParentLayerPaths(id) parents, err := a.getParentLayerPaths(id)
@ -441,7 +441,7 @@ func (a *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (s
// If a dir does not have a parent ( no layers )do not try to mount // If a dir does not have a parent ( no layers )do not try to mount
// just return the diff path to the data // just return the diff path to the data
if len(parents) > 0 { if len(parents) > 0 {
if err := a.mount(id, m, mountLabel, parents); err != nil { if err := a.mount(id, m, parents, options); err != nil {
return "", err return "", err
} }
} }
@ -585,7 +585,7 @@ func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
return layers, nil return layers, nil
} }
func (a *Driver) mount(id string, target string, mountLabel string, layers []string) error { func (a *Driver) mount(id string, target string, layers []string, options graphdriver.MountOpts) error {
a.Lock() a.Lock()
defer a.Unlock() defer a.Unlock()
@ -596,7 +596,7 @@ func (a *Driver) mount(id string, target string, mountLabel string, layers []str
rw := a.getDiffPath(id) rw := a.getDiffPath(id)
if err := a.aufsMount(layers, rw, target, mountLabel); err != nil { if err := a.aufsMount(layers, rw, target, options); err != nil {
return fmt.Errorf("error creating aufs mount to %s: %v", target, err) return fmt.Errorf("error creating aufs mount to %s: %v", target, err)
} }
return nil return nil
@ -643,7 +643,7 @@ func (a *Driver) Cleanup() error {
return mountpk.Unmount(a.root) return mountpk.Unmount(a.root)
} }
func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err error) { func (a *Driver) aufsMount(ro []string, rw, target string, options graphdriver.MountOpts) (err error) {
defer func() { defer func() {
if err != nil { if err != nil {
Unmount(target) Unmount(target)
@ -657,7 +657,7 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro
if useDirperm() { if useDirperm() {
offset += len(",dirperm1") offset += len(",dirperm1")
} }
b := make([]byte, unix.Getpagesize()-len(mountLabel)-offset) // room for xino & mountLabel b := make([]byte, unix.Getpagesize()-len(options.MountLabel)-offset) // room for xino & mountLabel
bp := copy(b, fmt.Sprintf("br:%s=rw", rw)) bp := copy(b, fmt.Sprintf("br:%s=rw", rw))
index := 0 index := 0
@ -670,21 +670,25 @@ func (a *Driver) aufsMount(ro []string, rw, target, mountLabel string) (err erro
} }
opts := "dio,xino=/dev/shm/aufs.xino" opts := "dio,xino=/dev/shm/aufs.xino"
if a.mountOptions != "" { mountOptions := a.mountOptions
opts += fmt.Sprintf(",%s", a.mountOptions) if len(options.Options) > 0 {
mountOptions = strings.Join(options.Options, ",")
}
if mountOptions != "" {
opts += fmt.Sprintf(",%s", mountOptions)
} }
if useDirperm() { if useDirperm() {
opts += ",dirperm1" opts += ",dirperm1"
} }
data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), mountLabel) data := label.FormatMountLabel(fmt.Sprintf("%s,%s", string(b[:bp]), opts), options.MountLabel)
if err = mount("none", target, "aufs", 0, data); err != nil { if err = mount("none", target, "aufs", 0, data); err != nil {
return return
} }
for ; index < len(ro); index++ { for ; index < len(ro); index++ {
layer := fmt.Sprintf(":%s=ro+wh", ro[index]) layer := fmt.Sprintf(":%s=ro+wh", ro[index])
data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), mountLabel) data := label.FormatMountLabel(fmt.Sprintf("append%s", layer), options.MountLabel)
if err = mount("none", target, "aufs", unix.MS_REMOUNT, data); err != nil { if err = mount("none", target, "aufs", unix.MS_REMOUNT, data); err != nil {
return return
} }

View File

@ -634,12 +634,15 @@ func (d *Driver) Remove(id string) error {
} }
// Get the requested filesystem id. // Get the requested filesystem id.
func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
dir := d.subvolumesDirID(id) dir := d.subvolumesDirID(id)
st, err := os.Stat(dir) st, err := os.Stat(dir)
if err != nil { if err != nil {
return "", err return "", err
} }
if len(options.Options) > 0 {
return "", fmt.Errorf("btrfs driver does not support mount options")
}
if !st.IsDir() { if !st.IsDir() {
return "", fmt.Errorf("%s: not a directory", dir) return "", fmt.Errorf("%s: not a directory", dir)

View File

@ -114,7 +114,10 @@ func NewNaiveLayerIDMapUpdater(driver ProtoDriver) LayerIDMapUpdater {
// same "container" IDs. // same "container" IDs.
func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error { func (n *naiveLayerIDMapUpdater) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMappings, mountLabel string) error {
driver := n.ProtoDriver driver := n.ProtoDriver
layerFs, err := driver.Get(id, mountLabel, nil, nil) options := MountOpts{
MountLabel: mountLabel,
}
layerFs, err := driver.Get(id, options)
if err != nil { if err != nil {
return err return err
} }

View File

@ -8,6 +8,7 @@ import (
"syscall" "syscall"
"github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/system"
) )
func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error { func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.IDMappings) error {
@ -45,10 +46,31 @@ func platformLChown(path string, info os.FileInfo, toHost, toContainer *idtools.
uid, gid = mappedPair.UID, mappedPair.GID uid, gid = mappedPair.UID, mappedPair.GID
} }
if uid != int(st.Uid) || gid != int(st.Gid) { if uid != int(st.Uid) || gid != int(st.Gid) {
stat, err := os.Lstat(path)
if err != nil {
return fmt.Errorf("%s: lstat(%q): %v", os.Args[0], path, err)
}
cap, err := system.Lgetxattr(path, "security.capability")
if err != nil && err != system.ErrNotSupportedPlatform {
return fmt.Errorf("%s: Lgetxattr(%q): %v", os.Args[0], path, err)
}
// Make the change. // Make the change.
if err := syscall.Lchown(path, uid, gid); err != nil { if err := syscall.Lchown(path, uid, gid); err != nil {
return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err) return fmt.Errorf("%s: chown(%q): %v", os.Args[0], path, err)
} }
// Restore the SUID and SGID bits if they were originally set.
if (stat.Mode()&os.ModeSymlink == 0) && stat.Mode()&(os.ModeSetuid|os.ModeSetgid) != 0 {
if err := os.Chmod(path, stat.Mode()); err != nil {
return fmt.Errorf("%s: chmod(%q): %v", os.Args[0], path, err)
}
}
if cap != nil {
if err := system.Lsetxattr(path, "security.capability", cap, 0); err != nil {
return fmt.Errorf("%s: Lsetxattr(%q): %v", os.Args[0], path, err)
}
}
} }
} }
return nil return nil

View File

@ -2364,7 +2364,7 @@ func (devices *DeviceSet) xfsSetNospaceRetries(info *devInfo) error {
} }
// MountDevice mounts the device if not already mounted. // MountDevice mounts the device if not already mounted.
func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error { func (devices *DeviceSet) MountDevice(hash, path string, moptions graphdriver.MountOpts) error {
info, err := devices.lookupDeviceWithLock(hash) info, err := devices.lookupDeviceWithLock(hash)
if err != nil { if err != nil {
return err return err
@ -2396,8 +2396,17 @@ func (devices *DeviceSet) MountDevice(hash, path, mountLabel string) error {
options = joinMountOptions(options, "nouuid") options = joinMountOptions(options, "nouuid")
} }
options = joinMountOptions(options, devices.mountOptions) mountOptions := devices.mountOptions
options = joinMountOptions(options, label.FormatMountLabel("", mountLabel)) if len(moptions.Options) > 0 {
addNouuid := strings.Contains("nouuid", mountOptions)
mountOptions = strings.Join(moptions.Options, ",")
if addNouuid {
mountOptions = fmt.Sprintf("nouuid,", mountOptions)
}
}
options = joinMountOptions(options, mountOptions)
options = joinMountOptions(options, label.FormatMountLabel("", moptions.MountLabel))
if err := mount.Mount(info.DevName(), path, fstype, options); err != nil { if err := mount.Mount(info.DevName(), path, fstype, options); err != nil {
return fmt.Errorf("devmapper: Error mounting '%s' on '%s': %s\n%v", info.DevName(), path, err, string(dmesg.Dmesg(256))) return fmt.Errorf("devmapper: Error mounting '%s' on '%s': %s\n%v", info.DevName(), path, err, string(dmesg.Dmesg(256)))

View File

@ -9,8 +9,6 @@ import (
"path" "path"
"strconv" "strconv"
"github.com/sirupsen/logrus"
"github.com/containers/storage/drivers" "github.com/containers/storage/drivers"
"github.com/containers/storage/pkg/devicemapper" "github.com/containers/storage/pkg/devicemapper"
"github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/idtools"
@ -18,6 +16,7 @@ import (
"github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
units "github.com/docker/go-units" units "github.com/docker/go-units"
"github.com/sirupsen/logrus"
) )
func init() { func init() {
@ -163,7 +162,7 @@ func (d *Driver) Remove(id string) error {
} }
// Get mounts a device with given id into the root filesystem // Get mounts a device with given id into the root filesystem
func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
d.locker.Lock(id) d.locker.Lock(id)
defer d.locker.Unlock(id) defer d.locker.Unlock(id)
mp := path.Join(d.home, "mnt", id) mp := path.Join(d.home, "mnt", id)
@ -189,7 +188,7 @@ func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (s
} }
// Mount the device // Mount the device
if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil { if err := d.DeviceSet.MountDevice(id, mp, options); err != nil {
d.ctr.Decrement(mp) d.ctr.Decrement(mp)
return "", err return "", err
} }

View File

@ -42,6 +42,16 @@ type CreateOpts struct {
StorageOpt map[string]string StorageOpt map[string]string
} }
// MountOpts contains optional arguments for LayerStope.Mount() methods.
type MountOpts struct {
// Mount label is the MAC Labels to assign to mount point (SELINUX)
MountLabel string
// UidMaps & GidMaps are the User Namespace mappings to be assigned to content in the mount point
UidMaps []idtools.IDMap
GidMaps []idtools.IDMap
Options []string
}
// InitFunc initializes the storage driver. // InitFunc initializes the storage driver.
type InitFunc func(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) type InitFunc func(root string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error)
@ -68,7 +78,7 @@ type ProtoDriver interface {
// to by this id. You can optionally specify a mountLabel or "". // to by this id. You can optionally specify a mountLabel or "".
// Optionally it gets the mappings used to create the layer. // Optionally it gets the mappings used to create the layer.
// Returns the absolute path to the mounted layered filesystem. // Returns the absolute path to the mounted layered filesystem.
Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (dir string, err error) Get(id string, options MountOpts) (dir string, err error)
// Put releases the system resources for the specified id, // Put releases the system resources for the specified id,
// e.g, unmounting layered filesystem. // e.g, unmounting layered filesystem.
Put(id string) error Put(id string) error

View File

@ -51,7 +51,10 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
parentMappings = &idtools.IDMappings{} parentMappings = &idtools.IDMappings{}
} }
layerFs, err := driver.Get(id, mountLabel, nil, nil) options := MountOpts{
MountLabel: mountLabel,
}
layerFs, err := driver.Get(id, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -78,7 +81,7 @@ func (gdw *NaiveDiffDriver) Diff(id string, idMappings *idtools.IDMappings, pare
}), nil }), nil
} }
parentFs, err := driver.Get(parent, mountLabel, nil, nil) parentFs, err := driver.Get(parent, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -119,7 +122,10 @@ func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, p
parentMappings = &idtools.IDMappings{} parentMappings = &idtools.IDMappings{}
} }
layerFs, err := driver.Get(id, mountLabel, nil, nil) options := MountOpts{
MountLabel: mountLabel,
}
layerFs, err := driver.Get(id, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,7 +134,10 @@ func (gdw *NaiveDiffDriver) Changes(id string, idMappings *idtools.IDMappings, p
parentFs := "" parentFs := ""
if parent != "" { if parent != "" {
parentFs, err = driver.Get(parent, mountLabel, nil, nil) options := MountOpts{
MountLabel: mountLabel,
}
parentFs, err = driver.Get(parent, options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,7 +158,10 @@ func (gdw *NaiveDiffDriver) ApplyDiff(id string, applyMappings *idtools.IDMappin
} }
// Mount the root filesystem so we can apply the diff/layer. // Mount the root filesystem so we can apply the diff/layer.
layerFs, err := driver.Get(id, mountLabel, nil, nil) mountOpts := MountOpts{
MountLabel: mountLabel,
}
layerFs, err := driver.Get(id, mountOpts)
if err != nil { if err != nil {
return return
} }
@ -189,7 +201,10 @@ func (gdw *NaiveDiffDriver) DiffSize(id string, idMappings *idtools.IDMappings,
return return
} }
layerFs, err := driver.Get(id, mountLabel, nil, nil) options := MountOpts{
MountLabel: mountLabel,
}
layerFs, err := driver.Get(id, options)
if err != nil { if err != nil {
return return
} }

View File

@ -20,7 +20,7 @@ import (
// which copies up the opaque flag when copying up an opaque // which copies up the opaque flag when copying up an opaque
// directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR. // directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR.
// When these exist naive diff should be used. // When these exist naive diff should be used.
func doesSupportNativeDiff(d string) error { func doesSupportNativeDiff(d, mountOpts string) error {
td, err := ioutil.TempDir(d, "opaque-bug-check") td, err := ioutil.TempDir(d, "opaque-bug-check")
if err != nil { if err != nil {
return err return err
@ -57,6 +57,9 @@ func doesSupportNativeDiff(d string) error {
} }
opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work")) opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work"))
if mountOpts != "" {
opts = fmt.Sprintf("%s,%s", opts, mountOpts)
}
if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil { if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
return errors.Wrap(err, "failed to mount overlay") return errors.Wrap(err, "failed to mount overlay")
} }

View File

@ -138,10 +138,12 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
} }
// check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs // check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs
switch fsMagic { if opts.mountProgram == "" {
case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs: switch fsMagic {
logrus.Errorf("'overlay' is not supported over %s", backingFs) case graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs:
return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s", backingFs) logrus.Errorf("'overlay' is not supported over %s", backingFs)
return nil, errors.Wrapf(graphdriver.ErrIncompatibleFS, "'overlay' is not supported over %s", backingFs)
}
} }
rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps) rootUID, rootGID, err := idtools.GetRootUIDGID(uidMaps, gidMaps)
@ -204,7 +206,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs) return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs)
} }
logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v", backingFs, projectQuotaSupported, !useNaiveDiff(home)) logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v", backingFs, projectQuotaSupported, !d.useNaiveDiff())
return d, nil return d, nil
} }
@ -336,9 +338,13 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.") return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
} }
func useNaiveDiff(home string) bool { func (d *Driver) useNaiveDiff() bool {
useNaiveDiffLock.Do(func() { useNaiveDiffLock.Do(func() {
if err := doesSupportNativeDiff(home); err != nil { if d.options.mountProgram != "" {
useNaiveDiffOnly = true
return
}
if err := doesSupportNativeDiff(d.home, d.options.mountOptions); err != nil {
logrus.Warnf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err) logrus.Warnf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err)
useNaiveDiffOnly = true useNaiveDiffOnly = true
} }
@ -356,7 +362,7 @@ func (d *Driver) Status() [][2]string {
return [][2]string{ return [][2]string{
{"Backing Filesystem", backingFs}, {"Backing Filesystem", backingFs},
{"Supports d_type", strconv.FormatBool(d.supportsDType)}, {"Supports d_type", strconv.FormatBool(d.supportsDType)},
{"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))}, {"Native Overlay Diff", strconv.FormatBool(!d.useNaiveDiff())},
} }
} }
@ -642,11 +648,11 @@ func (d *Driver) Remove(id string) error {
} }
// Get creates and mounts the required file system for the given id and returns the mount path. // Get creates and mounts the required file system for the given id and returns the mount path.
func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (_ string, retErr error) { func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) {
return d.get(id, mountLabel, false, uidMaps, gidMaps) return d.get(id, false, options)
} }
func (d *Driver) get(id, mountLabel string, disableShifting bool, uidMaps, gidMaps []idtools.IDMap) (_ string, retErr error) { func (d *Driver) get(id string, disableShifting bool, options graphdriver.MountOpts) (_ string, retErr error) {
d.locker.Lock(id) d.locker.Lock(id)
defer d.locker.Unlock(id) defer d.locker.Unlock(id)
dir := d.dir(id) dir := d.dir(id)
@ -737,10 +743,12 @@ func (d *Driver) get(id, mountLabel string, disableShifting bool, uidMaps, gidMa
workDir := path.Join(dir, "work") workDir := path.Join(dir, "work")
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workDir) opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workDir)
if d.options.mountOptions != "" { if len(options.Options) > 0 {
opts = fmt.Sprintf("%s,%s", strings.Join(options.Options, ","), opts)
} else if d.options.mountOptions != "" {
opts = fmt.Sprintf("%s,%s", d.options.mountOptions, opts) opts = fmt.Sprintf("%s,%s", d.options.mountOptions, opts)
} }
mountData := label.FormatMountLabel(opts, mountLabel) mountData := label.FormatMountLabel(opts, options.MountLabel)
mountFunc := unix.Mount mountFunc := unix.Mount
mountTarget := mergedDir mountTarget := mergedDir
@ -753,7 +761,7 @@ func (d *Driver) get(id, mountLabel string, disableShifting bool, uidMaps, gidMa
if d.options.mountProgram != "" { if d.options.mountProgram != "" {
mountFunc = func(source string, target string, mType string, flags uintptr, label string) error { mountFunc = func(source string, target string, mType string, flags uintptr, label string) error {
if !disableShifting { if !disableShifting {
label = d.optsAppendMappings(label, uidMaps, gidMaps) label = d.optsAppendMappings(label, options.UidMaps, options.GidMaps)
} }
mountProgram := exec.Command(d.options.mountProgram, "-o", label, target) mountProgram := exec.Command(d.options.mountProgram, "-o", label, target)
@ -763,7 +771,7 @@ func (d *Driver) get(id, mountLabel string, disableShifting bool, uidMaps, gidMa
} else if len(mountData) > pageSize { } else if len(mountData) > pageSize {
//FIXME: We need to figure out to get this to work with additional stores //FIXME: We need to figure out to get this to work with additional stores
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), path.Join(id, "diff"), path.Join(id, "work")) opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), path.Join(id, "diff"), path.Join(id, "work"))
mountData = label.FormatMountLabel(opts, mountLabel) mountData = label.FormatMountLabel(opts, options.MountLabel)
if len(mountData) > pageSize { if len(mountData) > pageSize {
return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData)) return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
} }
@ -839,6 +847,17 @@ func (d *Driver) isParent(id, parent string) bool {
return ld == parentDir return ld == parentDir
} }
func (d *Driver) getWhiteoutFormat() archive.WhiteoutFormat {
whiteoutFormat := archive.OverlayWhiteoutFormat
if d.options.mountProgram != "" {
// If we are using a mount program, we are most likely running
// as an unprivileged user that cannot use mknod, so fallback to the
// AUFS whiteout format.
whiteoutFormat = archive.AUFSWhiteoutFormat
}
return whiteoutFormat
}
// ApplyDiff applies the new layer into a root // ApplyDiff applies the new layer into a root
func (d *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent string, mountLabel string, diff io.Reader) (size int64, err error) { func (d *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent string, mountLabel string, diff io.Reader) (size int64, err error) {
if !d.isParent(id, parent) { if !d.isParent(id, parent) {
@ -856,7 +875,7 @@ func (d *Driver) ApplyDiff(id string, idMappings *idtools.IDMappings, parent str
if err := untar(diff, applyDir, &archive.TarOptions{ if err := untar(diff, applyDir, &archive.TarOptions{
UIDMaps: idMappings.UIDs(), UIDMaps: idMappings.UIDs(),
GIDMaps: idMappings.GIDs(), GIDMaps: idMappings.GIDs(),
WhiteoutFormat: archive.OverlayWhiteoutFormat, WhiteoutFormat: d.getWhiteoutFormat(),
}); err != nil { }); err != nil {
return 0, err return 0, err
} }
@ -881,7 +900,7 @@ func (d *Driver) getDiffPath(id string) string {
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) { func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) { if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel) return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel)
} }
return directory.Size(d.getDiffPath(id)) return directory.Size(d.getDiffPath(id))
@ -890,7 +909,7 @@ func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent stri
// Diff produces an archive of the changes between the specified // Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) { func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) { if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.Diff(id, idMappings, parent, parentMappings, mountLabel) return d.naiveDiff.Diff(id, idMappings, parent, parentMappings, mountLabel)
} }
@ -909,7 +928,7 @@ func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string,
Compression: archive.Uncompressed, Compression: archive.Uncompressed,
UIDMaps: idMappings.UIDs(), UIDMaps: idMappings.UIDs(),
GIDMaps: idMappings.GIDs(), GIDMaps: idMappings.GIDs(),
WhiteoutFormat: archive.OverlayWhiteoutFormat, WhiteoutFormat: d.getWhiteoutFormat(),
WhiteoutData: lowerDirs, WhiteoutData: lowerDirs,
}) })
} }
@ -917,7 +936,7 @@ func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string,
// Changes produces a list of changes between the specified layer // Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) { func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
if useNaiveDiff(d.home) || !d.isParent(id, parent) { if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.Changes(id, idMappings, parent, parentMappings, mountLabel) return d.naiveDiff.Changes(id, idMappings, parent, parentMappings, mountLabel)
} }
// Overlay doesn't have snapshots, so we need to get changes from all parent // Overlay doesn't have snapshots, so we need to get changes from all parent
@ -952,7 +971,10 @@ func (d *Driver) UpdateLayerIDMap(id string, toContainer, toHost *idtools.IDMapp
} }
// Mount the new layer and handle ownership changes and possible copy_ups in it. // Mount the new layer and handle ownership changes and possible copy_ups in it.
layerFs, err := d.get(id, mountLabel, true, nil, nil) options := graphdriver.MountOpts{
MountLabel: mountLabel,
}
layerFs, err := d.get(id, true, options)
if err != nil { if err != nil {
return err return err
} }

View File

@ -137,7 +137,7 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, ro bool
label.SetFileLabel(dir, mountLabel) label.SetFileLabel(dir, mountLabel)
} }
if parent != "" { if parent != "" {
parentDir, err := d.Get(parent, "", nil, nil) parentDir, err := d.Get(parent, graphdriver.MountOpts{})
if err != nil { if err != nil {
return fmt.Errorf("%s: %s", parent, err) return fmt.Errorf("%s: %s", parent, err)
} }
@ -179,8 +179,11 @@ func (d *Driver) Remove(id string) error {
} }
// Get returns the directory for the given id. // Get returns the directory for the given id.
func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr error) {
dir := d.dir(id) dir := d.dir(id)
if len(options.Options) > 0 {
return "", fmt.Errorf("vfs driver does not support mount options")
}
if st, err := os.Stat(dir); err != nil { if st, err := os.Stat(dir); err != nil {
return "", err return "", err
} else if !st.IsDir() { } else if !st.IsDir() {

View File

@ -362,11 +362,14 @@ func (d *Driver) Remove(id string) error {
} }
// Get returns the rootfs path for the id. This will mount the dir at its given path. // Get returns the rootfs path for the id. This will mount the dir at its given path.
func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
panicIfUsedByLcow() panicIfUsedByLcow()
logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, options.MountLabel)
var dir string var dir string
if len(options.Options) > 0 {
return "", fmt.Errorf("windows driver does not support mount options")
}
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
return "", err return "", err
@ -620,7 +623,7 @@ func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent stri
return return
} }
layerFs, err := d.Get(id, "", nil, nil) layerFs, err := d.Get(id, graphdriver.MountOpts{})
if err != nil { if err != nil {
return return
} }

View File

@ -52,7 +52,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
return nil, errors.Wrap(graphdriver.ErrPrerequisites, "the 'zfs' command is not available") return nil, errors.Wrap(graphdriver.ErrPrerequisites, "the 'zfs' command is not available")
} }
file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 600) file, err := os.OpenFile("/dev/zfs", os.O_RDWR, 0600)
if err != nil { if err != nil {
logrus.Debugf("[zfs] cannot open /dev/zfs: %v", err) logrus.Debugf("[zfs] cannot open /dev/zfs: %v", err)
return nil, errors.Wrapf(graphdriver.ErrPrerequisites, "could not open /dev/zfs: %v", err) return nil, errors.Wrapf(graphdriver.ErrPrerequisites, "could not open /dev/zfs: %v", err)
@ -360,15 +360,20 @@ func (d *Driver) Remove(id string) error {
} }
// Get returns the mountpoint for the given id after creating the target directories if necessary. // Get returns the mountpoint for the given id after creating the target directories if necessary.
func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (d *Driver) Get(id string, options graphdriver.MountOpts) (string, error) {
mountpoint := d.mountPath(id) mountpoint := d.mountPath(id)
if count := d.ctr.Increment(mountpoint); count > 1 { if count := d.ctr.Increment(mountpoint); count > 1 {
return mountpoint, nil return mountpoint, nil
} }
mountOptions := d.options.mountOptions
if len(options.Options) > 0 {
mountOptions = strings.Join(options.Options, ",")
}
filesystem := d.zfsPath(id) filesystem := d.zfsPath(id)
options := label.FormatMountLabel(d.options.mountOptions, mountLabel) opts := label.FormatMountLabel(mountOptions, options.MountLabel)
logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, options) logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, opts)
rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps) rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
if err != nil { if err != nil {
@ -381,7 +386,7 @@ func (d *Driver) Get(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (s
return "", err return "", err
} }
if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil { if err := mount.Mount(filesystem, mountpoint, "zfs", opts); err != nil {
d.ctr.Decrement(mountpoint) d.ctr.Decrement(mountpoint)
return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err) return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
"github.com/containers/storage/pkg/truncindex" "github.com/containers/storage/pkg/truncindex"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/vbatts/tar-split/tar/asm" "github.com/vbatts/tar-split/tar/asm"
"github.com/vbatts/tar-split/tar/storage" "github.com/vbatts/tar-split/tar/storage"
@ -210,7 +211,7 @@ type LayerStore interface {
// layers, it should not be written to. An SELinux label to be applied to the // layers, it should not be written to. An SELinux label to be applied to the
// mount can be specified to override the one configured for the layer. // mount can be specified to override the one configured for the layer.
// The mappings used by the container can be specified. // The mappings used by the container can be specified.
Mount(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) Mount(id string, options drivers.MountOpts) (string, error)
// Unmount unmounts a layer when it is no longer in use. // Unmount unmounts a layer when it is no longer in use.
Unmount(id string, force bool) (bool, error) Unmount(id string, force bool) (bool, error)
@ -294,6 +295,9 @@ func (r *layerStore) Load() error {
mounts := make(map[string]*Layer) mounts := make(map[string]*Layer)
compressedsums := make(map[digest.Digest][]string) compressedsums := make(map[digest.Digest][]string)
uncompressedsums := make(map[digest.Digest][]string) uncompressedsums := make(map[digest.Digest][]string)
if r.lockfile.IsReadWrite() {
label.ClearLabels()
}
if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil { if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil {
idlist = make([]string, 0, len(layers)) idlist = make([]string, 0, len(layers))
for n, layer := range layers { for n, layer := range layers {
@ -312,6 +316,9 @@ func (r *layerStore) Load() error {
if layer.UncompressedDigest != "" { if layer.UncompressedDigest != "" {
uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID) uncompressedsums[layer.UncompressedDigest] = append(uncompressedsums[layer.UncompressedDigest], layer.ID)
} }
if layer.MountLabel != "" {
label.ReserveLabel(layer.MountLabel)
}
} }
} }
if shouldSave && !r.IsReadWrite() { if shouldSave && !r.IsReadWrite() {
@ -535,8 +542,8 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
_, idInUse = r.byid[id] _, idInUse = r.byid[id]
} }
} }
if _, idInUse := r.byid[id]; idInUse { if duplicateLayer, idInUse := r.byid[id]; idInUse {
return nil, -1, ErrDuplicateID return duplicateLayer, -1, ErrDuplicateID
} }
names = dedupeNames(names) names = dedupeNames(names)
for _, name := range names { for _, name := range names {
@ -552,19 +559,31 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
} else { } else {
parentMappings = &idtools.IDMappings{} parentMappings = &idtools.IDMappings{}
} }
if mountLabel != "" {
label.ReserveLabel(mountLabel)
}
idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap) idMappings := idtools.NewIDMappingsFromMaps(moreOptions.UIDMap, moreOptions.GIDMap)
opts := drivers.CreateOpts{ opts := drivers.CreateOpts{
MountLabel: mountLabel, MountLabel: mountLabel,
StorageOpt: options, StorageOpt: options,
} }
if writeable { if writeable {
err = r.driver.CreateReadWrite(id, parent, &opts) if err = r.driver.CreateReadWrite(id, parent, &opts); err != nil {
if id != "" {
return nil, -1, errors.Wrapf(err, "error creating read-write layer with ID %q", id)
}
return nil, -1, errors.Wrapf(err, "error creating read-write layer")
}
} else { } else {
err = r.driver.Create(id, parent, &opts) if err = r.driver.Create(id, parent, &opts); err != nil {
if id != "" {
return nil, -1, errors.Wrapf(err, "error creating layer with ID %q", id)
}
return nil, -1, errors.Wrapf(err, "error creating layer")
}
} }
if !reflect.DeepEqual(parentMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(parentMappings.GIDs(), idMappings.GIDs()) { if !reflect.DeepEqual(parentMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(parentMappings.GIDs(), idMappings.GIDs()) {
err = r.driver.UpdateLayerIDMap(id, parentMappings, idMappings, mountLabel) if err = r.driver.UpdateLayerIDMap(id, parentMappings, idMappings, mountLabel); err != nil {
if err != nil {
// We don't have a record of this layer, but at least // We don't have a record of this layer, but at least
// try to clean it up underneath us. // try to clean it up underneath us.
r.driver.Remove(id) r.driver.Remove(id)
@ -640,7 +659,7 @@ func (r *layerStore) Mounted(id string) (int, error) {
return layer.MountCount, nil return layer.MountCount, nil
} }
func (r *layerStore) Mount(id, mountLabel string, uidMaps, gidMaps []idtools.IDMap) (string, error) { func (r *layerStore) Mount(id string, options drivers.MountOpts) (string, error) {
if !r.IsReadWrite() { if !r.IsReadWrite() {
return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath()) return "", errors.Wrapf(ErrStoreIsReadOnly, "not allowed to update mount locations for layers at %q", r.mountspath())
} }
@ -652,16 +671,16 @@ func (r *layerStore) Mount(id, mountLabel string, uidMaps, gidMaps []idtools.IDM
layer.MountCount++ layer.MountCount++
return layer.MountPoint, r.Save() return layer.MountPoint, r.Save()
} }
if mountLabel == "" { if options.MountLabel == "" {
mountLabel = layer.MountLabel options.MountLabel = layer.MountLabel
} }
if (uidMaps != nil || gidMaps != nil) && !r.driver.SupportsShifting() { if (options.UidMaps != nil || options.GidMaps != nil) && !r.driver.SupportsShifting() {
if !reflect.DeepEqual(uidMaps, layer.UIDMap) || !reflect.DeepEqual(gidMaps, layer.GIDMap) { if !reflect.DeepEqual(options.UidMaps, layer.UIDMap) || !reflect.DeepEqual(options.GidMaps, layer.GIDMap) {
return "", fmt.Errorf("cannot mount layer %v: shifting not enabled", layer.ID) return "", fmt.Errorf("cannot mount layer %v: shifting not enabled", layer.ID)
} }
} }
mountpoint, err := r.driver.Get(id, mountLabel, uidMaps, gidMaps) mountpoint, err := r.driver.Get(id, options)
if mountpoint != "" && err == nil { if mountpoint != "" && err == nil {
if layer.MountPoint != "" { if layer.MountPoint != "" {
delete(r.bymount, layer.MountPoint) delete(r.bymount, layer.MountPoint)
@ -822,14 +841,19 @@ func (r *layerStore) Delete(id string) error {
return ErrLayerUnknown return ErrLayerUnknown
} }
id = layer.ID id = layer.ID
if _, err := r.Unmount(id, true); err != nil { // This check is needed for idempotency of delete where the layer could have been
return err // already unmounted (since c/storage gives you that API directly)
for layer.MountCount > 0 {
if _, err := r.Unmount(id, false); err != nil {
return err
}
} }
err := r.driver.Remove(id) err := r.driver.Remove(id)
if err == nil { if err == nil {
os.Remove(r.tspath(id)) os.Remove(r.tspath(id))
delete(r.byid, id) delete(r.byid, id)
r.idindex.Delete(id) r.idindex.Delete(id)
mountLabel := layer.MountLabel
if layer.MountPoint != "" { if layer.MountPoint != "" {
delete(r.bymount, layer.MountPoint) delete(r.bymount, layer.MountPoint)
} }
@ -848,6 +872,18 @@ func (r *layerStore) Delete(id string) error {
r.layers = append(r.layers[:toDeleteIndex], r.layers[toDeleteIndex+1:]...) r.layers = append(r.layers[:toDeleteIndex], r.layers[toDeleteIndex+1:]...)
} }
} }
if mountLabel != "" {
var found bool
for _, candidate := range r.layers {
if candidate.MountLabel == mountLabel {
found = true
break
}
}
if !found {
label.ReleaseLabel(mountLabel)
}
}
if err = r.Save(); err != nil { if err = r.Save(); err != nil {
return err return err
} }
@ -948,7 +984,7 @@ func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
if getter, ok := r.driver.(drivers.DiffGetterDriver); ok { if getter, ok := r.driver.(drivers.DiffGetterDriver); ok {
return getter.DiffGetter(id) return getter.DiffGetter(id)
} }
path, err := r.Mount(id, "", nil, nil) path, err := r.Mount(id, drivers.MountOpts{})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,97 +0,0 @@
// +build ignore
// Simple tool to create an archive stream from an old and new directory
//
// By default it will stream the comparison of two temporary directories with junk files
package main
import (
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus"
)
var (
flDebug = flag.Bool("D", false, "debugging output")
flNewDir = flag.String("newdir", "", "")
flOldDir = flag.String("olddir", "", "")
log = logrus.New()
)
func main() {
flag.Usage = func() {
fmt.Println("Produce a tar from comparing two directory paths. By default a demo tar is created of around 200 files (including hardlinks)")
fmt.Printf("%s [OPTIONS]\n", os.Args[0])
flag.PrintDefaults()
}
flag.Parse()
log.Out = os.Stderr
if (len(os.Getenv("DEBUG")) > 0) || *flDebug {
logrus.SetLevel(logrus.DebugLevel)
}
var newDir, oldDir string
if len(*flNewDir) == 0 {
var err error
newDir, err = ioutil.TempDir("", "storage-test-newDir")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(newDir)
if _, err := prepareUntarSourceDirectory(100, newDir, true); err != nil {
log.Fatal(err)
}
} else {
newDir = *flNewDir
}
if len(*flOldDir) == 0 {
oldDir, err := ioutil.TempDir("", "storage-test-oldDir")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(oldDir)
} else {
oldDir = *flOldDir
}
changes, err := archive.ChangesDirs(newDir, oldDir)
if err != nil {
log.Fatal(err)
}
a, err := archive.ExportChanges(newDir, changes)
if err != nil {
log.Fatal(err)
}
defer a.Close()
i, err := io.Copy(os.Stdout, a)
if err != nil && err != io.EOF {
log.Fatal(err)
}
fmt.Fprintf(os.Stderr, "wrote archive of %d bytes", i)
}
func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) {
fileData := []byte("fooo")
for n := 0; n < numberOfFiles; n++ {
fileName := fmt.Sprintf("file-%d", n)
if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
return 0, err
}
if makeLinks {
if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil {
return 0, err
}
}
}
totalSize := numberOfFiles * len(fileData)
return totalSize, nil
}

View File

@ -0,0 +1,56 @@
package idtools
import (
"fmt"
"strconv"
"strings"
)
func nonDigitsToWhitespace(r rune) rune {
if !strings.ContainsRune("0123456789", r) {
return ' '
}
return r
}
func parseTriple(spec []string) (container, host, size uint32, err error) {
cid, err := strconv.ParseUint(spec[0], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[0], err)
}
hid, err := strconv.ParseUint(spec[1], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[1], err)
}
sz, err := strconv.ParseUint(spec[2], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[2], err)
}
return uint32(cid), uint32(hid), uint32(sz), nil
}
// ParseIDMap parses idmap triples from string.
func ParseIDMap(mapSpec []string, mapSetting string) (idmap []IDMap, err error) {
for _, idMapSpec := range mapSpec {
idSpec := strings.Fields(strings.Map(nonDigitsToWhitespace, idMapSpec))
if len(idSpec)%3 != 0 {
return nil, fmt.Errorf("error initializing ID mappings: %s setting is malformed", mapSetting)
}
for i := range idSpec {
if i%3 != 0 {
continue
}
cid, hid, size, err := parseTriple(idSpec[i : i+3])
if err != nil {
return nil, fmt.Errorf("error initializing ID mappings: %s setting is malformed", mapSetting)
}
mapping := IDMap{
ContainerID: int(cid),
HostID: int(hid),
Size: int(size),
}
idmap = append(idmap, mapping)
}
}
return idmap, nil
}

View File

@ -8,7 +8,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -22,9 +21,11 @@ import (
"github.com/containers/storage/pkg/directory" "github.com/containers/storage/pkg/directory"
"github.com/containers/storage/pkg/idtools" "github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/ioutils" "github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/parsers"
"github.com/containers/storage/pkg/stringid" "github.com/containers/storage/pkg/stringid"
"github.com/containers/storage/pkg/stringutils" "github.com/containers/storage/pkg/stringutils"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -251,6 +252,8 @@ type Store interface {
// Mount attempts to mount a layer, image, or container for access, and // Mount attempts to mount a layer, image, or container for access, and
// returns the pathname if it succeeds. // returns the pathname if it succeeds.
// Note if the mountLabel == "", the default label for the container
// will be used.
// //
// Note that we do some of this work in a child process. The calling // Note that we do some of this work in a child process. The calling
// process's main() function needs to import our pkg/reexec package and // process's main() function needs to import our pkg/reexec package and
@ -497,6 +500,9 @@ type ContainerOptions struct {
// container's layer will inherit settings from the image's top layer // container's layer will inherit settings from the image's top layer
// or, if it is not being created based on an image, the Store object. // or, if it is not being created based on an image, the Store object.
IDMappingOptions IDMappingOptions
LabelOpts []string
Flags map[string]interface{}
MountOpts []string
} }
type store struct { type store struct {
@ -1064,7 +1070,7 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, read
} }
mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, rc) mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, rc)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error creating ID-mapped copy of layer %q") return nil, errors.Wrapf(err, "error creating ID-mapped copy of layer %q", parentLayer.ID)
} }
if err = istore.addMappedTopLayer(image.ID, mappedLayer.ID); err != nil { if err = istore.addMappedTopLayer(image.ID, mappedLayer.ID); err != nil {
if err2 := rlstore.Delete(mappedLayer.ID); err2 != nil { if err2 := rlstore.Delete(mappedLayer.ID); err2 != nil {
@ -1175,7 +1181,26 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
}, },
} }
} }
clayer, err := rlstore.Create(layer, imageTopLayer, nil, "", nil, layerOptions, true) if options.Flags == nil {
options.Flags = make(map[string]interface{})
}
plabel, _ := options.Flags["ProcessLabel"].(string)
mlabel, _ := options.Flags["MountLabel"].(string)
if (plabel == "" && mlabel != "") ||
(plabel != "" && mlabel == "") {
return nil, errors.Errorf("ProcessLabel and Mountlabel must either not be specified or both specified")
}
if plabel == "" {
processLabel, mountLabel, err := label.InitLabels(options.LabelOpts)
if err != nil {
return nil, err
}
options.Flags["ProcessLabel"] = processLabel
options.Flags["MountLabel"] = mountLabel
}
clayer, err := rlstore.Create(layer, imageTopLayer, nil, options.Flags["MountLabel"].(string), nil, layerOptions, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1189,13 +1214,11 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
if modified, err := rcstore.Modified(); modified || err != nil { if modified, err := rcstore.Modified(); modified || err != nil {
rcstore.Load() rcstore.Load()
} }
options = &ContainerOptions{ options.IDMappingOptions = IDMappingOptions{
IDMappingOptions: IDMappingOptions{ HostUIDMapping: len(options.UIDMap) == 0,
HostUIDMapping: len(options.UIDMap) == 0, HostGIDMapping: len(options.GIDMap) == 0,
HostGIDMapping: len(options.GIDMap) == 0, UIDMap: copyIDMap(options.UIDMap),
UIDMap: copyIDMap(options.UIDMap), GIDMap: copyIDMap(options.GIDMap),
GIDMap: copyIDMap(options.GIDMap),
},
} }
container, err := rcstore.Create(id, names, imageID, layer, metadata, options) container, err := rcstore.Create(id, names, imageID, layer, metadata, options)
if err != nil || container == nil { if err != nil || container == nil {
@ -2123,21 +2146,20 @@ func (s *store) DeleteContainer(id string) error {
if err = rlstore.Delete(container.LayerID); err != nil { if err = rlstore.Delete(container.LayerID); err != nil {
return err return err
} }
if err = rcstore.Delete(id); err != nil {
return err
}
middleDir := s.graphDriverName + "-containers"
gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID)
if err = os.RemoveAll(gcpath); err != nil {
return err
}
rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID)
if err = os.RemoveAll(rcpath); err != nil {
return err
}
return nil
} }
return ErrNotALayer if err = rcstore.Delete(id); err != nil {
return err
}
middleDir := s.graphDriverName + "-containers"
gcpath := filepath.Join(s.GraphRoot(), middleDir, container.ID)
if err = os.RemoveAll(gcpath); err != nil {
return err
}
rcpath := filepath.Join(s.RunRoot(), middleDir, container.ID)
if err = os.RemoveAll(rcpath); err != nil {
return err
}
return nil
} }
} }
return ErrNotAContainer return ErrNotAContainer
@ -2258,10 +2280,14 @@ func (s *store) Version() ([][2]string, error) {
func (s *store) Mount(id, mountLabel string) (string, error) { func (s *store) Mount(id, mountLabel string) (string, error) {
container, err := s.Container(id) container, err := s.Container(id)
var uidMap, gidMap []idtools.IDMap var (
uidMap, gidMap []idtools.IDMap
mountOpts []string
)
if err == nil { if err == nil {
uidMap, gidMap = container.UIDMap, container.GIDMap uidMap, gidMap = container.UIDMap, container.GIDMap
id = container.LayerID id = container.LayerID
mountOpts = container.MountOpts()
} }
rlstore, err := s.LayerStore() rlstore, err := s.LayerStore()
if err != nil { if err != nil {
@ -2273,7 +2299,13 @@ func (s *store) Mount(id, mountLabel string) (string, error) {
rlstore.Load() rlstore.Load()
} }
if rlstore.Exists(id) { if rlstore.Exists(id) {
return rlstore.Mount(id, mountLabel, uidMap, gidMap) options := drivers.MountOpts{
MountLabel: mountLabel,
UidMaps: uidMap,
GidMaps: gidMap,
Options: mountOpts,
}
return rlstore.Mount(id, options)
} }
return "", ErrLayerUnknown return "", ErrLayerUnknown
} }
@ -3176,56 +3208,19 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
storeOptions.UIDMap = mappings.UIDs() storeOptions.UIDMap = mappings.UIDs()
storeOptions.GIDMap = mappings.GIDs() storeOptions.GIDMap = mappings.GIDs()
} }
nonDigitsToWhitespace := func(r rune) rune {
if strings.IndexRune("0123456789", r) == -1 { uidmap, err := idtools.ParseIDMap([]string{config.Storage.Options.RemapUIDs}, "remap-uids")
return ' ' if err != nil {
} else { fmt.Print(err)
return r } else {
} storeOptions.UIDMap = append(storeOptions.UIDMap, uidmap...)
} }
parseTriple := func(spec []string) (container, host, size uint32, err error) { gidmap, err := idtools.ParseIDMap([]string{config.Storage.Options.RemapGIDs}, "remap-gids")
cid, err := strconv.ParseUint(spec[0], 10, 32) if err != nil {
if err != nil { fmt.Print(err)
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[0], err) } else {
} storeOptions.GIDMap = append(storeOptions.GIDMap, gidmap...)
hid, err := strconv.ParseUint(spec[1], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[1], err)
}
sz, err := strconv.ParseUint(spec[2], 10, 32)
if err != nil {
return 0, 0, 0, fmt.Errorf("error parsing id map value %q: %v", spec[2], err)
}
return uint32(cid), uint32(hid), uint32(sz), nil
} }
parseIDMap := func(idMapSpec, mapSetting string) (idmap []idtools.IDMap) {
if len(idMapSpec) > 0 {
idSpec := strings.Fields(strings.Map(nonDigitsToWhitespace, idMapSpec))
if len(idSpec)%3 != 0 {
fmt.Printf("Error initializing ID mappings: %s setting is malformed.\n", mapSetting)
return nil
}
for i := range idSpec {
if i%3 != 0 {
continue
}
cid, hid, size, err := parseTriple(idSpec[i : i+3])
if err != nil {
fmt.Printf("Error initializing ID mappings: %s setting is malformed.\n", mapSetting)
return nil
}
mapping := idtools.IDMap{
ContainerID: int(cid),
HostID: int(hid),
Size: int(size),
}
idmap = append(idmap, mapping)
}
}
return idmap
}
storeOptions.UIDMap = append(storeOptions.UIDMap, parseIDMap(config.Storage.Options.RemapUIDs, "remap-uids")...)
storeOptions.GIDMap = append(storeOptions.GIDMap, parseIDMap(config.Storage.Options.RemapGIDs, "remap-gids")...)
if os.Getenv("STORAGE_DRIVER") != "" { if os.Getenv("STORAGE_DRIVER") != "" {
storeOptions.GraphDriverName = os.Getenv("STORAGE_DRIVER") storeOptions.GraphDriverName = os.Getenv("STORAGE_DRIVER")
} }
@ -3244,3 +3239,23 @@ func init() {
ReloadConfigurationFile(defaultConfigFile, &DefaultStoreOptions) ReloadConfigurationFile(defaultConfigFile, &DefaultStoreOptions)
} }
func GetDefaultMountOptions() ([]string, error) {
mountOpts := []string{
".mountopt",
fmt.Sprintf("%s.mountopt", DefaultStoreOptions.GraphDriverName),
}
for _, option := range DefaultStoreOptions.GraphDriverOptions {
key, val, err := parsers.ParseKeyValueOpt(option)
if err != nil {
return nil, err
}
key = strings.ToLower(key)
for _, m := range mountOpts {
if m == key {
return strings.Split(val, ","), nil
}
}
}
return nil, nil
}

View File

@ -2,13 +2,14 @@ github.com/BurntSushi/toml master
github.com/Microsoft/go-winio 307e919c663683a9000576fdc855acaf9534c165 github.com/Microsoft/go-winio 307e919c663683a9000576fdc855acaf9534c165
github.com/Microsoft/hcsshim a8d9cc56cbce765a7eebdf4792e6ceceeff3edb8 github.com/Microsoft/hcsshim a8d9cc56cbce765a7eebdf4792e6ceceeff3edb8
github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76 github.com/davecgh/go-spew 346938d642f2ec3594ed81d874461961cd0faa76
github.com/docker/engine-api 4290f40c056686fcaa5c9caf02eac1dde9315adf github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52 github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52
github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6 github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6
github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062 github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062
github.com/opencontainers/go-digest master github.com/opencontainers/go-digest master
github.com/opencontainers/runc 6c22e77604689db8725fa866f0f2ec0b3e8c3a07 github.com/opencontainers/runc 6c22e77604689db8725fa866f0f2ec0b3e8c3a07
github.com/opencontainers/selinux ba1aefe8057f1d0cfb8e88d0ec1dc85925ef987d github.com/opencontainers/selinux 36a9bc45a08c85f2c52bd9eb32e20267876773bd
github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460
github.com/pborman/uuid 1b00554d822231195d1babd97ff4a781231955c9 github.com/pborman/uuid 1b00554d822231195d1babd97ff4a781231955c9
github.com/pkg/errors master github.com/pkg/errors master
github.com/pmezard/go-difflib v1.0.0 github.com/pmezard/go-difflib v1.0.0
@ -20,4 +21,3 @@ github.com/tchap/go-patricia v2.2.6
github.com/vbatts/tar-split v0.10.2 github.com/vbatts/tar-split v0.10.2
golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6 golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5 golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460

View File

@ -28,6 +28,7 @@ github.com/prometheus/client_golang c332b6f63c0658a65eca15c0e5247ded801cf564
github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c github.com/prometheus/client_model 99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c
github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563 github.com/prometheus/common 89604d197083d4781071d3c65855d24ecfb0a563
github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd github.com/prometheus/procfs cb4147076ac75738c9a7d279075a253c0cc5acbd
github.com/Shopify/logrus-bugsnag 577dee27f20dd8f1a529f82210094af593be12bd
github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064 github.com/spf13/cobra 312092086bed4968099259622145a0c9ae280064
github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842 github.com/spf13/pflag 5644820622454e71517561946e3d94b9f9db6842
github.com/xenolf/lego a9d8cec0e6563575e5868a005359ac97911b5985 github.com/xenolf/lego a9d8cec0e6563575e5868a005359ac97911b5985

View File

@ -1,55 +0,0 @@
## About
This directory contains a collection of scripts used to build and manage this
repository. If there are any issues regarding the intention of a particular
script (or even part of a certain script), please reach out to us.
It may help us either refine our current scripts, or add on new ones
that are appropriate for a given use case.
## DinD (dind.sh)
DinD is a wrapper script which allows Docker to be run inside a Docker
container. DinD requires the container to
be run with privileged mode enabled.
## Generate Authors (generate-authors.sh)
Generates AUTHORS; a file with all the names and corresponding emails of
individual contributors. AUTHORS can be found in the home directory of
this repository.
## Make
There are two make files, each with different extensions. Neither are supposed
to be called directly; only invoke `make`. Both scripts run inside a Docker
container.
### make.ps1
- The Windows native build script that uses PowerShell semantics; it is limited
unlike `hack\make.sh` since it does not provide support for the full set of
operations provided by the Linux counterpart, `make.sh`. However, `make.ps1`
does provide support for local Windows development and Windows to Windows CI.
More information is found within `make.ps1` by the author, @jhowardmsft
### make.sh
- Referenced via `make test` when running tests on a local machine,
or directly referenced when running tests inside a Docker development container.
- When running on a local machine, `make test` to run all tests found in
`test`, `test-unit`, `test-integration`, and `test-docker-py` on
your local machine. The default timeout is set in `make.sh` to 60 minutes
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
all of the tests.
- When running inside a Docker development container, `hack/make.sh` does
not have a single target that runs all the tests. You need to provide a
single command line with multiple targets that performs the same thing.
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration test-docker-py`
- For more information related to testing outside the scope of this README,
refer to
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
## Vendor (vendor.sh)
A shell script that is a wrapper around Vndr. For information on how to use
this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md)

View File

@ -1,69 +0,0 @@
# Integration Testing on Swarm
IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster
## Architecture
### Master service
- Works as a funker caller
- Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`)
### Worker service
- Works as a funker callee
- Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration` using the bind-mounted API socket (`docker.sock`)
### Client
- Controls master and workers via `docker stack`
- No need to have a local daemon
Typically, the master and workers are supposed to be running on a cloud environment,
while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows.
## Requirement
- Docker daemon 1.13 or later
- Private registry for distributed execution with multiple nodes
## Usage
### Step 1: Prepare images
$ make build-integration-cli-on-swarm
Following environment variables are known to work in this step:
- `BUILDFLAGS`
- `DOCKER_INCREMENTAL_BINARY`
Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`.
### Step 2: Execute tests
$ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest
Following environment variables are known to work in this step:
- `DOCKER_GRAPHDRIVER`
- `DOCKER_EXPERIMENTAL`
#### Flags
Basic flags:
- `-replicas N`: the number of worker service replicas. i.e. degree of parallelism.
- `-chunks N`: the number of chunks. By default, `chunks` == `replicas`.
- `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`.
Experimental flags for mitigating makespan nonuniformity:
- `-shuffle`: Shuffle the test filter strings
Flags for debugging IT on Swarm itself:
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
- `-dry-run`: skip the actual workload
- `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm

View File

@ -1,2 +0,0 @@
# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here
github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773

View File

@ -68,6 +68,7 @@ make BUILDTAGS='seccomp apparmor'
| selinux | selinux process and mount labeling | <none> | | selinux | selinux process and mount labeling | <none> |
| apparmor | apparmor profile support | <none> | | apparmor | apparmor profile support | <none> |
| ambient | ambient capability support | kernel 4.3 | | ambient | ambient capability support | kernel 4.3 |
| nokmem | disable kernel memory account | <none> |
### Running the test suite ### Running the test suite
@ -263,3 +264,7 @@ PIDFile=/run/mycontainerid.pid
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
``` ```
## License
The code and docs are released under the [Apache 2.0 license](LICENSE).

View File

@ -148,6 +148,7 @@ config := &configs.Config{
{Type: configs.NEWPID}, {Type: configs.NEWPID},
{Type: configs.NEWUSER}, {Type: configs.NEWUSER},
{Type: configs.NEWNET}, {Type: configs.NEWNET},
{Type: configs.NEWCGROUP},
}), }),
Cgroups: &configs.Cgroup{ Cgroups: &configs.Cgroup{
Name: "test-container", Name: "test-container",
@ -323,6 +324,7 @@ generated when building libcontainer with docker.
## Copyright and license ## Copyright and license
Code and documentation copyright 2014 Docker, inc. Code released under the Apache 2.0 license. Code and documentation copyright 2014 Docker, inc.
Docs released under Creative commons. The code and documentation are released under the [Apache 2.0 license](../LICENSE).
The documentation is also released under Creative Commons Attribution 4.0 International License.
You may obtain a copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/.

View File

@ -42,6 +42,12 @@ enum sync_t {
SYNC_ERR = 0xFF, /* Fatal error, no turning back. The error code follows. */ SYNC_ERR = 0xFF, /* Fatal error, no turning back. The error code follows. */
}; };
/*
* Synchronisation value for cgroup namespace setup.
* The same constant is defined in process_linux.go as "createCgroupns".
*/
#define CREATECGROUPNS 0x80
/* longjmp() arguments. */ /* longjmp() arguments. */
#define JUMP_PARENT 0x00 #define JUMP_PARENT 0x00
#define JUMP_CHILD 0xA0 #define JUMP_CHILD 0xA0
@ -82,7 +88,7 @@ struct nlconfig_t {
uint8_t is_setgroup; uint8_t is_setgroup;
/* Rootless container settings. */ /* Rootless container settings. */
uint8_t is_rootless; uint8_t is_rootless_euid; /* boolean */
char *uidmappath; char *uidmappath;
size_t uidmappath_len; size_t uidmappath_len;
char *gidmappath; char *gidmappath;
@ -100,7 +106,7 @@ struct nlconfig_t {
#define GIDMAP_ATTR 27284 #define GIDMAP_ATTR 27284
#define SETGROUP_ATTR 27285 #define SETGROUP_ATTR 27285
#define OOM_SCORE_ADJ_ATTR 27286 #define OOM_SCORE_ADJ_ATTR 27286
#define ROOTLESS_ATTR 27287 #define ROOTLESS_EUID_ATTR 27287
#define UIDMAPPATH_ATTR 27288 #define UIDMAPPATH_ATTR 27288
#define GIDMAPPATH_ATTR 27289 #define GIDMAPPATH_ATTR 27289
@ -419,8 +425,8 @@ static void nl_parse(int fd, struct nlconfig_t *config)
case CLONE_FLAGS_ATTR: case CLONE_FLAGS_ATTR:
config->cloneflags = readint32(current); config->cloneflags = readint32(current);
break; break;
case ROOTLESS_ATTR: case ROOTLESS_EUID_ATTR:
config->is_rootless = readint8(current); config->is_rootless_euid = readint8(current); /* boolean */
break; break;
case OOM_SCORE_ADJ_ATTR: case OOM_SCORE_ADJ_ATTR:
config->oom_score_adj = current; config->oom_score_adj = current;
@ -640,7 +646,6 @@ void nsexec(void)
case JUMP_PARENT:{ case JUMP_PARENT:{
int len; int len;
pid_t child, first_child = -1; pid_t child, first_child = -1;
char buf[JSON_MAX];
bool ready = false; bool ready = false;
/* For debugging. */ /* For debugging. */
@ -687,7 +692,7 @@ void nsexec(void)
* newuidmap/newgidmap shall be used. * newuidmap/newgidmap shall be used.
*/ */
if (config.is_rootless && !config.is_setgroup) if (config.is_rootless_euid && !config.is_setgroup)
update_setgroups(child, SETGROUPS_DENY); update_setgroups(child, SETGROUPS_DENY);
/* Set up mappings. */ /* Set up mappings. */
@ -716,6 +721,18 @@ void nsexec(void)
kill(child, SIGKILL); kill(child, SIGKILL);
bail("failed to sync with child: write(SYNC_RECVPID_ACK)"); bail("failed to sync with child: write(SYNC_RECVPID_ACK)");
} }
/* Send the init_func pid back to our parent.
*
* Send the init_func pid and the pid of the first child back to our parent.
* We need to send both back because we can't reap the first child we created (CLONE_PARENT).
* It becomes the responsibility of our parent to reap the first child.
*/
len = dprintf(pipenum, "{\"pid\": %d, \"pid_first\": %d}\n", child, first_child);
if (len < 0) {
kill(child, SIGKILL);
bail("unable to generate JSON for child pid");
}
} }
break; break;
case SYNC_CHILD_READY: case SYNC_CHILD_READY:
@ -759,23 +776,6 @@ void nsexec(void)
bail("unexpected sync value: %u", s); bail("unexpected sync value: %u", s);
} }
} }
/*
* Send the init_func pid and the pid of the first child back to our parent.
*
* We need to send both back because we can't reap the first child we created (CLONE_PARENT).
* It becomes the responsibility of our parent to reap the first child.
*/
len = snprintf(buf, JSON_MAX, "{\"pid\": %d, \"pid_first\": %d}\n", child, first_child);
if (len < 0) {
kill(child, SIGKILL);
bail("unable to generate JSON for child pid");
}
if (write(pipenum, buf, len) != len) {
kill(child, SIGKILL);
bail("unable to send child pid to bootstrapper");
}
exit(0); exit(0);
} }
@ -862,14 +862,17 @@ void nsexec(void)
if (setresuid(0, 0, 0) < 0) if (setresuid(0, 0, 0) < 0)
bail("failed to become root in user namespace"); bail("failed to become root in user namespace");
} }
/* /*
* Unshare all of the namespaces. Note that we don't merge this * Unshare all of the namespaces. Now, it should be noted that this
* with clone() because there were some old kernel versions where * ordering might break in the future (especially with rootless
* clone(CLONE_PARENT | CLONE_NEWPID) was broken, so we'll just do * containers). But for now, it's not possible to split this into
* it the long way. * CLONE_NEWUSER + [the rest] because of some RHEL SELinux issues.
*
* Note that we don't merge this with clone() because there were
* some old kernel versions where clone(CLONE_PARENT | CLONE_NEWPID)
* was broken, so we'll just do it the long way anyway.
*/ */
if (unshare(config.cloneflags) < 0) if (unshare(config.cloneflags & ~CLONE_NEWCGROUP) < 0)
bail("failed to unshare namespaces"); bail("failed to unshare namespaces");
/* /*
@ -953,11 +956,23 @@ void nsexec(void)
if (setgid(0) < 0) if (setgid(0) < 0)
bail("setgid failed"); bail("setgid failed");
if (!config.is_rootless && config.is_setgroup) { if (!config.is_rootless_euid && config.is_setgroup) {
if (setgroups(0, NULL) < 0) if (setgroups(0, NULL) < 0)
bail("setgroups failed"); bail("setgroups failed");
} }
/* ... wait until our topmost parent has finished cgroup setup in p.manager.Apply() ... */
if (config.cloneflags & CLONE_NEWCGROUP) {
uint8_t value;
if (read(pipenum, &value, sizeof(value)) != sizeof(value))
bail("read synchronisation value failed");
if (value == CREATECGROUPNS) {
if (unshare(CLONE_NEWCGROUP) < 0)
bail("failed to unshare cgroup namespace");
} else
bail("received unknown synchronisation value");
}
s = SYNC_CHILD_READY; s = SYNC_CHILD_READY;
if (write(syncfd, &s, sizeof(s)) != sizeof(s)) if (write(syncfd, &s, sizeof(s)) != sizeof(s))
bail("failed to sync with patent: write(SYNC_CHILD_READY)"); bail("failed to sync with patent: write(SYNC_CHILD_READY)");

View File

@ -5,6 +5,7 @@ package user
import ( import (
"io" "io"
"os" "os"
"strconv"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@ -115,22 +116,23 @@ func CurrentGroup() (Group, error) {
return LookupGid(unix.Getgid()) return LookupGid(unix.Getgid())
} }
func CurrentUserSubUIDs() ([]SubID, error) { func currentUserSubIDs(fileName string) ([]SubID, error) {
u, err := CurrentUser() u, err := CurrentUser()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ParseSubIDFileFilter("/etc/subuid", filter := func(entry SubID) bool {
func(entry SubID) bool { return entry.Name == u.Name }) return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid)
}
return ParseSubIDFileFilter(fileName, filter)
} }
func CurrentGroupSubGIDs() ([]SubID, error) { func CurrentUserSubUIDs() ([]SubID, error) {
g, err := CurrentGroup() return currentUserSubIDs("/etc/subuid")
if err != nil { }
return nil, err
} func CurrentUserSubGIDs() ([]SubID, error) {
return ParseSubIDFileFilter("/etc/subgid", return currentUserSubIDs("/etc/subgid")
func(entry SubID) bool { return entry.Name == g.Name })
} }
func CurrentProcessUIDMap() ([]IDMap, error) { func CurrentProcessUIDMap() ([]IDMap, error) {

View File

@ -1,7 +1,7 @@
# OCI runtime-spec. When updating this, make sure you use a version tag rather # OCI runtime-spec. When updating this, make sure you use a version tag rather
# than a commit ID so it's much more obvious what version of the spec we are # than a commit ID so it's much more obvious what version of the spec we are
# using. # using.
github.com/opencontainers/runtime-spec v1.0.0 github.com/opencontainers/runtime-spec 5684b8af48c1ac3b1451fa499724e30e3c20a294
# Core libcontainer functionality. # Core libcontainer functionality.
github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08 github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
github.com/opencontainers/selinux v1.0.0-rc1 github.com/opencontainers/selinux v1.0.0-rc1

View File

@ -48,6 +48,11 @@ func GetPidLabel(pid int) (string, error) {
func Init() { func Init() {
} }
// ClearLabels clears all reserved labels
func ClearLabels() {
return
}
func ReserveLabel(label string) error { func ReserveLabel(label string) error {
return nil return nil
} }

View File

@ -24,17 +24,22 @@ var ErrIncompatibleLabel = fmt.Errorf("Bad SELinux option z and Z can not be use
// the container. A list of options can be passed into this function to alter // the container. A list of options can be passed into this function to alter
// the labels. The labels returned will include a random MCS String, that is // the labels. The labels returned will include a random MCS String, that is
// guaranteed to be unique. // guaranteed to be unique.
func InitLabels(options []string) (string, string, error) { func InitLabels(options []string) (plabel string, mlabel string, Err error) {
if !selinux.GetEnabled() { if !selinux.GetEnabled() {
return "", "", nil return "", "", nil
} }
processLabel, mountLabel := selinux.ContainerLabels() processLabel, mountLabel := selinux.ContainerLabels()
if processLabel != "" { if processLabel != "" {
defer func() {
if Err != nil {
ReleaseLabel(mountLabel)
}
}()
pcon := selinux.NewContext(processLabel) pcon := selinux.NewContext(processLabel)
mcon := selinux.NewContext(mountLabel) mcon := selinux.NewContext(mountLabel)
for _, opt := range options { for _, opt := range options {
if opt == "disable" { if opt == "disable" {
return "", "", nil return "", mountLabel, nil
} }
if i := strings.Index(opt, ":"); i == -1 { if i := strings.Index(opt, ":"); i == -1 {
return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type' followed by ':' and a value", opt) return "", "", fmt.Errorf("Bad label option %q, valid options 'disable' or \n'user, role, level, type' followed by ':' and a value", opt)
@ -156,6 +161,11 @@ func Init() {
selinux.GetEnabled() selinux.GetEnabled()
} }
// ClearLabels will clear all reserved labels
func ClearLabels() {
selinux.ClearLabels()
}
// ReserveLabel will record the fact that the MCS label has already been used. // ReserveLabel will record the fact that the MCS label has already been used.
// This will prevent InitLabels from using the MCS label in a newly created // This will prevent InitLabels from using the MCS label in a newly created
// container // container

View File

@ -409,6 +409,13 @@ func NewContext(label string) Context {
return c return c
} }
// ClearLabels clears all reserved labels
func ClearLabels() {
state.Lock()
state.mcsList = make(map[string]bool)
state.Unlock()
}
// ReserveLabel reserves the MLS/MCS level component of the specified label // ReserveLabel reserves the MLS/MCS level component of the specified label
func ReserveLabel(label string) { func ReserveLabel(label string) {
if len(label) != 0 { if len(label) != 0 {
@ -680,7 +687,11 @@ func Chcon(fpath string, label string, recurse bool) error {
return err return err
} }
callback := func(p string, info os.FileInfo, err error) error { callback := func(p string, info os.FileInfo, err error) error {
return SetFileLabel(p, label) e := SetFileLabel(p, label)
if os.IsNotExist(e) {
return nil
}
return e
} }
if recurse { if recurse {

View File

@ -107,6 +107,11 @@ func NewContext(label string) Context {
return c return c
} }
// ClearLabels clears all reserved MLS/MCS levels
func ClearLabels() {
return
}
// ReserveLabel reserves the MLS/MCS level component of the specified label // ReserveLabel reserves the MLS/MCS level component of the specified label
func ReserveLabel(label string) { func ReserveLabel(label string) {
return return

View File

@ -6,7 +6,7 @@
// return err // return err
// } // }
// //
// which applied recursively up the call stack results in error reports // which when applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows // without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way // programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error. // that does not destroy the original value of the error.
@ -15,16 +15,17 @@
// //
// The errors.Wrap function returns a new error that adds context to the // The errors.Wrap function returns a new error that adds context to the
// original error by recording a stack trace at the point Wrap is called, // original error by recording a stack trace at the point Wrap is called,
// and the supplied message. For example // together with the supplied message. For example
// //
// _, err := ioutil.ReadAll(r) // _, err := ioutil.ReadAll(r)
// if err != nil { // if err != nil {
// return errors.Wrap(err, "read failed") // return errors.Wrap(err, "read failed")
// } // }
// //
// If additional control is required the errors.WithStack and errors.WithMessage // If additional control is required, the errors.WithStack and
// functions destructure errors.Wrap into its component operations of annotating // errors.WithMessage functions destructure errors.Wrap into its component
// an error with a stack trace and an a message, respectively. // operations: annotating an error with a stack trace and with a message,
// respectively.
// //
// Retrieving the cause of an error // Retrieving the cause of an error
// //
@ -38,7 +39,7 @@
// } // }
// //
// can be inspected by errors.Cause. errors.Cause will recursively retrieve // can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error which does not implement causer, which is assumed to be // the topmost error that does not implement causer, which is assumed to be
// the original cause. For example: // the original cause. For example:
// //
// switch err := errors.Cause(err).(type) { // switch err := errors.Cause(err).(type) {
@ -48,16 +49,16 @@
// // unknown error // // unknown error
// } // }
// //
// causer interface is not exported by this package, but is considered a part // Although the causer interface is not exported by this package, it is
// of stable public API. // considered a part of its stable public interface.
// //
// Formatted printing of errors // Formatted printing of errors
// //
// All error values returned from this package implement fmt.Formatter and can // All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported // be formatted by the fmt package. The following verbs are supported:
// //
// %s print the error. If the error has a Cause it will be // %s print the error. If the error has a Cause it will be
// printed recursively // printed recursively.
// %v see %s // %v see %s
// %+v extended format. Each Frame of the error's StackTrace will // %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail. // be printed in detail.
@ -65,13 +66,13 @@
// Retrieving the stack trace of an error or wrapper // Retrieving the stack trace of an error or wrapper
// //
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
// invoked. This information can be retrieved with the following interface. // invoked. This information can be retrieved with the following interface:
// //
// type stackTracer interface { // type stackTracer interface {
// StackTrace() errors.StackTrace // StackTrace() errors.StackTrace
// } // }
// //
// Where errors.StackTrace is defined as // The returned errors.StackTrace type is defined as
// //
// type StackTrace []Frame // type StackTrace []Frame
// //
@ -85,8 +86,8 @@
// } // }
// } // }
// //
// stackTracer interface is not exported by this package, but is considered a part // Although the stackTracer interface is not exported by this package, it is
// of stable public API. // considered a part of its stable public interface.
// //
// See the documentation for Frame.Format for more details. // See the documentation for Frame.Format for more details.
package errors package errors
@ -220,6 +221,18 @@ func WithMessage(err error, message string) error {
} }
} }
// WithMessagef annotates err with the format specifier.
// If err is nil, WithMessagef returns nil.
func WithMessagef(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &withMessage{
cause: err,
msg: fmt.Sprintf(format, args...),
}
}
type withMessage struct { type withMessage struct {
cause error cause error
msg string msg string

View File

@ -1,40 +0,0 @@
// Copyright 2014-2017 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"bytes"
"io"
"log"
"os"
"github.com/ulikunitz/xz"
)
func main() {
const text = "The quick brown fox jumps over the lazy dog.\n"
var buf bytes.Buffer
// compress text
w, err := xz.NewWriter(&buf)
if err != nil {
log.Fatalf("xz.NewWriter error %s", err)
}
if _, err := io.WriteString(w, text); err != nil {
log.Fatalf("WriteString error %s", err)
}
if err := w.Close(); err != nil {
log.Fatalf("w.Close error %s", err)
}
// decompress buffer and write output to stdout
r, err := xz.NewReader(&buf)
if err != nil {
log.Fatalf("NewReader error %s", err)
}
if _, err = io.Copy(os.Stdout, r); err != nil {
log.Fatalf("io.Copy error %s", err)
}
}

View File

@ -280,10 +280,35 @@ Learn more about what types of template functions you can use in `ErrorTemplateF
## Formats ## Formats
JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this: JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this:
````json ````json
{"type": "string", "format": "email"} {"type": "string", "format": "email"}
```` ````
Available formats: date-time, hostname, email, ipv4, ipv6, uri, uri-reference, uuid, regex. Some of the new formats in draft-06 and draft-07 are not yet implemented.
Not all formats defined in draft-07 are available. Implemented formats are:
* `date`
* `time`
* `date-time`
* `hostname`. Subdomains that start with a number are also supported, but this means that it doesn't strictly follow [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5) and has the implication that ipv4 addresses are also recognized as valid hostnames.
* `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support.
* `idn-email`. Same caveat as `email`.
* `ipv4`
* `ipv6`
* `uri`. Includes unicode support.
* `uri-reference`. Includes unicode support.
* `iri`
* `iri-reference`
* `uri-template`
* `uuid`
* `regex`. Go uses the [RE2](https://github.com/google/re2/wiki/Syntax) engine and is not [ECMA262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) compatible.
* `json-pointer`
* `relative-json-pointer`
`email`, `uri` and `uri-reference` use the same validation code as their unicode counterparts `idn-email`, `iri` and `iri-reference`. If you rely on unicode support you should use the specific
unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats.
The validation code for `uri`, `idn-email` and their relatives use mostly standard library code. Go 1.5 and 1.6 contain some minor bugs with handling URIs and unicode. You are encouraged to use Go 1.7+ if you rely on these formats.
For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this: For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this:
@ -340,6 +365,13 @@ func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool {
gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{}) gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{})
```` ````
Formats can also be removed, for example if you want to override one of the formats that is defined by default.
```go
gojsonschema.FormatCheckers.Remove("hostname")
```
## Additional custom validation ## Additional custom validation
After the validation has run and you have the results, you may add additional After the validation has run and you have the results, you may add additional
errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead

View File

@ -81,7 +81,7 @@ type (
ResultErrorFields ResultErrorFields
} }
// ItemsMustBeUniqueError. ErrorDetails: type // ItemsMustBeUniqueError. ErrorDetails: type, i, j
ItemsMustBeUniqueError struct { ItemsMustBeUniqueError struct {
ResultErrorFields ResultErrorFields
} }

View File

@ -2,9 +2,11 @@ package gojsonschema
import ( import (
"net" "net"
"net/mail"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
) )
@ -51,12 +53,19 @@ type (
// http://tools.ietf.org/html/rfc3339#section-5.6 // http://tools.ietf.org/html/rfc3339#section-5.6
DateTimeFormatChecker struct{} DateTimeFormatChecker struct{}
DateFormatChecker struct{}
TimeFormatChecker struct{}
// URIFormatChecker validates a URI with a valid Scheme per RFC3986 // URIFormatChecker validates a URI with a valid Scheme per RFC3986
URIFormatChecker struct{} URIFormatChecker struct{}
// URIReferenceFormatChecker validates a URI or relative-reference per RFC3986 // URIReferenceFormatChecker validates a URI or relative-reference per RFC3986
URIReferenceFormatChecker struct{} URIReferenceFormatChecker struct{}
// URITemplateFormatChecker validates a URI template per RFC6570
URITemplateFormatChecker struct{}
// HostnameFormatChecker validates a hostname is in the correct format // HostnameFormatChecker validates a hostname is in the correct format
HostnameFormatChecker struct{} HostnameFormatChecker struct{}
@ -65,6 +74,12 @@ type (
// RegexFormatChecker validates a regex is in the correct format // RegexFormatChecker validates a regex is in the correct format
RegexFormatChecker struct{} RegexFormatChecker struct{}
// JSONPointerFormatChecker validates a JSON Pointer per RFC6901
JSONPointerFormatChecker struct{}
// RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format
RelativeJSONPointerFormatChecker struct{}
) )
var ( var (
@ -72,45 +87,65 @@ var (
// so library users can add custom formatters // so library users can add custom formatters
FormatCheckers = FormatCheckerChain{ FormatCheckers = FormatCheckerChain{
formatters: map[string]FormatChecker{ formatters: map[string]FormatChecker{
"date-time": DateTimeFormatChecker{}, "date": DateFormatChecker{},
"hostname": HostnameFormatChecker{}, "time": TimeFormatChecker{},
"email": EmailFormatChecker{}, "date-time": DateTimeFormatChecker{},
"ipv4": IPV4FormatChecker{}, "hostname": HostnameFormatChecker{},
"ipv6": IPV6FormatChecker{}, "email": EmailFormatChecker{},
"uri": URIFormatChecker{}, "idn-email": EmailFormatChecker{},
"uri-reference": URIReferenceFormatChecker{}, "ipv4": IPV4FormatChecker{},
"uuid": UUIDFormatChecker{}, "ipv6": IPV6FormatChecker{},
"regex": RegexFormatChecker{}, "uri": URIFormatChecker{},
"uri-reference": URIReferenceFormatChecker{},
"iri": URIFormatChecker{},
"iri-reference": URIReferenceFormatChecker{},
"uri-template": URITemplateFormatChecker{},
"uuid": UUIDFormatChecker{},
"regex": RegexFormatChecker{},
"json-pointer": JSONPointerFormatChecker{},
"relative-json-pointer": RelativeJSONPointerFormatChecker{},
}, },
} }
// Regex credit: https://github.com/asaskevich/govalidator
rxEmail = regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$")
// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname // Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
// Use a regex to make sure curly brackets are balanced properly after validating it as a AURI
rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")
rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")
rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")
lock = new(sync.Mutex)
) )
// Add adds a FormatChecker to the FormatCheckerChain // Add adds a FormatChecker to the FormatCheckerChain
// The name used will be the value used for the format key in your json schema // The name used will be the value used for the format key in your json schema
func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain { func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
lock.Lock()
c.formatters[name] = f c.formatters[name] = f
lock.Unlock()
return c return c
} }
// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists) // Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain { func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
lock.Lock()
delete(c.formatters, name) delete(c.formatters, name)
lock.Unlock()
return c return c
} }
// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name // Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
func (c *FormatCheckerChain) Has(name string) bool { func (c *FormatCheckerChain) Has(name string) bool {
lock.Lock()
_, ok := c.formatters[name] _, ok := c.formatters[name]
lock.Unlock()
return ok return ok
} }
@ -134,7 +169,9 @@ func (f EmailFormatChecker) IsFormat(input interface{}) bool {
return false return false
} }
return rxEmail.MatchString(asString) _, err := mail.ParseAddress(asString)
return err == nil
} }
// Credit: https://github.com/asaskevich/govalidator // Credit: https://github.com/asaskevich/govalidator
@ -185,6 +222,29 @@ func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
return false return false
} }
func (f DateFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string)
if ok == false {
return false
}
_, err := time.Parse("2006-01-02", asString)
return err == nil
}
func (f TimeFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string)
if ok == false {
return false
}
if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
return true
}
_, err := time.Parse("15:04:05", asString)
return err == nil
}
func (f URIFormatChecker) IsFormat(input interface{}) bool { func (f URIFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string) asString, ok := input.(string)
@ -193,11 +253,12 @@ func (f URIFormatChecker) IsFormat(input interface{}) bool {
} }
u, err := url.Parse(asString) u, err := url.Parse(asString)
if err != nil || u.Scheme == "" { if err != nil || u.Scheme == "" {
return false return false
} }
return true return !strings.Contains(asString, `\`)
} }
func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool { func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
@ -208,7 +269,21 @@ func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
} }
_, err := url.Parse(asString) _, err := url.Parse(asString)
return err == nil return err == nil && !strings.Contains(asString, `\`)
}
func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string)
if ok == false {
return false
}
u, err := url.Parse(asString)
if err != nil || strings.Contains(asString, `\`) {
return false
}
return rxURITemplate.MatchString(u.Path)
} }
func (f HostnameFormatChecker) IsFormat(input interface{}) bool { func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
@ -248,3 +323,21 @@ func (f RegexFormatChecker) IsFormat(input interface{}) bool {
} }
return true return true
} }
func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string)
if ok == false {
return false
}
return rxJSONPointer.MatchString(asString)
}
func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
asString, ok := input.(string)
if ok == false {
return false
}
return rxRelJSONPointer.MatchString(asString)
}

View File

@ -147,7 +147,7 @@ func (l DefaultLocale) ArrayMaxItems() string {
} }
func (l DefaultLocale) Unique() string { func (l DefaultLocale) Unique() string {
return `{{.type}} items must be unique` return `{{.type}} items[{{.i}},{{.j}}] must be unique`
} }
func (l DefaultLocale) ArrayContains() string { func (l DefaultLocale) ArrayContains() string {

View File

@ -76,12 +76,6 @@ type (
// Field outputs the field name without the root context // Field outputs the field name without the root context
// i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName
func (v *ResultErrorFields) Field() string { func (v *ResultErrorFields) Field() string {
if p, ok := v.Details()["property"]; ok {
if str, isString := p.(string); isString {
return str
}
}
return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".") return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".")
} }

View File

@ -62,6 +62,16 @@ func isStringInSlice(s []string, what string) bool {
return false return false
} }
// indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s.
func indexStringInSlice(s []string, what string) int {
for i := range s {
if s[i] == what {
return i
}
}
return -1
}
func marshalToJsonString(value interface{}) (*string, error) { func marshalToJsonString(value interface{}) (*string, error) {
mBytes, err := json.Marshal(value) mBytes, err := json.Marshal(value)

View File

@ -517,17 +517,17 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
// uniqueItems: // uniqueItems:
if currentSubSchema.uniqueItems { if currentSubSchema.uniqueItems {
var stringifiedItems []string var stringifiedItems []string
for _, v := range value { for j, v := range value {
vString, err := marshalWithoutNumber(v) vString, err := marshalWithoutNumber(v)
if err != nil { if err != nil {
result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err}) result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err})
} }
if isStringInSlice(stringifiedItems, *vString) { if i := indexStringInSlice(stringifiedItems, *vString); i > -1 {
result.addInternalError( result.addInternalError(
new(ItemsMustBeUniqueError), new(ItemsMustBeUniqueError),
context, context,
value, value,
ErrorDetails{"type": TYPE_ARRAY}, ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j},
) )
} }
stringifiedItems = append(stringifiedItems, *vString) stringifiedItems = append(stringifiedItems, *vString)

2
vendor/go4.org/README.md generated vendored
View File

@ -1,6 +1,6 @@
# go4 # go4
[![travis badge](https://travis-ci.org/camlistore/go4.svg?branch=master)](https://travis-ci.org/camlistore/go4 "Travis CI") [![travis badge](https://travis-ci.org/go4org/go4.svg?branch=master)](https://travis-ci.org/go4org/go4 "Travis CI")
[go4.org](http://go4.org) is a collection of packages for [go4.org](http://go4.org) is a collection of packages for
Go programmers. Go programmers.

View File

@ -345,36 +345,8 @@ EachPacket:
switch pkt := p.(type) { switch pkt := p.(type) {
case *packet.UserId: case *packet.UserId:
// Make a new Identity object, that we might wind up throwing away. if err := addUserID(e, packets, pkt); err != nil {
// We'll only add it if we get a valid self-signature over this return nil, err
// userID.
current := new(Identity)
current.Name = pkt.Id
current.UserId = pkt
for {
p, err = packets.Next()
if err == io.EOF {
break EachPacket
} else if err != nil {
return nil, err
}
sig, ok := p.(*packet.Signature)
if !ok {
packets.Unread(p)
continue EachPacket
}
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
return nil, errors.StructuralError("user ID self-signature invalid: " + err.Error())
}
current.SelfSignature = sig
e.Identities[pkt.Id] = current
} else {
current.Signatures = append(current.Signatures, sig)
}
} }
case *packet.Signature: case *packet.Signature:
if pkt.SigType == packet.SigTypeKeyRevocation { if pkt.SigType == packet.SigTypeKeyRevocation {
@ -426,6 +398,42 @@ EachPacket:
return e, nil return e, nil
} }
func addUserID(e *Entity, packets *packet.Reader, pkt *packet.UserId) error {
// Make a new Identity object, that we might wind up throwing away.
// We'll only add it if we get a valid self-signature over this
// userID.
identity := new(Identity)
identity.Name = pkt.Id
identity.UserId = pkt
for {
p, err := packets.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
sig, ok := p.(*packet.Signature)
if !ok {
packets.Unread(p)
break
}
if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId {
if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, e.PrimaryKey, sig); err != nil {
return errors.StructuralError("user ID self-signature invalid: " + err.Error())
}
identity.SelfSignature = sig
e.Identities[pkt.Id] = identity
} else {
identity.Signatures = append(identity.Signatures, sig)
}
}
return nil
}
func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error { func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *packet.PrivateKey) error {
var subKey Subkey var subKey Subkey
subKey.PublicKey = pub subKey.PublicKey = pub
@ -457,7 +465,8 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
case packet.SigTypeSubkeyRevocation: case packet.SigTypeSubkeyRevocation:
subKey.Sig = sig subKey.Sig = sig
case packet.SigTypeSubkeyBinding: case packet.SigTypeSubkeyBinding:
if subKey.Sig == nil {
if shouldReplaceSubkeySig(subKey.Sig, sig) {
subKey.Sig = sig subKey.Sig = sig
} }
} }
@ -472,6 +481,22 @@ func addSubkey(e *Entity, packets *packet.Reader, pub *packet.PublicKey, priv *p
return nil return nil
} }
func shouldReplaceSubkeySig(existingSig, potentialNewSig *packet.Signature) bool {
if potentialNewSig == nil {
return false
}
if existingSig == nil {
return true
}
if existingSig.SigType == packet.SigTypeSubkeyRevocation {
return false // never override a revocation signature
}
return potentialNewSig.CreationTime.After(existingSig.CreationTime)
}
const defaultRSAKeyBits = 2048 const defaultRSAKeyBits = 2048
// NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a

View File

@ -404,14 +404,16 @@ const (
type PublicKeyAlgorithm uint8 type PublicKeyAlgorithm uint8
const ( const (
PubKeyAlgoRSA PublicKeyAlgorithm = 1 PubKeyAlgoRSA PublicKeyAlgorithm = 1
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 PubKeyAlgoElGamal PublicKeyAlgorithm = 16
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 PubKeyAlgoDSA PublicKeyAlgorithm = 17
PubKeyAlgoElGamal PublicKeyAlgorithm = 16
PubKeyAlgoDSA PublicKeyAlgorithm = 17
// RFC 6637, Section 5. // RFC 6637, Section 5.
PubKeyAlgoECDH PublicKeyAlgorithm = 18 PubKeyAlgoECDH PublicKeyAlgorithm = 18
PubKeyAlgoECDSA PublicKeyAlgorithm = 19 PubKeyAlgoECDSA PublicKeyAlgorithm = 19
// Deprecated in RFC 4880, Section 13.5. Use key flags instead.
PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2
PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3
) )
// CanEncrypt returns true if it's possible to encrypt a message to a public // CanEncrypt returns true if it's possible to encrypt a message to a public

View File

@ -64,14 +64,19 @@ func NewECDSAPrivateKey(currentTime time.Time, priv *ecdsa.PrivateKey) *PrivateK
return pk return pk
} }
// NewSignerPrivateKey creates a sign-only PrivateKey from a crypto.Signer that // NewSignerPrivateKey creates a PrivateKey from a crypto.Signer that
// implements RSA or ECDSA. // implements RSA or ECDSA.
func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey { func NewSignerPrivateKey(currentTime time.Time, signer crypto.Signer) *PrivateKey {
pk := new(PrivateKey) pk := new(PrivateKey)
// In general, the public Keys should be used as pointers. We still
// type-switch on the values, for backwards-compatibility.
switch pubkey := signer.Public().(type) { switch pubkey := signer.Public().(type) {
case *rsa.PublicKey:
pk.PublicKey = *NewRSAPublicKey(currentTime, pubkey)
case rsa.PublicKey: case rsa.PublicKey:
pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey) pk.PublicKey = *NewRSAPublicKey(currentTime, &pubkey)
pk.PubKeyAlgo = PubKeyAlgoRSASignOnly case *ecdsa.PublicKey:
pk.PublicKey = *NewECDSAPublicKey(currentTime, pubkey)
case ecdsa.PublicKey: case ecdsa.PublicKey:
pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey) pk.PublicKey = *NewECDSAPublicKey(currentTime, &pubkey)
default: default:

View File

@ -542,7 +542,7 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
r, s, err = ecdsa.Sign(config.Random(), pk, digest) r, s, err = ecdsa.Sign(config.Random(), pk, digest)
} else { } else {
var b []byte var b []byte
b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, nil) b, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
if err == nil { if err == nil {
r, s, err = unwrapECDSASig(b) r, s, err = unwrapECDSASig(b)
} }

View File

@ -80,7 +80,7 @@ func (uat *UserAttribute) Serialize(w io.Writer) (err error) {
// ImageData returns zero or more byte slices, each containing // ImageData returns zero or more byte slices, each containing
// JPEG File Interchange Format (JFIF), for each photo in the // JPEG File Interchange Format (JFIF), for each photo in the
// the user attribute packet. // user attribute packet.
func (uat *UserAttribute) ImageData() (imageData [][]byte) { func (uat *UserAttribute) ImageData() (imageData [][]byte) {
for _, sp := range uat.Contents { for _, sp := range uat.Contents {
if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 {

View File

@ -2,18 +2,15 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.7
// Package ctxhttp provides helper functions for performing context-aware HTTP requests. // Package ctxhttp provides helper functions for performing context-aware HTTP requests.
package ctxhttp // import "golang.org/x/net/context/ctxhttp" package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import ( import (
"context"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"golang.org/x/net/context"
) )
// Do sends an HTTP request with the provided http.Client and returns // Do sends an HTTP request with the provided http.Client and returns

View File

@ -1,147 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.7
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import (
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
)
func nop() {}
var (
testHookContextDoneBeforeHeaders = nop
testHookDoReturned = nop
testHookDidBodyClose = nop
)
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
// If the client is nil, http.DefaultClient is used.
// If the context is canceled or times out, ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
// TODO(djd): Respect any existing value of req.Cancel.
cancel := make(chan struct{})
req.Cancel = cancel
type responseAndError struct {
resp *http.Response
err error
}
result := make(chan responseAndError, 1)
// Make local copies of test hooks closed over by goroutines below.
// Prevents data races in tests.
testHookDoReturned := testHookDoReturned
testHookDidBodyClose := testHookDidBodyClose
go func() {
resp, err := client.Do(req)
testHookDoReturned()
result <- responseAndError{resp, err}
}()
var resp *http.Response
select {
case <-ctx.Done():
testHookContextDoneBeforeHeaders()
close(cancel)
// Clean up after the goroutine calling client.Do:
go func() {
if r := <-result; r.resp != nil {
testHookDidBodyClose()
r.resp.Body.Close()
}
}()
return nil, ctx.Err()
case r := <-result:
var err error
resp, err = r.resp, r.err
if err != nil {
return resp, err
}
}
c := make(chan struct{})
go func() {
select {
case <-ctx.Done():
close(cancel)
case <-c:
// The response's Body is closed.
}
}()
resp.Body = &notifyingReader{resp.Body, c}
return resp, nil
}
// Get issues a GET request via the Do function.
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Head issues a HEAD request via the Do function.
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Post issues a POST request via the Do function.
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return Do(ctx, client, req)
}
// PostForm issues a POST request via the Do function.
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
// notifyingReader is an io.ReadCloser that closes the notify channel after
// Close is called or a Read fails on the underlying ReadCloser.
type notifyingReader struct {
io.ReadCloser
notify chan<- struct{}
}
func (r *notifyingReader) Read(p []byte) (int, error) {
n, err := r.ReadCloser.Read(p)
if err != nil && r.notify != nil {
close(r.notify)
r.notify = nil
}
return n, err
}
func (r *notifyingReader) Close() error {
err := r.ReadCloser.Close()
if r.notify != nil {
close(r.notify)
r.notify = nil
}
return err
}

View File

@ -1,82 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.6
package http2
import (
"crypto/tls"
"fmt"
"net/http"
)
func configureTransport(t1 *http.Transport) (*Transport, error) {
connPool := new(clientConnPool)
t2 := &Transport{
ConnPool: noDialClientConnPool{connPool},
t1: t1,
}
connPool.t = t2
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
return nil, err
}
if t1.TLSClientConfig == nil {
t1.TLSClientConfig = new(tls.Config)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
}
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
addr := authorityAddr("https", authority)
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
go c.Close()
return erringRoundTripper{err}
} else if !used {
// Turns out we don't need this c.
// For example, two goroutines made requests to the same host
// at the same time, both kicking off TCP dials. (since protocol
// was unknown)
go c.Close()
}
return t2
}
if m := t1.TLSNextProto; len(m) == 0 {
t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
"h2": upgradeFn,
}
} else {
m["h2"] = upgradeFn
}
return t2, nil
}
// registerHTTPSProtocol calls Transport.RegisterProtocol but
// converting panics into errors.
func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}()
t.RegisterProtocol("https", rt)
return nil
}
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
// if there's already has a cached connection to the host.
// (The field is exported so it can be accessed via reflect from net/http; tested
// by TestNoDialH2RoundTripperType)
type noDialH2RoundTripper struct{ *Transport }
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res, err := rt.Transport.RoundTrip(req)
if isNoCachedConnError(err) {
return nil, http.ErrSkipAltProtocol
}
return res, err
}

View File

@ -1477,7 +1477,7 @@ func (fr *Framer) maxHeaderStringLen() int {
} }
// readMetaFrame returns 0 or more CONTINUATION frames from fr and // readMetaFrame returns 0 or more CONTINUATION frames from fr and
// merge them into into the provided hf and returns a MetaHeadersFrame // merge them into the provided hf and returns a MetaHeadersFrame
// with the decoded hpack values. // with the decoded hpack values.
func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) { func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
if fr.AllowIllegalReads { if fr.AllowIllegalReads {

View File

@ -6,19 +6,22 @@
package http2 package http2
import "net/textproto" import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *clientTrace) bool { func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
return trace != nil && trace.WroteHeaderField != nil return trace != nil && trace.WroteHeaderField != nil
} }
func traceWroteHeaderField(trace *clientTrace, k, v string) { func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
if trace != nil && trace.WroteHeaderField != nil { if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField(k, []string{v}) trace.WroteHeaderField(k, []string{v})
} }
} }
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error { func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
if trace != nil { if trace != nil {
return trace.Got1xxResponse return trace.Got1xxResponse
} }

View File

@ -1,16 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.6
package http2
import (
"net/http"
"time"
)
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
return t1.ExpectContinueTimeout
}

121
vendor/golang.org/x/net/http2/go17.go generated vendored
View File

@ -1,121 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.7
package http2
import (
"context"
"net"
"net/http"
"net/http/httptrace"
"time"
)
type contextContext interface {
context.Context
}
var errCanceled = context.Canceled
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
ctx, cancel = context.WithCancel(context.Background())
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
if hs := opts.baseConfig(); hs != nil {
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
}
return
}
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
return context.WithCancel(ctx)
}
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
return req.WithContext(ctx)
}
type clientTrace httptrace.ClientTrace
func reqContext(r *http.Request) context.Context { return r.Context() }
func (t *Transport) idleConnTimeout() time.Duration {
if t.t1 != nil {
return t.t1.IdleConnTimeout
}
return 0
}
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
func traceGetConn(req *http.Request, hostPort string) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GetConn == nil {
return
}
trace.GetConn(hostPort)
}
func traceGotConn(req *http.Request, cc *ClientConn) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GotConn == nil {
return
}
ci := httptrace.GotConnInfo{Conn: cc.tconn}
cc.mu.Lock()
ci.Reused = cc.nextStreamID > 1
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
if ci.WasIdle && !cc.lastActive.IsZero() {
ci.IdleTime = time.Now().Sub(cc.lastActive)
}
cc.mu.Unlock()
trace.GotConn(ci)
}
func traceWroteHeaders(trace *clientTrace) {
if trace != nil && trace.WroteHeaders != nil {
trace.WroteHeaders()
}
}
func traceGot100Continue(trace *clientTrace) {
if trace != nil && trace.Got100Continue != nil {
trace.Got100Continue()
}
}
func traceWait100Continue(trace *clientTrace) {
if trace != nil && trace.Wait100Continue != nil {
trace.Wait100Continue()
}
}
func traceWroteRequest(trace *clientTrace, err error) {
if trace != nil && trace.WroteRequest != nil {
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
}
}
func traceFirstResponseByte(trace *clientTrace) {
if trace != nil && trace.GotFirstResponseByte != nil {
trace.GotFirstResponseByte()
}
}
func requestTrace(req *http.Request) *clientTrace {
trace := httptrace.ContextClientTrace(req.Context())
return (*clientTrace)(trace)
}
// Ping sends a PING frame to the server and waits for the ack.
func (cc *ClientConn) Ping(ctx context.Context) error {
return cc.ping(ctx)
}
// Shutdown gracefully closes the client connection, waiting for running streams to complete.
func (cc *ClientConn) Shutdown(ctx context.Context) error {
return cc.shutdown(ctx)
}

View File

@ -1,36 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.7,!go1.8
package http2
import "crypto/tls"
// temporary copy of Go 1.7's private tls.Config.clone:
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
Renegotiation: c.Renegotiation,
}
}

View File

@ -1,56 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.8
package http2
import (
"crypto/tls"
"io"
"net/http"
)
func cloneTLSConfig(c *tls.Config) *tls.Config {
c2 := c.Clone()
c2.GetClientCertificate = c.GetClientCertificate // golang.org/issue/19264
return c2
}
var _ http.Pusher = (*responseWriter)(nil)
// Push implements http.Pusher.
func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
internalOpts := pushOptions{}
if opts != nil {
internalOpts.Method = opts.Method
internalOpts.Header = opts.Header
}
return w.push(target, internalOpts)
}
func configureServer18(h1 *http.Server, h2 *Server) error {
if h2.IdleTimeout == 0 {
if h1.IdleTimeout != 0 {
h2.IdleTimeout = h1.IdleTimeout
} else {
h2.IdleTimeout = h1.ReadTimeout
}
}
return nil
}
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil && panicValue != http.ErrAbortHandler
}
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
return req.GetBody
}
func reqBodyIsNoBody(body io.ReadCloser) bool {
return body == http.NoBody
}
func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only

View File

@ -1,16 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
package http2
import (
"net/http"
)
func configureServer19(s *http.Server, conf *Server) error {
s.RegisterOnShutdown(conf.state.startGracefulShutdown)
return nil
}

View File

@ -6,12 +6,15 @@
package http2 package http2
import "net/textproto" import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *clientTrace) bool { return false } func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false }
func traceWroteHeaderField(trace *clientTrace, k, v string) {} func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {}
func traceGot1xxResponseFunc(trace *clientTrace) func(int, textproto.MIMEHeader) error { func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
return nil return nil
} }

View File

@ -1,21 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.6
package http2
import (
"net/http"
"time"
)
func configureTransport(t1 *http.Transport) (*Transport, error) {
return nil, errTransportVersion
}
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
return 0
}

View File

@ -1,95 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.7
package http2
import (
"crypto/tls"
"errors"
"net"
"net/http"
"time"
)
type contextContext interface {
Done() <-chan struct{}
Err() error
}
var errCanceled = errors.New("canceled")
type fakeContext struct{}
func (fakeContext) Done() <-chan struct{} { return nil }
func (fakeContext) Err() error { panic("should not be called") }
func reqContext(r *http.Request) fakeContext {
return fakeContext{}
}
func setResponseUncompressed(res *http.Response) {
// Nothing.
}
type clientTrace struct{}
func requestTrace(*http.Request) *clientTrace { return nil }
func traceGetConn(*http.Request, string) {}
func traceGotConn(*http.Request, *ClientConn) {}
func traceFirstResponseByte(*clientTrace) {}
func traceWroteHeaders(*clientTrace) {}
func traceWroteRequest(*clientTrace, error) {}
func traceGot100Continue(trace *clientTrace) {}
func traceWait100Continue(trace *clientTrace) {}
func nop() {}
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
return nil, nop
}
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
return ctx, nop
}
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
return req
}
// temporary copy of Go 1.6's private tls.Config.clone:
func cloneTLSConfig(c *tls.Config) *tls.Config {
return &tls.Config{
Rand: c.Rand,
Time: c.Time,
Certificates: c.Certificates,
NameToCertificate: c.NameToCertificate,
GetCertificate: c.GetCertificate,
RootCAs: c.RootCAs,
NextProtos: c.NextProtos,
ServerName: c.ServerName,
ClientAuth: c.ClientAuth,
ClientCAs: c.ClientCAs,
InsecureSkipVerify: c.InsecureSkipVerify,
CipherSuites: c.CipherSuites,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
SessionTicketKey: c.SessionTicketKey,
ClientSessionCache: c.ClientSessionCache,
MinVersion: c.MinVersion,
MaxVersion: c.MaxVersion,
CurvePreferences: c.CurvePreferences,
}
}
func (cc *ClientConn) Ping(ctx contextContext) error {
return cc.ping(ctx)
}
func (cc *ClientConn) Shutdown(ctx contextContext) error {
return cc.shutdown(ctx)
}
func (t *Transport) idleConnTimeout() time.Duration { return 0 }

View File

@ -1,29 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.8
package http2
import (
"io"
"net/http"
)
func configureServer18(h1 *http.Server, h2 *Server) error {
// No IdleTimeout to sync prior to Go 1.8.
return nil
}
func shouldLogPanic(panicValue interface{}) bool {
return panicValue != nil
}
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
return nil
}
func reqBodyIsNoBody(io.ReadCloser) bool { return false }
func go18httpNoBody() io.ReadCloser { return nil } // for tests only

View File

@ -1,16 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.9
package http2
import (
"net/http"
)
func configureServer19(s *http.Server, conf *Server) error {
// not supported prior to go1.9
return nil
}

View File

@ -28,6 +28,7 @@ package http2
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
@ -209,12 +210,14 @@ func ConfigureServer(s *http.Server, conf *Server) error {
conf = new(Server) conf = new(Server)
} }
conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})} conf.state = &serverInternalState{activeConns: make(map[*serverConn]struct{})}
if err := configureServer18(s, conf); err != nil { if h1, h2 := s, conf; h2.IdleTimeout == 0 {
return err if h1.IdleTimeout != 0 {
} h2.IdleTimeout = h1.IdleTimeout
if err := configureServer19(s, conf); err != nil { } else {
return err h2.IdleTimeout = h1.ReadTimeout
}
} }
s.RegisterOnShutdown(conf.state.startGracefulShutdown)
if s.TLSConfig == nil { if s.TLSConfig == nil {
s.TLSConfig = new(tls.Config) s.TLSConfig = new(tls.Config)
@ -435,6 +438,15 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
sc.serve() sc.serve()
} }
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx context.Context, cancel func()) {
ctx, cancel = context.WithCancel(context.Background())
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
if hs := opts.baseConfig(); hs != nil {
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
}
return
}
func (sc *serverConn) rejectConn(err ErrCode, debug string) { func (sc *serverConn) rejectConn(err ErrCode, debug string) {
sc.vlogf("http2: server rejecting conn: %v, %s", err, debug) sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
// ignoring errors. hanging up anyway. // ignoring errors. hanging up anyway.
@ -450,7 +462,7 @@ type serverConn struct {
conn net.Conn conn net.Conn
bw *bufferedWriter // writing to conn bw *bufferedWriter // writing to conn
handler http.Handler handler http.Handler
baseCtx contextContext baseCtx context.Context
framer *Framer framer *Framer
doneServing chan struct{} // closed when serverConn.serve ends doneServing chan struct{} // closed when serverConn.serve ends
readFrameCh chan readFrameResult // written by serverConn.readFrames readFrameCh chan readFrameResult // written by serverConn.readFrames
@ -530,7 +542,7 @@ type stream struct {
id uint32 id uint32
body *pipe // non-nil if expecting DATA frames body *pipe // non-nil if expecting DATA frames
cw closeWaiter // closed wait stream transitions to closed state cw closeWaiter // closed wait stream transitions to closed state
ctx contextContext ctx context.Context
cancelCtx func() cancelCtx func()
// owned by serverConn's serve loop: // owned by serverConn's serve loop:
@ -1110,7 +1122,7 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
// errHandlerPanicked is the error given to any callers blocked in a read from // errHandlerPanicked is the error given to any callers blocked in a read from
// Request.Body when the main goroutine panics. Since most handlers read in the // Request.Body when the main goroutine panics. Since most handlers read in the
// the main ServeHTTP goroutine, this will show up rarely. // main ServeHTTP goroutine, this will show up rarely.
var errHandlerPanicked = errors.New("http2: handler panicked") var errHandlerPanicked = errors.New("http2: handler panicked")
// wroteFrame is called on the serve goroutine with the result of // wroteFrame is called on the serve goroutine with the result of
@ -1882,7 +1894,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
panic("internal error: cannot create stream with id 0") panic("internal error: cannot create stream with id 0")
} }
ctx, cancelCtx := contextWithCancel(sc.baseCtx) ctx, cancelCtx := context.WithCancel(sc.baseCtx)
st := &stream{ st := &stream{
sc: sc, sc: sc,
id: id, id: id,
@ -2048,7 +2060,7 @@ func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*r
Body: body, Body: body,
Trailer: trailer, Trailer: trailer,
} }
req = requestWithContext(req, st.ctx) req = req.WithContext(st.ctx)
rws := responseWriterStatePool.Get().(*responseWriterState) rws := responseWriterStatePool.Get().(*responseWriterState)
bwSave := rws.bw bwSave := rws.bw
@ -2076,7 +2088,7 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
stream: rw.rws.stream, stream: rw.rws.stream,
}) })
// Same as net/http: // Same as net/http:
if shouldLogPanic(e) { if e != nil && e != http.ErrAbortHandler {
const size = 64 << 10 const size = 64 << 10
buf := make([]byte, size) buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)] buf = buf[:runtime.Stack(buf, false)]
@ -2638,14 +2650,9 @@ var (
ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS") ErrPushLimitReached = errors.New("http2: push would exceed peer's SETTINGS_MAX_CONCURRENT_STREAMS")
) )
// pushOptions is the internal version of http.PushOptions, which we var _ http.Pusher = (*responseWriter)(nil)
// cannot include here because it's only defined in Go 1.8 and later.
type pushOptions struct {
Method string
Header http.Header
}
func (w *responseWriter) push(target string, opts pushOptions) error { func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
st := w.rws.stream st := w.rws.stream
sc := st.sc sc := st.sc
sc.serveG.checkNotOn() sc.serveG.checkNotOn()
@ -2656,6 +2663,10 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
return ErrRecursivePush return ErrRecursivePush
} }
if opts == nil {
opts = new(http.PushOptions)
}
// Default options. // Default options.
if opts.Method == "" { if opts.Method == "" {
opts.Method = "GET" opts.Method = "GET"

View File

@ -10,6 +10,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"context"
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
"errors" "errors"
@ -21,6 +22,7 @@ import (
mathrand "math/rand" mathrand "math/rand"
"net" "net"
"net/http" "net/http"
"net/http/httptrace"
"net/textproto" "net/textproto"
"sort" "sort"
"strconv" "strconv"
@ -118,16 +120,56 @@ func (t *Transport) disableCompression() bool {
return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression)
} }
var errTransportVersion = errors.New("http2: ConfigureTransport is only supported starting at Go 1.6")
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
// It requires Go 1.6 or later and returns an error if the net/http package is too old // It returns an error if t1 has already been HTTP/2-enabled.
// or if t1 has already been HTTP/2-enabled.
func ConfigureTransport(t1 *http.Transport) error { func ConfigureTransport(t1 *http.Transport) error {
_, err := configureTransport(t1) // in configure_transport.go (go1.6) or not_go16.go _, err := configureTransport(t1)
return err return err
} }
func configureTransport(t1 *http.Transport) (*Transport, error) {
connPool := new(clientConnPool)
t2 := &Transport{
ConnPool: noDialClientConnPool{connPool},
t1: t1,
}
connPool.t = t2
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
return nil, err
}
if t1.TLSClientConfig == nil {
t1.TLSClientConfig = new(tls.Config)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
}
if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
}
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
addr := authorityAddr("https", authority)
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
go c.Close()
return erringRoundTripper{err}
} else if !used {
// Turns out we don't need this c.
// For example, two goroutines made requests to the same host
// at the same time, both kicking off TCP dials. (since protocol
// was unknown)
go c.Close()
}
return t2
}
if m := t1.TLSNextProto; len(m) == 0 {
t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
"h2": upgradeFn,
}
} else {
m["h2"] = upgradeFn
}
return t2, nil
}
func (t *Transport) connPool() ClientConnPool { func (t *Transport) connPool() ClientConnPool {
t.connPoolOnce.Do(t.initConnPool) t.connPoolOnce.Do(t.initConnPool)
return t.connPoolOrDef return t.connPoolOrDef
@ -192,7 +234,7 @@ type ClientConn struct {
type clientStream struct { type clientStream struct {
cc *ClientConn cc *ClientConn
req *http.Request req *http.Request
trace *clientTrace // or nil trace *httptrace.ClientTrace // or nil
ID uint32 ID uint32
resc chan resAndError resc chan resAndError
bufPipe pipe // buffered pipe with the flow-controlled response payload bufPipe pipe // buffered pipe with the flow-controlled response payload
@ -226,7 +268,7 @@ type clientStream struct {
// channel to be signaled. A non-nil error is returned only if the request was // channel to be signaled. A non-nil error is returned only if the request was
// canceled. // canceled.
func awaitRequestCancel(req *http.Request, done <-chan struct{}) error { func awaitRequestCancel(req *http.Request, done <-chan struct{}) error {
ctx := reqContext(req) ctx := req.Context()
if req.Cancel == nil && ctx.Done() == nil { if req.Cancel == nil && ctx.Done() == nil {
return nil return nil
} }
@ -401,8 +443,8 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
select { select {
case <-time.After(time.Second * time.Duration(backoff)): case <-time.After(time.Second * time.Duration(backoff)):
continue continue
case <-reqContext(req).Done(): case <-req.Context().Done():
return nil, reqContext(req).Err() return nil, req.Context().Err()
} }
} }
} }
@ -439,16 +481,15 @@ func shouldRetryRequest(req *http.Request, err error, afterBodyWrite bool) (*htt
} }
// If the Body is nil (or http.NoBody), it's safe to reuse // If the Body is nil (or http.NoBody), it's safe to reuse
// this request and its Body. // this request and its Body.
if req.Body == nil || reqBodyIsNoBody(req.Body) { if req.Body == nil || req.Body == http.NoBody {
return req, nil return req, nil
} }
// If the request body can be reset back to its original // If the request body can be reset back to its original
// state via the optional req.GetBody, do that. // state via the optional req.GetBody, do that.
getBody := reqGetBody(req) // Go 1.8: getBody = req.GetBody if req.GetBody != nil {
if getBody != nil {
// TODO: consider a req.Body.Close here? or audit that all caller paths do? // TODO: consider a req.Body.Close here? or audit that all caller paths do?
body, err := getBody() body, err := req.GetBody()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -494,7 +535,7 @@ func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, er
func (t *Transport) newTLSConfig(host string) *tls.Config { func (t *Transport) newTLSConfig(host string) *tls.Config {
cfg := new(tls.Config) cfg := new(tls.Config)
if t.TLSClientConfig != nil { if t.TLSClientConfig != nil {
*cfg = *cloneTLSConfig(t.TLSClientConfig) *cfg = *t.TLSClientConfig.Clone()
} }
if !strSliceContains(cfg.NextProtos, NextProtoTLS) { if !strSliceContains(cfg.NextProtos, NextProtoTLS) {
cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...) cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)
@ -545,7 +586,7 @@ func (t *Transport) expectContinueTimeout() time.Duration {
if t.t1 == nil { if t.t1 == nil {
return 0 return 0
} }
return transportExpectContinueTimeout(t.t1) return t.t1.ExpectContinueTimeout
} }
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
@ -711,8 +752,7 @@ func (cc *ClientConn) closeIfIdle() {
var shutdownEnterWaitStateHook = func() {} var shutdownEnterWaitStateHook = func() {}
// Shutdown gracefully close the client connection, waiting for running streams to complete. // Shutdown gracefully close the client connection, waiting for running streams to complete.
// Public implementation is in go17.go and not_go17.go func (cc *ClientConn) Shutdown(ctx context.Context) error {
func (cc *ClientConn) shutdown(ctx contextContext) error {
if err := cc.sendGoAway(); err != nil { if err := cc.sendGoAway(); err != nil {
return err return err
} }
@ -882,7 +922,7 @@ func checkConnHeaders(req *http.Request) error {
// req.ContentLength, where 0 actually means zero (not unknown) and -1 // req.ContentLength, where 0 actually means zero (not unknown) and -1
// means unknown. // means unknown.
func actualContentLength(req *http.Request) int64 { func actualContentLength(req *http.Request) int64 {
if req.Body == nil || reqBodyIsNoBody(req.Body) { if req.Body == nil || req.Body == http.NoBody {
return 0 return 0
} }
if req.ContentLength != 0 { if req.ContentLength != 0 {
@ -952,7 +992,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
cs := cc.newStream() cs := cc.newStream()
cs.req = req cs.req = req
cs.trace = requestTrace(req) cs.trace = httptrace.ContextClientTrace(req.Context())
cs.requestedGzip = requestedGzip cs.requestedGzip = requestedGzip
bodyWriter := cc.t.getBodyWriterState(cs, body) bodyWriter := cc.t.getBodyWriterState(cs, body)
cs.on100 = bodyWriter.on100 cs.on100 = bodyWriter.on100
@ -990,7 +1030,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
readLoopResCh := cs.resc readLoopResCh := cs.resc
bodyWritten := false bodyWritten := false
ctx := reqContext(req) ctx := req.Context()
handleReadLoopResponse := func(re resAndError) (*http.Response, bool, error) { handleReadLoopResponse := func(re resAndError) (*http.Response, bool, error) {
res := re.res res := re.res
@ -1060,6 +1100,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
default: default:
} }
if err != nil { if err != nil {
cc.forgetStreamID(cs.ID)
return nil, cs.getStartedWrite(), err return nil, cs.getStartedWrite(), err
} }
bodyWritten = true bodyWritten = true
@ -1181,6 +1222,7 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
sawEOF = true sawEOF = true
err = nil err = nil
} else if err != nil { } else if err != nil {
cc.writeStreamReset(cs.ID, ErrCodeCancel, err)
return err return err
} }
@ -1416,7 +1458,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
return nil, errRequestHeaderListSize return nil, errRequestHeaderListSize
} }
trace := requestTrace(req) trace := httptrace.ContextClientTrace(req.Context())
traceHeaders := traceHasWroteHeaderField(trace) traceHeaders := traceHasWroteHeaderField(trace)
// Header list size is ok. Write the headers. // Header list size is ok. Write the headers.
@ -1839,7 +1881,7 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
res.Header.Del("Content-Length") res.Header.Del("Content-Length")
res.ContentLength = -1 res.ContentLength = -1
res.Body = &gzipReader{body: res.Body} res.Body = &gzipReader{body: res.Body}
setResponseUncompressed(res) res.Uncompressed = true
} }
return res, nil return res, nil
} }
@ -2216,8 +2258,7 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
} }
// Ping sends a PING frame to the server and waits for the ack. // Ping sends a PING frame to the server and waits for the ack.
// Public implementation is in go17.go and not_go17.go func (cc *ClientConn) Ping(ctx context.Context) error {
func (cc *ClientConn) ping(ctx contextContext) error {
c := make(chan struct{}) c := make(chan struct{})
// Generate a random payload // Generate a random payload
var p [8]byte var p [8]byte
@ -2451,3 +2492,91 @@ func (s bodyWriterState) scheduleBodyWrite() {
func isConnectionCloseRequest(req *http.Request) bool { func isConnectionCloseRequest(req *http.Request) bool {
return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close") return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close")
} }
// registerHTTPSProtocol calls Transport.RegisterProtocol but
// converting panics into errors.
func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}()
t.RegisterProtocol("https", rt)
return nil
}
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
// if there's already has a cached connection to the host.
// (The field is exported so it can be accessed via reflect from net/http; tested
// by TestNoDialH2RoundTripperType)
type noDialH2RoundTripper struct{ *Transport }
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
res, err := rt.Transport.RoundTrip(req)
if isNoCachedConnError(err) {
return nil, http.ErrSkipAltProtocol
}
return res, err
}
func (t *Transport) idleConnTimeout() time.Duration {
if t.t1 != nil {
return t.t1.IdleConnTimeout
}
return 0
}
func traceGetConn(req *http.Request, hostPort string) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GetConn == nil {
return
}
trace.GetConn(hostPort)
}
func traceGotConn(req *http.Request, cc *ClientConn) {
trace := httptrace.ContextClientTrace(req.Context())
if trace == nil || trace.GotConn == nil {
return
}
ci := httptrace.GotConnInfo{Conn: cc.tconn}
cc.mu.Lock()
ci.Reused = cc.nextStreamID > 1
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
if ci.WasIdle && !cc.lastActive.IsZero() {
ci.IdleTime = time.Now().Sub(cc.lastActive)
}
cc.mu.Unlock()
trace.GotConn(ci)
}
func traceWroteHeaders(trace *httptrace.ClientTrace) {
if trace != nil && trace.WroteHeaders != nil {
trace.WroteHeaders()
}
}
func traceGot100Continue(trace *httptrace.ClientTrace) {
if trace != nil && trace.Got100Continue != nil {
trace.Got100Continue()
}
}
func traceWait100Continue(trace *httptrace.ClientTrace) {
if trace != nil && trace.Wait100Continue != nil {
trace.Wait100Continue()
}
}
func traceWroteRequest(trace *httptrace.ClientTrace, err error) {
if trace != nil && trace.WroteRequest != nil {
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
}
}
func traceFirstResponseByte(trace *httptrace.ClientTrace) {
if trace != nil && trace.GotFirstResponseByte != nil {
trace.GotFirstResponseByte()
}
}

View File

@ -199,7 +199,7 @@ func (w *writeResHeaders) staysWithinBuffer(max int) bool {
// TODO: this is a common one. It'd be nice to return true // TODO: this is a common one. It'd be nice to return true
// here and get into the fast path if we could be clever and // here and get into the fast path if we could be clever and
// calculate the size fast enough, or at least a conservative // calculate the size fast enough, or at least a conservative
// uppper bound that usually fires. (Maybe if w.h and // upper bound that usually fires. (Maybe if w.h and
// w.trailers are nil, so we don't need to enumerate it.) // w.trailers are nil, so we don't need to enumerate it.)
// Otherwise I'm afraid that just calculating the length to // Otherwise I'm afraid that just calculating the length to
// answer this question would be slower than the ~2µs benefit. // answer this question would be slower than the ~2µs benefit.
@ -329,7 +329,7 @@ func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
} }
// encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k]) // encodeHeaders encodes an http.Header. If keys is not nil, then (k, h[k])
// is encoded only only if k is in keys. // is encoded only if k is in keys.
func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) { func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
if keys == nil { if keys == nil {
sorter := sorterPool.Get().(*sorter) sorter := sorterPool.Get().(*sorter)

17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s generated vendored Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !gccgo
#include "textflag.h"
//
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
//
TEXT ·syscall6(SB),NOSPLIT,$0-88
JMP syscall·syscall6(SB)
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
JMP syscall·rawSyscall6(SB)

View File

@ -15,12 +15,6 @@
// Just jump to package syscall's implementation for all these functions. // Just jump to package syscall's implementation for all these functions.
// The runtime may know about them. // The runtime may know about them.
TEXT ·Syscall(SB),NOSPLIT,$0-56
BR syscall·Syscall(SB)
TEXT ·Syscall6(SB),NOSPLIT,$0-80
BR syscall·Syscall6(SB)
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48 TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
BL runtime·entersyscall(SB) BL runtime·entersyscall(SB)
MOVD a1+8(FP), R3 MOVD a1+8(FP), R3
@ -36,12 +30,6 @@ TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
BL runtime·exitsyscall(SB) BL runtime·exitsyscall(SB)
RET RET
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
BR syscall·RawSyscall(SB)
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
BR syscall·RawSyscall6(SB)
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48 TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
MOVD a1+8(FP), R3 MOVD a1+8(FP), R3
MOVD a2+16(FP), R4 MOVD a2+16(FP), R4

View File

@ -1,98 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// mkpost processes the output of cgo -godefs to
// modify the generated types. It is used to clean up
// the sys API in an architecture specific manner.
//
// mkpost is run after cgo -godefs; see README.md.
package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"log"
"os"
"regexp"
)
func main() {
// Get the OS and architecture (using GOARCH_TARGET if it exists)
goos := os.Getenv("GOOS")
goarch := os.Getenv("GOARCH_TARGET")
if goarch == "" {
goarch = os.Getenv("GOARCH")
}
// Check that we are using the new build system if we should be.
if goos == "linux" && goarch != "sparc64" {
if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
os.Stderr.WriteString("In the new build system, mkpost should not be called directly.\n")
os.Stderr.WriteString("See README.md\n")
os.Exit(1)
}
}
b, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
// Intentionally export __val fields in Fsid and Sigset_t
valRegex := regexp.MustCompile(`type (Fsid|Sigset_t) struct {(\s+)X__val(\s+\S+\s+)}`)
b = valRegex.ReplaceAll(b, []byte("type $1 struct {${2}Val$3}"))
// If we have empty Ptrace structs, we should delete them. Only s390x emits
// nonempty Ptrace structs.
ptraceRexexp := regexp.MustCompile(`type Ptrace((Psw|Fpregs|Per) struct {\s*})`)
b = ptraceRexexp.ReplaceAll(b, nil)
// Replace the control_regs union with a blank identifier for now.
controlRegsRegex := regexp.MustCompile(`(Control_regs)\s+\[0\]uint64`)
b = controlRegsRegex.ReplaceAll(b, []byte("_ [0]uint64"))
// Remove fields that are added by glibc
// Note that this is unstable as the identifers are private.
removeFieldsRegex := regexp.MustCompile(`X__glibc\S*`)
b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
// Convert [65]int8 to [65]byte in Utsname members to simplify
// conversion to string; see golang.org/issue/20753
convertUtsnameRegex := regexp.MustCompile(`((Sys|Node|Domain)name|Release|Version|Machine)(\s+)\[(\d+)\]u?int8`)
b = convertUtsnameRegex.ReplaceAll(b, []byte("$1$3[$4]byte"))
// Remove spare fields (e.g. in Statx_t)
spareFieldsRegex := regexp.MustCompile(`X__spare\S*`)
b = spareFieldsRegex.ReplaceAll(b, []byte("_"))
// Remove cgo padding fields
removePaddingFieldsRegex := regexp.MustCompile(`Pad_cgo_\d+`)
b = removePaddingFieldsRegex.ReplaceAll(b, []byte("_"))
// Remove padding, hidden, or unused fields
removeFieldsRegex = regexp.MustCompile(`\b(X_\S+|Padding)`)
b = removeFieldsRegex.ReplaceAll(b, []byte("_"))
// Remove the first line of warning from cgo
b = b[bytes.IndexByte(b, '\n')+1:]
// Modify the command in the header to include:
// mkpost, our own warning, and a build tag.
replacement := fmt.Sprintf(`$1 | go run mkpost.go
// Code generated by the command above; see README.md. DO NOT EDIT.
// +build %s,%s`, goarch, goos)
cgoCommandRegex := regexp.MustCompile(`(cgo -godefs .*)`)
b = cgoCommandRegex.ReplaceAll(b, []byte(replacement))
// gofmt
b, err = format.Source(b)
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(b)
}

View File

@ -15,10 +15,6 @@ import (
"unsafe" "unsafe"
) )
const (
_SYS_PLEDGE = 108
)
// Pledge implements the pledge syscall. // Pledge implements the pledge syscall.
// //
// The pledge syscall does not accept execpromises on OpenBSD releases // The pledge syscall does not accept execpromises on OpenBSD releases
@ -34,15 +30,9 @@ func Pledge(promises, execpromises string) error {
return err return err
} }
// If OpenBSD <= 5.9, pledge is not available. err = pledgeAvailable(maj, min, execpromises)
if (maj == 5 && min != 9) || maj < 5 { if err != nil {
return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min) return err
}
// If OpenBSD <= 6.2 and execpromises is not empty
// return an error - execpromises is not available before 6.3
if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
} }
pptr, err := syscall.BytePtrFromString(promises) pptr, err := syscall.BytePtrFromString(promises)
@ -63,7 +53,71 @@ func Pledge(promises, execpromises string) error {
expr = unsafe.Pointer(exptr) expr = unsafe.Pointer(exptr)
} }
_, _, e := syscall.Syscall(_SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0) _, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
if e != 0 {
return e
}
return nil
}
// PledgePromises implements the pledge syscall.
//
// This changes the promises and leaves the execpromises untouched.
//
// For more information see pledge(2).
func PledgePromises(promises string) error {
maj, min, err := majmin()
if err != nil {
return err
}
err = pledgeAvailable(maj, min, "")
if err != nil {
return err
}
// This variable holds the execpromises and is always nil.
var expr unsafe.Pointer
pptr, err := syscall.BytePtrFromString(promises)
if err != nil {
return err
}
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(unsafe.Pointer(pptr)), uintptr(expr), 0)
if e != 0 {
return e
}
return nil
}
// PledgeExecpromises implements the pledge syscall.
//
// This changes the execpromises and leaves the promises untouched.
//
// For more information see pledge(2).
func PledgeExecpromises(execpromises string) error {
maj, min, err := majmin()
if err != nil {
return err
}
err = pledgeAvailable(maj, min, execpromises)
if err != nil {
return err
}
// This variable holds the promises and is always nil.
var pptr unsafe.Pointer
exptr, err := syscall.BytePtrFromString(execpromises)
if err != nil {
return err
}
_, _, e := syscall.Syscall(SYS_PLEDGE, uintptr(pptr), uintptr(unsafe.Pointer(exptr)), 0)
if e != 0 { if e != 0 {
return e return e
} }
@ -93,3 +147,20 @@ func majmin() (major int, minor int, err error) {
return return
} }
// pledgeAvailable checks for availability of the pledge(2) syscall
// based on the running OpenBSD version.
func pledgeAvailable(maj, min int, execpromises string) error {
// If OpenBSD <= 5.9, pledge is not available.
if (maj == 5 && min != 9) || maj < 5 {
return fmt.Errorf("pledge syscall is not available on OpenBSD %d.%d", maj, min)
}
// If OpenBSD <= 6.2 and execpromises is not empty,
// return an error - execpromises is not available before 6.3
if (maj < 6 || (maj == 6 && min <= 2)) && execpromises != "" {
return fmt.Errorf("cannot use execpromises on OpenBSD %d.%d", maj, min)
}
return nil
}

44
vendor/golang.org/x/sys/unix/openbsd_unveil.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build openbsd
package unix
import (
"syscall"
"unsafe"
)
// Unveil implements the unveil syscall.
// For more information see unveil(2).
// Note that the special case of blocking further
// unveil calls is handled by UnveilBlock.
func Unveil(path string, flags string) error {
pathPtr, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}
flagsPtr, err := syscall.BytePtrFromString(flags)
if err != nil {
return err
}
_, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(unsafe.Pointer(pathPtr)), uintptr(unsafe.Pointer(flagsPtr)), 0)
if e != 0 {
return e
}
return nil
}
// UnveilBlock blocks future unveil calls.
// For more information see unveil(2).
func UnveilBlock() error {
// Both pointers must be nil.
var pathUnsafe, flagsUnsafe unsafe.Pointer
_, _, e := syscall.Syscall(SYS_UNVEIL, uintptr(pathUnsafe), uintptr(flagsUnsafe), 0)
if e != 0 {
return e
}
return nil
}

View File

@ -12,7 +12,7 @@ import "unsafe"
// Round the length of a raw sockaddr up to align it properly. // Round the length of a raw sockaddr up to align it properly.
func cmsgAlignOf(salen int) int { func cmsgAlignOf(salen int) int {
salign := sizeofPtr salign := SizeofPtr
// NOTE: It seems like 64-bit Darwin, DragonFly BSD and // NOTE: It seems like 64-bit Darwin, DragonFly BSD and
// Solaris kernels still require 32-bit aligned access to // Solaris kernels still require 32-bit aligned access to
// network subsystem. // network subsystem.

View File

@ -305,11 +305,11 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
type WaitStatus uint32 type WaitStatus uint32
func (w WaitStatus) Stopped() bool { return w&0x40 != 0 } func (w WaitStatus) Stopped() bool { return w&0x40 != 0 }
func (w WaitStatus) StopSignal() syscall.Signal { func (w WaitStatus) StopSignal() Signal {
if !w.Stopped() { if !w.Stopped() {
return -1 return -1
} }
return syscall.Signal(w>>8) & 0xFF return Signal(w>>8) & 0xFF
} }
func (w WaitStatus) Exited() bool { return w&0xFF == 0 } func (w WaitStatus) Exited() bool { return w&0xFF == 0 }
@ -321,11 +321,11 @@ func (w WaitStatus) ExitStatus() int {
} }
func (w WaitStatus) Signaled() bool { return w&0x40 == 0 && w&0xFF != 0 } func (w WaitStatus) Signaled() bool { return w&0x40 == 0 && w&0xFF != 0 }
func (w WaitStatus) Signal() syscall.Signal { func (w WaitStatus) Signal() Signal {
if !w.Signaled() { if !w.Signaled() {
return -1 return -1
} }
return syscall.Signal(w>>16) & 0xFF return Signal(w>>16) & 0xFF
} }
func (w WaitStatus) Continued() bool { return w&0x01000000 != 0 } func (w WaitStatus) Continued() bool { return w&0x01000000 != 0 }
@ -383,6 +383,8 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. // FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command.
//sys FcntlFlock(fd uintptr, cmd int, lk *Flock_t) (err error) = fcntl //sys FcntlFlock(fd uintptr, cmd int, lk *Flock_t) (err error) = fcntl
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
func Flock(fd int, how int) (err error) { func Flock(fd int, how int) (err error) {
return syscall.Flock(fd, how) return syscall.Flock(fd, how)
} }
@ -396,15 +398,12 @@ func Flock(fd int, how int) (err error) {
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys Dup(oldfd int) (fd int, err error) //sys Dup(oldfd int) (fd int, err error)
//sys Dup3(oldfd int, newfd int, flags int) (err error)
//sys Exit(code int) //sys Exit(code int)
//sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error) //sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fallocate(fd int, mode uint32, off int64, len int64) (err error)
//sys Fchdir(fd int) (err error) //sys Fchdir(fd int) (err error)
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) //sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) //sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
//sys Fdatasync(fd int) (err error) //sys Fdatasync(fd int) (err error)
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
// readdir_r // readdir_r
@ -417,7 +416,7 @@ func Flock(fd int, how int) (err error) {
//sys Getpriority(which int, who int) (prio int, err error) //sys Getpriority(which int, who int) (prio int, err error)
//sysnb Getrusage(who int, rusage *Rusage) (err error) //sysnb Getrusage(who int, rusage *Rusage) (err error)
//sysnb Getsid(pid int) (sid int, err error) //sysnb Getsid(pid int) (sid int, err error)
//sysnb Kill(pid int, sig syscall.Signal) (err error) //sysnb Kill(pid int, sig Signal) (err error)
//sys Klogctl(typ int, buf []byte) (n int, err error) = syslog //sys Klogctl(typ int, buf []byte) (n int, err error) = syslog
//sys Mkdir(dirfd int, path string, mode uint32) (err error) //sys Mkdir(dirfd int, path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mkdirat(dirfd int, path string, mode uint32) (err error)
@ -429,7 +428,6 @@ func Flock(fd int, how int) (err error) {
//sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) //sys Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
//sys read(fd int, p []byte) (n int, err error) //sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error)
//sys Removexattr(path string, attr string) (err error)
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error) //sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Setdomainname(p []byte) (err error) //sys Setdomainname(p []byte) (err error)
//sys Sethostname(p []byte) (err error) //sys Sethostname(p []byte) (err error)
@ -443,7 +441,6 @@ func Flock(fd int, how int) (err error) {
//sys Setpriority(which int, who int, prio int) (err error) //sys Setpriority(which int, who int, prio int) (err error)
//sys Statx(dirfd int, path string, flags int, mask int, stat *Statx_t) (err error) //sys Statx(dirfd int, path string, flags int, mask int, stat *Statx_t) (err error)
//sys Sync() //sys Sync()
//sys Tee(rfd int, wfd int, len int, flags int) (n int64, err error)
//sysnb Times(tms *Tms) (ticks uintptr, err error) //sysnb Times(tms *Tms) (ticks uintptr, err error)
//sysnb Umask(mask int) (oldmask int) //sysnb Umask(mask int) (oldmask int)
//sysnb Uname(buf *Utsname) (err error) //sysnb Uname(buf *Utsname) (err error)
@ -451,7 +448,6 @@ func Flock(fd int, how int) (err error) {
// //sys Unmount(target string, flags int) (err error) = umount // //sys Unmount(target string, flags int) (err error) = umount
//sys Unlink(path string) (err error) //sys Unlink(path string) (err error)
//sys Unlinkat(dirfd int, path string, flags int) (err error) //sys Unlinkat(dirfd int, path string, flags int) (err error)
//sys Unshare(flags int) (err error)
//sys Ustat(dev int, ubuf *Ustat_t) (err error) //sys Ustat(dev int, ubuf *Ustat_t) (err error)
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys readlen(fd int, p *byte, np int) (n int, err error) = read //sys readlen(fd int, p *byte, np int) (n int, err error) = read
@ -537,19 +533,6 @@ func Pipe(p []int) (err error) {
return return
} }
//sysnb pipe2(p *[2]_C_int, flags int) (err error)
func Pipe2(p []int, flags int) (err error) {
if len(p) != 2 {
return EINVAL
}
var pp [2]_C_int
err = pipe2(&pp, flags)
p[0] = int(pp[0])
p[1] = int(pp[1])
return
}
//sys poll(fds *PollFd, nfds int, timeout int) (n int, err error) //sys poll(fds *PollFd, nfds int, timeout int) (n int, err error)
func Poll(fds []PollFd, timeout int) (n int, err error) { func Poll(fds []PollFd, timeout int) (n int, err error) {

View File

@ -248,11 +248,13 @@ func Uname(uname *Utsname) error {
//sys Dup(fd int) (nfd int, err error) //sys Dup(fd int) (nfd int, err error)
//sys Dup2(from int, to int) (err error) //sys Dup2(from int, to int) (err error)
//sys Exit(code int) //sys Exit(code int)
//sys Faccessat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchdir(fd int) (err error) //sys Fchdir(fd int) (err error)
//sys Fchflags(fd int, flags int) (err error) //sys Fchflags(fd int, flags int) (err error)
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) //sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
@ -280,13 +282,17 @@ func Uname(uname *Utsname) error {
//sys Kqueue() (fd int, err error) //sys Kqueue() (fd int, err error)
//sys Lchown(path string, uid int, gid int) (err error) //sys Lchown(path string, uid int, gid int) (err error)
//sys Link(path string, link string) (err error) //sys Link(path string, link string) (err error)
//sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error)
//sys Listen(s int, backlog int) (err error) //sys Listen(s int, backlog int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) //sys Lstat(path string, stat *Stat_t) (err error)
//sys Mkdir(path string, mode uint32) (err error) //sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mkfifo(path string, mode uint32) (err error) //sys Mkfifo(path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error) //sys Mknod(path string, mode uint32, dev int) (err error)
//sys Mknodat(fd int, path string, mode uint32, dev int) (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Open(path string, mode int, perm uint32) (fd int, err error)
//sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error)
//sys Pathconf(path string, name int) (val int, err error) //sys Pathconf(path string, name int) (val int, err error)
//sys read(fd int, p []byte) (n int, err error) //sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error)
@ -312,11 +318,13 @@ func Uname(uname *Utsname) error {
//sys Stat(path string, stat *Stat_t) (err error) //sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error)
//sys Symlink(path string, link string) (err error) //sys Symlink(path string, link string) (err error)
//sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error)
//sys Sync() (err error) //sys Sync() (err error)
//sys Truncate(path string, length int64) (err error) //sys Truncate(path string, length int64) (err error)
//sys Umask(newmask int) (oldmask int) //sys Umask(newmask int) (oldmask int)
//sys Undelete(path string) (err error) //sys Undelete(path string) (err error)
//sys Unlink(path string) (err error) //sys Unlink(path string) (err error)
//sys Unlinkat(dirfd int, path string, flags int) (err error)
//sys Unmount(path string, flags int) (err error) //sys Unmount(path string, flags int) (err error)
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)

View File

@ -13,9 +13,34 @@
package unix package unix
import ( import (
"sync"
"unsafe" "unsafe"
) )
const (
SYS_FSTAT_FREEBSD12 = 551 // { int fstat(int fd, _Out_ struct stat *sb); }
SYS_FSTATAT_FREEBSD12 = 552 // { int fstatat(int fd, _In_z_ char *path, \
SYS_GETDIRENTRIES_FREEBSD12 = 554 // { ssize_t getdirentries(int fd, \
SYS_STATFS_FREEBSD12 = 555 // { int statfs(_In_z_ char *path, \
SYS_FSTATFS_FREEBSD12 = 556 // { int fstatfs(int fd, \
SYS_GETFSSTAT_FREEBSD12 = 557 // { int getfsstat( \
SYS_MKNODAT_FREEBSD12 = 559 // { int mknodat(int fd, _In_z_ char *path, \
)
// See https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html.
var (
osreldateOnce sync.Once
osreldate uint32
)
// INO64_FIRST from /usr/src/lib/libc/sys/compat-ino64.h
const _ino64First = 1200031
func supportsABI(ver uint32) bool {
osreldateOnce.Do(func() { osreldate, _ = SysctlUint32("kern.osreldate") })
return osreldate >= ver
}
// SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets. // SockaddrDatalink implements the Sockaddr interface for AF_LINK type sockets.
type SockaddrDatalink struct { type SockaddrDatalink struct {
Len uint8 Len uint8
@ -121,17 +146,39 @@ func Getwd() (string, error) {
} }
func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
var _p0 unsafe.Pointer var (
var bufsize uintptr _p0 unsafe.Pointer
bufsize uintptr
oldBuf []statfs_freebsd11_t
needsConvert bool
)
if len(buf) > 0 { if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0]) if supportsABI(_ino64First) {
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) _p0 = unsafe.Pointer(&buf[0])
bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf))
} else {
n := len(buf)
oldBuf = make([]statfs_freebsd11_t, n)
_p0 = unsafe.Pointer(&oldBuf[0])
bufsize = unsafe.Sizeof(statfs_freebsd11_t{}) * uintptr(n)
needsConvert = true
}
} }
r0, _, e1 := Syscall(SYS_GETFSSTAT, uintptr(_p0), bufsize, uintptr(flags)) var sysno uintptr = SYS_GETFSSTAT
if supportsABI(_ino64First) {
sysno = SYS_GETFSSTAT_FREEBSD12
}
r0, _, e1 := Syscall(sysno, uintptr(_p0), bufsize, uintptr(flags))
n = int(r0) n = int(r0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
if e1 == 0 && needsConvert {
for i := range oldBuf {
buf[i].convertFrom(&oldBuf[i])
}
}
return return
} }
@ -225,6 +272,234 @@ func Uname(uname *Utsname) error {
return nil return nil
} }
func Stat(path string, st *Stat_t) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstatat_freebsd12(AT_FDCWD, path, st, 0)
}
err = stat(path, &oldStat)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Lstat(path string, st *Stat_t) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstatat_freebsd12(AT_FDCWD, path, st, AT_SYMLINK_NOFOLLOW)
}
err = lstat(path, &oldStat)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Fstat(fd int, st *Stat_t) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstat_freebsd12(fd, st)
}
err = fstat(fd, &oldStat)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Fstatat(fd int, path string, st *Stat_t, flags int) (err error) {
var oldStat stat_freebsd11_t
if supportsABI(_ino64First) {
return fstatat_freebsd12(fd, path, st, flags)
}
err = fstatat(fd, path, &oldStat, flags)
if err != nil {
return err
}
st.convertFrom(&oldStat)
return nil
}
func Statfs(path string, st *Statfs_t) (err error) {
var oldStatfs statfs_freebsd11_t
if supportsABI(_ino64First) {
return statfs_freebsd12(path, st)
}
err = statfs(path, &oldStatfs)
if err != nil {
return err
}
st.convertFrom(&oldStatfs)
return nil
}
func Fstatfs(fd int, st *Statfs_t) (err error) {
var oldStatfs statfs_freebsd11_t
if supportsABI(_ino64First) {
return fstatfs_freebsd12(fd, st)
}
err = fstatfs(fd, &oldStatfs)
if err != nil {
return err
}
st.convertFrom(&oldStatfs)
return nil
}
func Getdents(fd int, buf []byte) (n int, err error) {
return Getdirentries(fd, buf, nil)
}
func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
if supportsABI(_ino64First) {
return getdirentries_freebsd12(fd, buf, basep)
}
// The old syscall entries are smaller than the new. Use 1/4 of the original
// buffer size rounded up to DIRBLKSIZ (see /usr/src/lib/libc/sys/getdirentries.c).
oldBufLen := roundup(len(buf)/4, _dirblksiz)
oldBuf := make([]byte, oldBufLen)
n, err = getdirentries(fd, oldBuf, basep)
if err == nil && n > 0 {
n = convertFromDirents11(buf, oldBuf[:n])
}
return
}
func Mknod(path string, mode uint32, dev uint64) (err error) {
var oldDev int
if supportsABI(_ino64First) {
return mknodat_freebsd12(AT_FDCWD, path, mode, dev)
}
oldDev = int(dev)
return mknod(path, mode, oldDev)
}
func Mknodat(fd int, path string, mode uint32, dev uint64) (err error) {
var oldDev int
if supportsABI(_ino64First) {
return mknodat_freebsd12(fd, path, mode, dev)
}
oldDev = int(dev)
return mknodat(fd, path, mode, oldDev)
}
// round x to the nearest multiple of y, larger or equal to x.
//
// from /usr/include/sys/param.h Macros for counting and rounding.
// #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
func roundup(x, y int) int {
return ((x + y - 1) / y) * y
}
func (s *Stat_t) convertFrom(old *stat_freebsd11_t) {
*s = Stat_t{
Dev: uint64(old.Dev),
Ino: uint64(old.Ino),
Nlink: uint64(old.Nlink),
Mode: old.Mode,
Uid: old.Uid,
Gid: old.Gid,
Rdev: uint64(old.Rdev),
Atim: old.Atim,
Mtim: old.Mtim,
Ctim: old.Ctim,
Birthtim: old.Birthtim,
Size: old.Size,
Blocks: old.Blocks,
Blksize: old.Blksize,
Flags: old.Flags,
Gen: uint64(old.Gen),
}
}
func (s *Statfs_t) convertFrom(old *statfs_freebsd11_t) {
*s = Statfs_t{
Version: _statfsVersion,
Type: old.Type,
Flags: old.Flags,
Bsize: old.Bsize,
Iosize: old.Iosize,
Blocks: old.Blocks,
Bfree: old.Bfree,
Bavail: old.Bavail,
Files: old.Files,
Ffree: old.Ffree,
Syncwrites: old.Syncwrites,
Asyncwrites: old.Asyncwrites,
Syncreads: old.Syncreads,
Asyncreads: old.Asyncreads,
// Spare
Namemax: old.Namemax,
Owner: old.Owner,
Fsid: old.Fsid,
// Charspare
// Fstypename
// Mntfromname
// Mntonname
}
sl := old.Fstypename[:]
n := clen(*(*[]byte)(unsafe.Pointer(&sl)))
copy(s.Fstypename[:], old.Fstypename[:n])
sl = old.Mntfromname[:]
n = clen(*(*[]byte)(unsafe.Pointer(&sl)))
copy(s.Mntfromname[:], old.Mntfromname[:n])
sl = old.Mntonname[:]
n = clen(*(*[]byte)(unsafe.Pointer(&sl)))
copy(s.Mntonname[:], old.Mntonname[:n])
}
func convertFromDirents11(buf []byte, old []byte) int {
const (
fixedSize = int(unsafe.Offsetof(Dirent{}.Name))
oldFixedSize = int(unsafe.Offsetof(dirent_freebsd11{}.Name))
)
dstPos := 0
srcPos := 0
for dstPos+fixedSize < len(buf) && srcPos+oldFixedSize < len(old) {
dstDirent := (*Dirent)(unsafe.Pointer(&buf[dstPos]))
srcDirent := (*dirent_freebsd11)(unsafe.Pointer(&old[srcPos]))
reclen := roundup(fixedSize+int(srcDirent.Namlen)+1, 8)
if dstPos+reclen > len(buf) {
break
}
dstDirent.Fileno = uint64(srcDirent.Fileno)
dstDirent.Off = 0
dstDirent.Reclen = uint16(reclen)
dstDirent.Type = srcDirent.Type
dstDirent.Pad0 = 0
dstDirent.Namlen = uint16(srcDirent.Namlen)
dstDirent.Pad1 = 0
copy(dstDirent.Name[:], srcDirent.Name[:srcDirent.Namlen])
padding := buf[dstPos+fixedSize+int(dstDirent.Namlen) : dstPos+reclen]
for i := range padding {
padding[i] = 0
}
dstPos += int(dstDirent.Reclen)
srcPos += int(srcDirent.Reclen)
}
return dstPos
}
/* /*
* Exposed directly * Exposed directly
*/ */
@ -264,13 +539,16 @@ func Uname(uname *Utsname) error {
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) //sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys fstat(fd int, stat *stat_freebsd11_t) (err error)
//sys Fstatat(fd int, path string, stat *Stat_t, flags int) (err error) //sys fstat_freebsd12(fd int, stat *Stat_t) (err error)
//sys Fstatfs(fd int, stat *Statfs_t) (err error) //sys fstatat(fd int, path string, stat *stat_freebsd11_t, flags int) (err error)
//sys fstatat_freebsd12(fd int, path string, stat *Stat_t, flags int) (err error)
//sys fstatfs(fd int, stat *statfs_freebsd11_t) (err error)
//sys fstatfs_freebsd12(fd int, stat *Statfs_t) (err error)
//sys Fsync(fd int) (err error) //sys Fsync(fd int) (err error)
//sys Ftruncate(fd int, length int64) (err error) //sys Ftruncate(fd int, length int64) (err error)
//sys Getdents(fd int, buf []byte) (n int, err error) //sys getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) //sys getdirentries_freebsd12(fd int, buf []byte, basep *uintptr) (n int, err error)
//sys Getdtablesize() (size int) //sys Getdtablesize() (size int)
//sysnb Getegid() (egid int) //sysnb Getegid() (egid int)
//sysnb Geteuid() (uid int) //sysnb Geteuid() (uid int)
@ -292,11 +570,13 @@ func Uname(uname *Utsname) error {
//sys Link(path string, link string) (err error) //sys Link(path string, link string) (err error)
//sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error) //sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error)
//sys Listen(s int, backlog int) (err error) //sys Listen(s int, backlog int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) //sys lstat(path string, stat *stat_freebsd11_t) (err error)
//sys Mkdir(path string, mode uint32) (err error) //sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error) //sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mkfifo(path string, mode uint32) (err error) //sys Mkfifo(path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error) //sys mknod(path string, mode uint32, dev int) (err error)
//sys mknodat(fd int, path string, mode uint32, dev int) (err error)
//sys mknodat_freebsd12(fd int, path string, mode uint32, dev uint64) (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Open(path string, mode int, perm uint32) (fd int, err error)
//sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error) //sys Openat(fdat int, path string, mode int, perm uint32) (fd int, err error)
@ -326,8 +606,9 @@ func Uname(uname *Utsname) error {
//sysnb Setsid() (pid int, err error) //sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tp *Timeval) (err error) //sysnb Settimeofday(tp *Timeval) (err error)
//sysnb Setuid(uid int) (err error) //sysnb Setuid(uid int) (err error)
//sys Stat(path string, stat *Stat_t) (err error) //sys stat(path string, stat *stat_freebsd11_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error) //sys statfs(path string, stat *statfs_freebsd11_t) (err error)
//sys statfs_freebsd12(path string, stat *Statfs_t) (err error)
//sys Symlink(path string, link string) (err error) //sys Symlink(path string, link string) (err error)
//sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error) //sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error)
//sys Sync() (err error) //sys Sync() (err error)
@ -382,6 +663,7 @@ func Uname(uname *Utsname) error {
// Kqueue_portset // Kqueue_portset
// Getattrlist // Getattrlist
// Setattrlist // Setattrlist
// Getdents
// Getdirentriesattr // Getdirentriesattr
// Searchfs // Searchfs
// Delete // Delete

View File

@ -12,6 +12,8 @@
package unix package unix
import ( import (
"encoding/binary"
"net"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -55,6 +57,15 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
// ioctl itself should not be exposed directly, but additional get/set // ioctl itself should not be exposed directly, but additional get/set
// functions for specific types are permissible. // functions for specific types are permissible.
// IoctlSetPointerInt performs an ioctl operation which sets an
// integer value on fd, using the specified request number. The ioctl
// argument is called with a pointer to the integer value, rather than
// passing the integer value directly.
func IoctlSetPointerInt(fd int, req uint, value int) error {
v := int32(value)
return ioctl(fd, req, uintptr(unsafe.Pointer(&v)))
}
// IoctlSetInt performs an ioctl operation which sets an integer value // IoctlSetInt performs an ioctl operation which sets an integer value
// on fd, using the specified request number. // on fd, using the specified request number.
func IoctlSetInt(fd int, req uint, value int) error { func IoctlSetInt(fd int, req uint, value int) error {
@ -692,6 +703,69 @@ func (sa *SockaddrVM) sockaddr() (unsafe.Pointer, _Socklen, error) {
return unsafe.Pointer(&sa.raw), SizeofSockaddrVM, nil return unsafe.Pointer(&sa.raw), SizeofSockaddrVM, nil
} }
type SockaddrXDP struct {
Flags uint16
Ifindex uint32
QueueID uint32
SharedUmemFD uint32
raw RawSockaddrXDP
}
func (sa *SockaddrXDP) sockaddr() (unsafe.Pointer, _Socklen, error) {
sa.raw.Family = AF_XDP
sa.raw.Flags = sa.Flags
sa.raw.Ifindex = sa.Ifindex
sa.raw.Queue_id = sa.QueueID
sa.raw.Shared_umem_fd = sa.SharedUmemFD
return unsafe.Pointer(&sa.raw), SizeofSockaddrXDP, nil
}
// This constant mirrors the #define of PX_PROTO_OE in
// linux/if_pppox.h. We're defining this by hand here instead of
// autogenerating through mkerrors.sh because including
// linux/if_pppox.h causes some declaration conflicts with other
// includes (linux/if_pppox.h includes linux/in.h, which conflicts
// with netinet/in.h). Given that we only need a single zero constant
// out of that file, it's cleaner to just define it by hand here.
const px_proto_oe = 0
type SockaddrPPPoE struct {
SID uint16
Remote net.HardwareAddr
Dev string
raw RawSockaddrPPPoX
}
func (sa *SockaddrPPPoE) sockaddr() (unsafe.Pointer, _Socklen, error) {
if len(sa.Remote) != 6 {
return nil, 0, EINVAL
}
if len(sa.Dev) > IFNAMSIZ-1 {
return nil, 0, EINVAL
}
*(*uint16)(unsafe.Pointer(&sa.raw[0])) = AF_PPPOX
// This next field is in host-endian byte order. We can't use the
// same unsafe pointer cast as above, because this value is not
// 32-bit aligned and some architectures don't allow unaligned
// access.
//
// However, the value of px_proto_oe is 0, so we can use
// encoding/binary helpers to write the bytes without worrying
// about the ordering.
binary.BigEndian.PutUint32(sa.raw[2:6], px_proto_oe)
// This field is deliberately big-endian, unlike the previous
// one. The kernel expects SID to be in network byte order.
binary.BigEndian.PutUint16(sa.raw[6:8], sa.SID)
copy(sa.raw[8:14], sa.Remote)
for i := 14; i < 14+IFNAMSIZ; i++ {
sa.raw[i] = 0
}
copy(sa.raw[14:], sa.Dev)
return unsafe.Pointer(&sa.raw), SizeofSockaddrPPPoX, nil
}
func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
switch rsa.Addr.Family { switch rsa.Addr.Family {
case AF_NETLINK: case AF_NETLINK:
@ -793,6 +867,31 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) {
} }
return sa, nil return sa, nil
} }
case AF_XDP:
pp := (*RawSockaddrXDP)(unsafe.Pointer(rsa))
sa := &SockaddrXDP{
Flags: pp.Flags,
Ifindex: pp.Ifindex,
QueueID: pp.Queue_id,
SharedUmemFD: pp.Shared_umem_fd,
}
return sa, nil
case AF_PPPOX:
pp := (*RawSockaddrPPPoX)(unsafe.Pointer(rsa))
if binary.BigEndian.Uint32(pp[2:6]) != px_proto_oe {
return nil, EINVAL
}
sa := &SockaddrPPPoE{
SID: binary.BigEndian.Uint16(pp[6:8]),
Remote: net.HardwareAddr(pp[8:14]),
}
for i := 14; i < 14+IFNAMSIZ; i++ {
if pp[i] == 0 {
sa.Dev = string(pp[14:i])
break
}
}
return sa, nil
} }
return nil, EAFNOSUPPORT return nil, EAFNOSUPPORT
} }
@ -1095,7 +1194,7 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
// The ptrace syscall differs from glibc's ptrace. // The ptrace syscall differs from glibc's ptrace.
// Peeks returns the word in *data, not as the return value. // Peeks returns the word in *data, not as the return value.
var buf [sizeofPtr]byte var buf [SizeofPtr]byte
// Leading edge. PEEKTEXT/PEEKDATA don't require aligned // Leading edge. PEEKTEXT/PEEKDATA don't require aligned
// access (PEEKUSER warns that it might), but if we don't // access (PEEKUSER warns that it might), but if we don't
@ -1103,12 +1202,12 @@ func ptracePeek(req int, pid int, addr uintptr, out []byte) (count int, err erro
// boundary and not get the bytes leading up to the page // boundary and not get the bytes leading up to the page
// boundary. // boundary.
n := 0 n := 0
if addr%sizeofPtr != 0 { if addr%SizeofPtr != 0 {
err = ptrace(req, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) err = ptrace(req, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
if err != nil { if err != nil {
return 0, err return 0, err
} }
n += copy(out, buf[addr%sizeofPtr:]) n += copy(out, buf[addr%SizeofPtr:])
out = out[n:] out = out[n:]
} }
@ -1146,15 +1245,15 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
// Leading edge. // Leading edge.
n := 0 n := 0
if addr%sizeofPtr != 0 { if addr%SizeofPtr != 0 {
var buf [sizeofPtr]byte var buf [SizeofPtr]byte
err = ptrace(peekReq, pid, addr-addr%sizeofPtr, uintptr(unsafe.Pointer(&buf[0]))) err = ptrace(peekReq, pid, addr-addr%SizeofPtr, uintptr(unsafe.Pointer(&buf[0])))
if err != nil { if err != nil {
return 0, err return 0, err
} }
n += copy(buf[addr%sizeofPtr:], data) n += copy(buf[addr%SizeofPtr:], data)
word := *((*uintptr)(unsafe.Pointer(&buf[0]))) word := *((*uintptr)(unsafe.Pointer(&buf[0])))
err = ptrace(pokeReq, pid, addr-addr%sizeofPtr, word) err = ptrace(pokeReq, pid, addr-addr%SizeofPtr, word)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -1162,19 +1261,19 @@ func ptracePoke(pokeReq int, peekReq int, pid int, addr uintptr, data []byte) (c
} }
// Interior. // Interior.
for len(data) > sizeofPtr { for len(data) > SizeofPtr {
word := *((*uintptr)(unsafe.Pointer(&data[0]))) word := *((*uintptr)(unsafe.Pointer(&data[0])))
err = ptrace(pokeReq, pid, addr+uintptr(n), word) err = ptrace(pokeReq, pid, addr+uintptr(n), word)
if err != nil { if err != nil {
return n, err return n, err
} }
n += sizeofPtr n += SizeofPtr
data = data[sizeofPtr:] data = data[SizeofPtr:]
} }
// Trailing edge. // Trailing edge.
if len(data) > 0 { if len(data) > 0 {
var buf [sizeofPtr]byte var buf [SizeofPtr]byte
err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0]))) err = ptrace(peekReq, pid, addr+uintptr(n), uintptr(unsafe.Pointer(&buf[0])))
if err != nil { if err != nil {
return n, err return n, err
@ -1273,9 +1372,11 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sys Adjtimex(buf *Timex) (state int, err error) //sys Adjtimex(buf *Timex) (state int, err error)
//sys Chdir(path string) (err error) //sys Chdir(path string) (err error)
//sys Chroot(path string) (err error) //sys Chroot(path string) (err error)
//sys ClockGetres(clockid int32, res *Timespec) (err error)
//sys ClockGettime(clockid int32, time *Timespec) (err error) //sys ClockGettime(clockid int32, time *Timespec) (err error)
//sys Close(fd int) (err error) //sys Close(fd int) (err error)
//sys CopyFileRange(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error) //sys CopyFileRange(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int, err error)
//sys DeleteModule(name string, flags int) (err error)
//sys Dup(oldfd int) (fd int, err error) //sys Dup(oldfd int) (fd int, err error)
//sys Dup3(oldfd int, newfd int, flags int) (err error) //sys Dup3(oldfd int, newfd int, flags int) (err error)
//sysnb EpollCreate1(flag int) (fd int, err error) //sysnb EpollCreate1(flag int) (fd int, err error)
@ -1289,6 +1390,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sys fcntl(fd int, cmd int, arg int) (val int, err error) //sys fcntl(fd int, cmd int, arg int) (val int, err error)
//sys Fdatasync(fd int) (err error) //sys Fdatasync(fd int) (err error)
//sys Fgetxattr(fd int, attr string, dest []byte) (sz int, err error) //sys Fgetxattr(fd int, attr string, dest []byte) (sz int, err error)
//sys FinitModule(fd int, params string, flags int) (err error)
//sys Flistxattr(fd int, dest []byte) (sz int, err error) //sys Flistxattr(fd int, dest []byte) (sz int, err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fremovexattr(fd int, attr string) (err error) //sys Fremovexattr(fd int, attr string) (err error)
@ -1310,6 +1412,7 @@ func Getpgrp() (pid int) {
//sysnb Getsid(pid int) (sid int, err error) //sysnb Getsid(pid int) (sid int, err error)
//sysnb Gettid() (tid int) //sysnb Gettid() (tid int)
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error) //sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
//sys InitModule(moduleImage []byte, params string) (err error)
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) //sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
//sysnb InotifyInit1(flags int) (fd int, err error) //sysnb InotifyInit1(flags int) (fd int, err error)
//sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) //sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error)
@ -1496,12 +1599,9 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
// Brk // Brk
// Capget // Capget
// Capset // Capset
// ClockGetres
// ClockNanosleep // ClockNanosleep
// ClockSettime // ClockSettime
// Clone // Clone
// CreateModule
// DeleteModule
// EpollCtlOld // EpollCtlOld
// EpollPwait // EpollPwait
// EpollWaitOld // EpollWaitOld
@ -1545,7 +1645,6 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
// Pselect6 // Pselect6
// Ptrace // Ptrace
// Putpmsg // Putpmsg
// QueryModule
// Quotactl // Quotactl
// Readahead // Readahead
// Readv // Readv

View File

@ -160,3 +160,16 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
} }
return poll(&fds[0], len(fds), timeout) return poll(&fds[0], len(fds), timeout)
} }
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error)
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error {
cmdlineLen := len(cmdline)
if cmdlineLen > 0 {
// Account for the additional NULL byte added by
// BytePtrFromString in kexecFileLoad. The kexec_file_load
// syscall expects a NULL-terminated string.
cmdlineLen++
}
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
}

View File

@ -191,12 +191,9 @@ func Dup2(oldfd int, newfd int) (err error) {
return Dup3(oldfd, newfd, 0) return Dup3(oldfd, newfd, 0)
} }
func Pause() (err error) { func Pause() error {
_, _, e1 := Syscall6(SYS_PPOLL, 0, 0, 0, 0, 0, 0) _, err := ppoll(nil, 0, nil, nil)
if e1 != 0 { return err
err = errnoErr(e1)
}
return
} }
func Poll(fds []PollFd, timeout int) (n int, err error) { func Poll(fds []PollFd, timeout int) (n int, err error) {

View File

@ -136,3 +136,16 @@ func SyncFileRange(fd int, off int64, n int64, flags int) error {
// order of their arguments. // order of their arguments.
return syncFileRange2(fd, flags, off, n) return syncFileRange2(fd, flags, off, n)
} }
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error)
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error {
cmdlineLen := len(cmdline)
if cmdlineLen > 0 {
// Account for the additional NULL byte added by
// BytePtrFromString in kexecFileLoad. The kexec_file_load
// syscall expects a NULL-terminated string.
cmdlineLen++
}
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
}

View File

@ -191,12 +191,9 @@ func Dup2(oldfd int, newfd int) (err error) {
return Dup3(oldfd, newfd, 0) return Dup3(oldfd, newfd, 0)
} }
func Pause() (err error) { func Pause() error {
_, _, e1 := Syscall6(SYS_PPOLL, 0, 0, 0, 0, 0, 0) _, err := ppoll(nil, 0, nil, nil)
if e1 != 0 { return err
err = errnoErr(e1)
}
return
} }
func Poll(fds []PollFd, timeout int) (n int, err error) { func Poll(fds []PollFd, timeout int) (n int, err error) {

View File

@ -322,3 +322,16 @@ func Poll(fds []PollFd, timeout int) (n int, err error) {
} }
return poll(&fds[0], len(fds), timeout) return poll(&fds[0], len(fds), timeout)
} }
//sys kexecFileLoad(kernelFd int, initrdFd int, cmdlineLen int, cmdline string, flags int) (err error)
func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error {
cmdlineLen := len(cmdline)
if cmdlineLen > 0 {
// Account for the additional NULL byte added by
// BytePtrFromString in kexecFileLoad. The kexec_file_load
// syscall expects a NULL-terminated string.
cmdlineLen++
}
return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
}

View File

@ -13,6 +13,7 @@
package unix package unix
import ( import (
"runtime"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -93,6 +94,23 @@ func nametomib(name string) (mib []_C_int, err error) {
return mib, nil return mib, nil
} }
func SysctlClockinfo(name string) (*Clockinfo, error) {
mib, err := sysctlmib(name)
if err != nil {
return nil, err
}
n := uintptr(SizeofClockinfo)
var ci Clockinfo
if err := sysctl(mib, (*byte)(unsafe.Pointer(&ci)), &n, nil, 0); err != nil {
return nil, err
}
if n != SizeofClockinfo {
return nil, EIO
}
return &ci, nil
}
//sysnb pipe() (fd1 int, fd2 int, err error) //sysnb pipe() (fd1 int, fd2 int, err error)
func Pipe(p []int) (err error) { func Pipe(p []int) (err error) {
if len(p) != 2 { if len(p) != 2 {
@ -173,6 +191,13 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
return &value, err return &value, err
} }
func IoctlGetPtmget(fd int, req uint) (*Ptmget, error) {
var value Ptmget
err := ioctl(fd, req, uintptr(unsafe.Pointer(&value)))
runtime.KeepAlive(value)
return &value, err
}
func Uname(uname *Utsname) error { func Uname(uname *Utsname) error {
mib := []_C_int{CTL_KERN, KERN_OSTYPE} mib := []_C_int{CTL_KERN, KERN_OSTYPE}
n := unsafe.Sizeof(uname.Sysname) n := unsafe.Sizeof(uname.Sysname)
@ -252,6 +277,7 @@ func Uname(uname *Utsname) error {
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) //sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
@ -276,19 +302,26 @@ func Uname(uname *Utsname) error {
//sys Kqueue() (fd int, err error) //sys Kqueue() (fd int, err error)
//sys Lchown(path string, uid int, gid int) (err error) //sys Lchown(path string, uid int, gid int) (err error)
//sys Link(path string, link string) (err error) //sys Link(path string, link string) (err error)
//sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error)
//sys Listen(s int, backlog int) (err error) //sys Listen(s int, backlog int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) //sys Lstat(path string, stat *Stat_t) (err error)
//sys Mkdir(path string, mode uint32) (err error) //sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mkfifo(path string, mode uint32) (err error) //sys Mkfifo(path string, mode uint32) (err error)
//sys Mkfifoat(dirfd int, path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error) //sys Mknod(path string, mode uint32, dev int) (err error)
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Open(path string, mode int, perm uint32) (fd int, err error)
//sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error)
//sys Pathconf(path string, name int) (val int, err error) //sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) //sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
//sys read(fd int, p []byte) (n int, err error) //sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error)
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error) //sys Rename(from string, to string) (err error)
//sys Renameat(fromfd int, from string, tofd int, to string) (err error)
//sys Revoke(path string) (err error) //sys Revoke(path string) (err error)
//sys Rmdir(path string) (err error) //sys Rmdir(path string) (err error)
//sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK //sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK
@ -306,10 +339,12 @@ func Uname(uname *Utsname) error {
//sysnb Setuid(uid int) (err error) //sysnb Setuid(uid int) (err error)
//sys Stat(path string, stat *Stat_t) (err error) //sys Stat(path string, stat *Stat_t) (err error)
//sys Symlink(path string, link string) (err error) //sys Symlink(path string, link string) (err error)
//sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error)
//sys Sync() (err error) //sys Sync() (err error)
//sys Truncate(path string, length int64) (err error) //sys Truncate(path string, length int64) (err error)
//sys Umask(newmask int) (oldmask int) //sys Umask(newmask int) (oldmask int)
//sys Unlink(path string) (err error) //sys Unlink(path string) (err error)
//sys Unlinkat(dirfd int, path string, flags int) (err error)
//sys Unmount(path string, flags int) (err error) //sys Unmount(path string, flags int) (err error)
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)

View File

@ -43,6 +43,23 @@ func nametomib(name string) (mib []_C_int, err error) {
return nil, EINVAL return nil, EINVAL
} }
func SysctlUvmexp(name string) (*Uvmexp, error) {
mib, err := sysctlmib(name)
if err != nil {
return nil, err
}
n := uintptr(SizeofUvmexp)
var u Uvmexp
if err := sysctl(mib, (*byte)(unsafe.Pointer(&u)), &n, nil, 0); err != nil {
return nil, err
}
if n != SizeofUvmexp {
return nil, EIO
}
return &u, nil
}
//sysnb pipe(p *[2]_C_int) (err error) //sysnb pipe(p *[2]_C_int) (err error)
func Pipe(p []int) (err error) { func Pipe(p []int) (err error) {
if len(p) != 2 { if len(p) != 2 {
@ -141,6 +158,15 @@ func IoctlGetTermios(fd int, req uint) (*Termios, error) {
return &value, err return &value, err
} }
//sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error)
func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
if len(fds) == 0 {
return ppoll(nil, 0, timeout, sigmask)
}
return ppoll(&fds[0], len(fds), timeout, sigmask)
}
func Uname(uname *Utsname) error { func Uname(uname *Utsname) error {
mib := []_C_int{CTL_KERN, KERN_OSTYPE} mib := []_C_int{CTL_KERN, KERN_OSTYPE}
n := unsafe.Sizeof(uname.Sysname) n := unsafe.Sizeof(uname.Sysname)
@ -207,6 +233,7 @@ func Uname(uname *Utsname) error {
//sys Fchmod(fd int, mode uint32) (err error) //sys Fchmod(fd int, mode uint32) (err error)
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) //sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
//sys Fchown(fd int, uid int, gid int) (err error) //sys Fchown(fd int, uid int, gid int) (err error)
//sys Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error)
//sys Flock(fd int, how int) (err error) //sys Flock(fd int, how int) (err error)
//sys Fpathconf(fd int, name int) (val int, err error) //sys Fpathconf(fd int, name int) (val int, err error)
//sys Fstat(fd int, stat *Stat_t) (err error) //sys Fstat(fd int, stat *Stat_t) (err error)
@ -233,19 +260,26 @@ func Uname(uname *Utsname) error {
//sys Kqueue() (fd int, err error) //sys Kqueue() (fd int, err error)
//sys Lchown(path string, uid int, gid int) (err error) //sys Lchown(path string, uid int, gid int) (err error)
//sys Link(path string, link string) (err error) //sys Link(path string, link string) (err error)
//sys Linkat(pathfd int, path string, linkfd int, link string, flags int) (err error)
//sys Listen(s int, backlog int) (err error) //sys Listen(s int, backlog int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error) //sys Lstat(path string, stat *Stat_t) (err error)
//sys Mkdir(path string, mode uint32) (err error) //sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mkfifo(path string, mode uint32) (err error) //sys Mkfifo(path string, mode uint32) (err error)
//sys Mkfifoat(dirfd int, path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error) //sys Mknod(path string, mode uint32, dev int) (err error)
//sys Mknodat(dirfd int, path string, mode uint32, dev int) (err error)
//sys Nanosleep(time *Timespec, leftover *Timespec) (err error) //sys Nanosleep(time *Timespec, leftover *Timespec) (err error)
//sys Open(path string, mode int, perm uint32) (fd int, err error) //sys Open(path string, mode int, perm uint32) (fd int, err error)
//sys Openat(dirfd int, path string, mode int, perm uint32) (fd int, err error)
//sys Pathconf(path string, name int) (val int, err error) //sys Pathconf(path string, name int) (val int, err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) //sys Pread(fd int, p []byte, offset int64) (n int, err error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) //sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
//sys read(fd int, p []byte) (n int, err error) //sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error) //sys Readlink(path string, buf []byte) (n int, err error)
//sys Readlinkat(dirfd int, path string, buf []byte) (n int, err error)
//sys Rename(from string, to string) (err error) //sys Rename(from string, to string) (err error)
//sys Renameat(fromfd int, from string, tofd int, to string) (err error)
//sys Revoke(path string) (err error) //sys Revoke(path string) (err error)
//sys Rmdir(path string) (err error) //sys Rmdir(path string) (err error)
//sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK //sys Seek(fd int, offset int64, whence int) (newoffset int64, err error) = SYS_LSEEK
@ -268,10 +302,12 @@ func Uname(uname *Utsname) error {
//sys Stat(path string, stat *Stat_t) (err error) //sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, stat *Statfs_t) (err error) //sys Statfs(path string, stat *Statfs_t) (err error)
//sys Symlink(path string, link string) (err error) //sys Symlink(path string, link string) (err error)
//sys Symlinkat(oldpath string, newdirfd int, newpath string) (err error)
//sys Sync() (err error) //sys Sync() (err error)
//sys Truncate(path string, length int64) (err error) //sys Truncate(path string, length int64) (err error)
//sys Umask(newmask int) (oldmask int) //sys Umask(newmask int) (oldmask int)
//sys Unlink(path string) (err error) //sys Unlink(path string) (err error)
//sys Unlinkat(dirfd int, path string, flags int) (err error)
//sys Unmount(path string, flags int) (err error) //sys Unmount(path string, flags int) (err error)
//sys write(fd int, p []byte) (n int, err error) //sys write(fd int, p []byte) (n int, err error)
//sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error)
@ -294,15 +330,11 @@ func Uname(uname *Utsname) error {
// clock_settime // clock_settime
// closefrom // closefrom
// execve // execve
// faccessat
// fchmodat
// fchownat
// fcntl // fcntl
// fhopen // fhopen
// fhstat // fhstat
// fhstatfs // fhstatfs
// fork // fork
// fstatat
// futimens // futimens
// getfh // getfh
// getgid // getgid
@ -316,12 +348,8 @@ func Uname(uname *Utsname) error {
// lfs_markv // lfs_markv
// lfs_segclean // lfs_segclean
// lfs_segwait // lfs_segwait
// linkat
// mincore // mincore
// minherit // minherit
// mkdirat
// mkfifoat
// mknodat
// mount // mount
// mquery // mquery
// msgctl // msgctl
@ -330,12 +358,10 @@ func Uname(uname *Utsname) error {
// msgsnd // msgsnd
// nfssvc // nfssvc
// nnpfspioctl // nnpfspioctl
// openat
// preadv // preadv
// profil // profil
// pwritev // pwritev
// quotactl // quotactl
// readlinkat
// readv // readv
// reboot // reboot
// renameat // renameat
@ -356,13 +382,11 @@ func Uname(uname *Utsname) error {
// sigprocmask // sigprocmask
// sigreturn // sigreturn
// sigsuspend // sigsuspend
// symlinkat
// sysarch // sysarch
// syscall // syscall
// threxit // threxit
// thrsigdivert // thrsigdivert
// thrsleep // thrsleep
// thrwakeup // thrwakeup
// unlinkat
// vfork // vfork
// writev // writev

View File

@ -31,3 +31,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
func (cmsg *Cmsghdr) SetLen(length int) { func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
// SYS___SYSCTL is used by syscall_bsd.go for all BSDs, but in modern versions
// of openbsd/386 the syscall is called sysctl instead of __sysctl.
const SYS___SYSCTL = SYS_SYSCTL

View File

@ -31,3 +31,7 @@ func (msghdr *Msghdr) SetControllen(length int) {
func (cmsg *Cmsghdr) SetLen(length int) { func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint32(length) cmsg.Len = uint32(length)
} }
// SYS___SYSCTL is used by syscall_bsd.go for all BSDs, but in modern versions
// of openbsd/arm the syscall is called sysctl instead of __sysctl.
const SYS___SYSCTL = SYS_SYSCTL

View File

@ -22,10 +22,10 @@ var (
) )
const ( const (
darwin64Bit = runtime.GOOS == "darwin" && sizeofPtr == 8 darwin64Bit = runtime.GOOS == "darwin" && SizeofPtr == 8
dragonfly64Bit = runtime.GOOS == "dragonfly" && sizeofPtr == 8 dragonfly64Bit = runtime.GOOS == "dragonfly" && SizeofPtr == 8
netbsd32Bit = runtime.GOOS == "netbsd" && sizeofPtr == 4 netbsd32Bit = runtime.GOOS == "netbsd" && SizeofPtr == 4
solaris64Bit = runtime.GOOS == "solaris" && sizeofPtr == 8 solaris64Bit = runtime.GOOS == "solaris" && SizeofPtr == 8
) )
// Do the interface allocations only once for common // Do the interface allocations only once for common

View File

@ -3,7 +3,7 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris
// +build !gccgo // +build !gccgo,!ppc64le,!ppc64
package unix package unix

24
vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
// +build ppc64le ppc64
// +build !gccgo
package unix
import "syscall"
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return syscall.Syscall(trap, a1, a2, a3)
}
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return syscall.Syscall6(trap, a1, a2, a3, a4, a5, a6)
}
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return syscall.RawSyscall(trap, a1, a2, a3)
}
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return syscall.RawSyscall6(trap, a1, a2, a3, a4, a5, a6)
}

Some files were not shown because too many files have changed in this diff Show More