Migrate Sirupsen to sirupsen.

This commit is contained in:
Ludovic Fernandez 2018-01-22 12:16:03 +01:00 committed by Traefiker
parent c134dcd6fe
commit fb4ba7af2b
684 changed files with 92394 additions and 33943 deletions

202
vendor/github.com/containerd/continuity/LICENSE generated vendored Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

641
vendor/github.com/containerd/continuity/context.go generated vendored Normal file
View file

@ -0,0 +1,641 @@
package continuity
import (
"bytes"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"github.com/containerd/continuity/devices"
driverpkg "github.com/containerd/continuity/driver"
"github.com/containerd/continuity/pathdriver"
"github.com/opencontainers/go-digest"
)
var (
ErrNotFound = fmt.Errorf("not found")
ErrNotSupported = fmt.Errorf("not supported")
)
// Context represents a file system context for accessing resources. The
// responsibility of the context is to convert system specific resources to
// generic Resource objects. Most of this is safe path manipulation, as well
// as extraction of resource details.
type Context interface {
Apply(Resource) error
Verify(Resource) error
Resource(string, os.FileInfo) (Resource, error)
Walk(filepath.WalkFunc) error
}
// SymlinkPath is intended to give the symlink target value
// in a root context. Target and linkname are absolute paths
// not under the given root.
type SymlinkPath func(root, linkname, target string) (string, error)
type ContextOptions struct {
Digester Digester
Driver driverpkg.Driver
PathDriver pathdriver.PathDriver
Provider ContentProvider
}
// context represents a file system context for accessing resources.
// Generally, all path qualified access and system considerations should land
// here.
type context struct {
driver driverpkg.Driver
pathDriver pathdriver.PathDriver
root string
digester Digester
provider ContentProvider
}
// NewContext returns a Context associated with root. The default driver will
// be used, as returned by NewDriver.
func NewContext(root string) (Context, error) {
return NewContextWithOptions(root, ContextOptions{})
}
// NewContextWithOptions returns a Context associate with the root.
func NewContextWithOptions(root string, options ContextOptions) (Context, error) {
// normalize to absolute path
pathDriver := options.PathDriver
if pathDriver == nil {
pathDriver = pathdriver.LocalPathDriver
}
root = pathDriver.FromSlash(root)
root, err := pathDriver.Abs(pathDriver.Clean(root))
if err != nil {
return nil, err
}
driver := options.Driver
if driver == nil {
driver, err = driverpkg.NewSystemDriver()
if err != nil {
return nil, err
}
}
digester := options.Digester
if digester == nil {
digester = simpleDigester{digest.Canonical}
}
// Check the root directory. Need to be a little careful here. We are
// allowing a link for now, but this may have odd behavior when
// canonicalizing paths. As long as all files are opened through the link
// path, this should be okay.
fi, err := driver.Stat(root)
if err != nil {
return nil, err
}
if !fi.IsDir() {
return nil, &os.PathError{Op: "NewContext", Path: root, Err: os.ErrInvalid}
}
return &context{
root: root,
driver: driver,
pathDriver: pathDriver,
digester: digester,
provider: options.Provider,
}, nil
}
// Resource returns the resource as path p, populating the entry with info
// from fi. The path p should be the path of the resource in the context,
// typically obtained through Walk or from the value of Resource.Path(). If fi
// is nil, it will be resolved.
func (c *context) Resource(p string, fi os.FileInfo) (Resource, error) {
fp, err := c.fullpath(p)
if err != nil {
return nil, err
}
if fi == nil {
fi, err = c.driver.Lstat(fp)
if err != nil {
return nil, err
}
}
base, err := newBaseResource(p, fi)
if err != nil {
return nil, err
}
base.xattrs, err = c.resolveXAttrs(fp, fi, base)
if err == ErrNotSupported {
log.Printf("resolving xattrs on %s not supported", fp)
} else if err != nil {
return nil, err
}
// TODO(stevvooe): Handle windows alternate data streams.
if fi.Mode().IsRegular() {
dgst, err := c.digest(p)
if err != nil {
return nil, err
}
return newRegularFile(*base, base.paths, fi.Size(), dgst)
}
if fi.Mode().IsDir() {
return newDirectory(*base)
}
if fi.Mode()&os.ModeSymlink != 0 {
// We handle relative links vs absolute links by including a
// beginning slash for absolute links. Effectively, the bundle's
// root is treated as the absolute link anchor.
target, err := c.driver.Readlink(fp)
if err != nil {
return nil, err
}
return newSymLink(*base, target)
}
if fi.Mode()&os.ModeNamedPipe != 0 {
return newNamedPipe(*base, base.paths)
}
if fi.Mode()&os.ModeDevice != 0 {
deviceDriver, ok := c.driver.(driverpkg.DeviceInfoDriver)
if !ok {
log.Printf("device extraction not supported %s", fp)
return nil, ErrNotSupported
}
// character and block devices merely need to recover the
// major/minor device number.
major, minor, err := deviceDriver.DeviceInfo(fi)
if err != nil {
return nil, err
}
return newDevice(*base, base.paths, major, minor)
}
log.Printf("%q (%v) is not supported", fp, fi.Mode())
return nil, ErrNotFound
}
func (c *context) verifyMetadata(resource, target Resource) error {
if target.Mode() != resource.Mode() {
return fmt.Errorf("resource %q has incorrect mode: %v != %v", target.Path(), target.Mode(), resource.Mode())
}
if target.UID() != resource.UID() {
return fmt.Errorf("unexpected uid for %q: %v != %v", target.Path(), target.UID(), resource.GID())
}
if target.GID() != resource.GID() {
return fmt.Errorf("unexpected gid for %q: %v != %v", target.Path(), target.GID(), target.GID())
}
if xattrer, ok := resource.(XAttrer); ok {
txattrer, tok := target.(XAttrer)
if !tok {
return fmt.Errorf("resource %q has xattrs but target does not support them", resource.Path())
}
// For xattrs, only ensure that we have those defined in the resource
// and their values match. We can ignore other xattrs. In other words,
// we only verify that target has the subset defined by resource.
txattrs := txattrer.XAttrs()
for attr, value := range xattrer.XAttrs() {
tvalue, ok := txattrs[attr]
if !ok {
return fmt.Errorf("resource %q target missing xattr %q", resource.Path(), attr)
}
if !bytes.Equal(value, tvalue) {
return fmt.Errorf("xattr %q value differs for resource %q", attr, resource.Path())
}
}
}
switch r := resource.(type) {
case RegularFile:
// TODO(stevvooe): Another reason to use a record-based approach. We
// have to do another type switch to get this to work. This could be
// fixed with an Equal function, but let's study this a little more to
// be sure.
t, ok := target.(RegularFile)
if !ok {
return fmt.Errorf("resource %q target not a regular file", r.Path())
}
if t.Size() != r.Size() {
return fmt.Errorf("resource %q target has incorrect size: %v != %v", t.Path(), t.Size(), r.Size())
}
case Directory:
t, ok := target.(Directory)
if !ok {
return fmt.Errorf("resource %q target not a directory", t.Path())
}
case SymLink:
t, ok := target.(SymLink)
if !ok {
return fmt.Errorf("resource %q target not a symlink", t.Path())
}
if t.Target() != r.Target() {
return fmt.Errorf("resource %q target has mismatched target: %q != %q", t.Path(), t.Target(), r.Target())
}
case Device:
t, ok := target.(Device)
if !ok {
return fmt.Errorf("resource %q is not a device", t.Path())
}
if t.Major() != r.Major() || t.Minor() != r.Minor() {
return fmt.Errorf("resource %q has mismatched major/minor numbers: %d,%d != %d,%d", t.Path(), t.Major(), t.Minor(), r.Major(), r.Minor())
}
case NamedPipe:
t, ok := target.(NamedPipe)
if !ok {
return fmt.Errorf("resource %q is not a named pipe", t.Path())
}
default:
return fmt.Errorf("cannot verify resource: %v", resource)
}
return nil
}
// Verify the resource in the context. An error will be returned a discrepancy
// is found.
func (c *context) Verify(resource Resource) error {
fp, err := c.fullpath(resource.Path())
if err != nil {
return err
}
fi, err := c.driver.Lstat(fp)
if err != nil {
return err
}
target, err := c.Resource(resource.Path(), fi)
if err != nil {
return err
}
if target.Path() != resource.Path() {
return fmt.Errorf("resource paths do not match: %q != %q", target.Path(), resource.Path())
}
if err := c.verifyMetadata(resource, target); err != nil {
return err
}
if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable {
hardlinkKey, err := newHardlinkKey(fi)
if err == errNotAHardLink {
if len(h.Paths()) > 1 {
return fmt.Errorf("%q is not a hardlink to %q", h.Paths()[1], resource.Path())
}
} else if err != nil {
return err
}
for _, path := range h.Paths()[1:] {
fpLink, err := c.fullpath(path)
if err != nil {
return err
}
fiLink, err := c.driver.Lstat(fpLink)
if err != nil {
return err
}
targetLink, err := c.Resource(path, fiLink)
if err != nil {
return err
}
hardlinkKeyLink, err := newHardlinkKey(fiLink)
if err != nil {
return err
}
if hardlinkKeyLink != hardlinkKey {
return fmt.Errorf("%q is not a hardlink to %q", path, resource.Path())
}
if err := c.verifyMetadata(resource, targetLink); err != nil {
return err
}
}
}
switch r := resource.(type) {
case RegularFile:
t, ok := target.(RegularFile)
if !ok {
return fmt.Errorf("resource %q target not a regular file", r.Path())
}
// TODO(stevvooe): This may need to get a little more sophisticated
// for digest comparison. We may want to actually calculate the
// provided digests, rather than the implementations having an
// overlap.
if !digestsMatch(t.Digests(), r.Digests()) {
return fmt.Errorf("digests for resource %q do not match: %v != %v", t.Path(), t.Digests(), r.Digests())
}
}
return nil
}
func (c *context) checkoutFile(fp string, rf RegularFile) error {
if c.provider == nil {
return fmt.Errorf("no file provider")
}
var (
r io.ReadCloser
err error
)
for _, dgst := range rf.Digests() {
r, err = c.provider.Reader(dgst)
if err == nil {
break
}
}
if err != nil {
return fmt.Errorf("file content could not be provided: %v", err)
}
defer r.Close()
return atomicWriteFile(fp, r, rf)
}
// Apply the resource to the contexts. An error will be returned if the
// operation fails. Depending on the resource type, the resource may be
// created. For resource that cannot be resolved, an error will be returned.
func (c *context) Apply(resource Resource) error {
fp, err := c.fullpath(resource.Path())
if err != nil {
return err
}
if !strings.HasPrefix(fp, c.root) {
return fmt.Errorf("resource %v escapes root", resource)
}
var chmod = true
fi, err := c.driver.Lstat(fp)
if err != nil {
if !os.IsNotExist(err) {
return err
}
}
switch r := resource.(type) {
case RegularFile:
if fi == nil {
if err := c.checkoutFile(fp, r); err != nil {
return fmt.Errorf("error checking out file %q: %v", resource.Path(), err)
}
chmod = false
} else {
if !fi.Mode().IsRegular() {
return fmt.Errorf("file %q should be a regular file, but is not", resource.Path())
}
if fi.Size() != r.Size() {
if err := c.checkoutFile(fp, r); err != nil {
return fmt.Errorf("error checking out file %q: %v", resource.Path(), err)
}
} else {
for _, dgst := range r.Digests() {
f, err := os.Open(fp)
if err != nil {
return fmt.Errorf("failure opening file for read %q: %v", resource.Path(), err)
}
compared, err := dgst.Algorithm().FromReader(f)
if err == nil && dgst != compared {
if err := c.checkoutFile(fp, r); err != nil {
return fmt.Errorf("error checking out file %q: %v", resource.Path(), err)
}
break
}
if err1 := f.Close(); err == nil {
err = err1
}
if err != nil {
return fmt.Errorf("error checking digest for %q: %v", resource.Path(), err)
}
}
}
}
case Directory:
if fi == nil {
if err := c.driver.Mkdir(fp, resource.Mode()); err != nil {
return err
}
} else if !fi.Mode().IsDir() {
return fmt.Errorf("%q should be a directory, but is not", resource.Path())
}
case SymLink:
var target string // only possibly set if target resource is a symlink
if fi != nil {
if fi.Mode()&os.ModeSymlink != 0 {
target, err = c.driver.Readlink(fp)
if err != nil {
return err
}
}
}
if target != r.Target() {
if fi != nil {
if err := c.driver.Remove(fp); err != nil { // RemoveAll in case of directory?
return err
}
}
if err := c.driver.Symlink(r.Target(), fp); err != nil {
return err
}
}
// NOTE(stevvooe): Chmod on symlink is not supported on linux. We
// may want to maintain support for other platforms that have it.
chmod = false
case Device:
if fi == nil {
if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil {
return err
}
} else if (fi.Mode() & os.ModeDevice) == 0 {
return fmt.Errorf("%q should be a device, but is not", resource.Path())
} else {
major, minor, err := devices.DeviceInfo(fi)
if err != nil {
return err
}
if major != r.Major() || minor != r.Minor() {
if err := c.driver.Remove(fp); err != nil {
return err
}
if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil {
return err
}
}
}
case NamedPipe:
if fi == nil {
if err := c.driver.Mkfifo(fp, resource.Mode()); err != nil {
return err
}
} else if (fi.Mode() & os.ModeNamedPipe) == 0 {
return fmt.Errorf("%q should be a named pipe, but is not", resource.Path())
}
}
if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable {
for _, path := range h.Paths() {
if path == resource.Path() {
continue
}
lp, err := c.fullpath(path)
if err != nil {
return err
}
if _, fi := c.driver.Lstat(lp); fi == nil {
c.driver.Remove(lp)
}
if err := c.driver.Link(fp, lp); err != nil {
return err
}
}
}
// Update filemode if file was not created
if chmod {
if err := c.driver.Lchmod(fp, resource.Mode()); err != nil {
return err
}
}
if err := c.driver.Lchown(fp, resource.UID(), resource.GID()); err != nil {
return err
}
if xattrer, ok := resource.(XAttrer); ok {
// For xattrs, only ensure that we have those defined in the resource
// and their values are set. We can ignore other xattrs. In other words,
// we only set xattres defined by resource but never remove.
if _, ok := resource.(SymLink); ok {
lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver)
if !ok {
return fmt.Errorf("unsupported symlink xattr for resource %q", resource.Path())
}
if err := lxattrDriver.LSetxattr(fp, xattrer.XAttrs()); err != nil {
return err
}
} else {
xattrDriver, ok := c.driver.(driverpkg.XAttrDriver)
if !ok {
return fmt.Errorf("unsupported xattr for resource %q", resource.Path())
}
if err := xattrDriver.Setxattr(fp, xattrer.XAttrs()); err != nil {
return err
}
}
}
return nil
}
// Walk provides a convenience function to call filepath.Walk correctly for
// the context. Otherwise identical to filepath.Walk, the path argument is
// corrected to be contained within the context.
func (c *context) Walk(fn filepath.WalkFunc) error {
return c.pathDriver.Walk(c.root, func(p string, fi os.FileInfo, err error) error {
contained, err := c.contain(p)
return fn(contained, fi, err)
})
}
// fullpath returns the system path for the resource, joined with the context
// root. The path p must be a part of the context.
func (c *context) fullpath(p string) (string, error) {
p = c.pathDriver.Join(c.root, p)
if !strings.HasPrefix(p, c.root) {
return "", fmt.Errorf("invalid context path")
}
return p, nil
}
// contain cleans and santizes the filesystem path p to be an absolute path,
// effectively relative to the context root.
func (c *context) contain(p string) (string, error) {
sanitized, err := c.pathDriver.Rel(c.root, p)
if err != nil {
return "", err
}
// ZOMBIES(stevvooe): In certain cases, we may want to remap these to a
// "containment error", so the caller can decide what to do.
return c.pathDriver.Join("/", c.pathDriver.Clean(sanitized)), nil
}
// digest returns the digest of the file at path p, relative to the root.
func (c *context) digest(p string) (digest.Digest, error) {
f, err := c.driver.Open(c.pathDriver.Join(c.root, p))
if err != nil {
return "", err
}
defer f.Close()
return c.digester.Digest(f)
}
// resolveXAttrs attempts to resolve the extended attributes for the resource
// at the path fp, which is the full path to the resource. If the resource
// cannot have xattrs, nil will be returned.
func (c *context) resolveXAttrs(fp string, fi os.FileInfo, base *resource) (map[string][]byte, error) {
if fi.Mode().IsRegular() || fi.Mode().IsDir() {
xattrDriver, ok := c.driver.(driverpkg.XAttrDriver)
if !ok {
log.Println("xattr extraction not supported")
return nil, ErrNotSupported
}
return xattrDriver.Getxattr(fp)
}
if fi.Mode()&os.ModeSymlink != 0 {
lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver)
if !ok {
log.Println("xattr extraction for symlinks not supported")
return nil, ErrNotSupported
}
return lxattrDriver.LGetxattr(fp)
}
return nil, nil
}

88
vendor/github.com/containerd/continuity/digests.go generated vendored Normal file
View file

@ -0,0 +1,88 @@
package continuity
import (
"fmt"
"io"
"sort"
"github.com/opencontainers/go-digest"
)
// Digester produces a digest for a given read stream
type Digester interface {
Digest(io.Reader) (digest.Digest, error)
}
// ContentProvider produces a read stream for a given digest
type ContentProvider interface {
Reader(digest.Digest) (io.ReadCloser, error)
}
type simpleDigester struct {
algorithm digest.Algorithm
}
func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) {
digester := sd.algorithm.Digester()
if _, err := io.Copy(digester.Hash(), r); err != nil {
return "", err
}
return digester.Digest(), nil
}
// uniqifyDigests sorts and uniqifies the provided digest, ensuring that the
// digests are not repeated and no two digests with the same algorithm have
// different values. Because a stable sort is used, this has the effect of
// "zipping" digest collections from multiple resources.
func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) {
sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here.
seen := map[digest.Digest]struct{}{}
algs := map[digest.Algorithm][]digest.Digest{} // detect different digests.
var out []digest.Digest
// uniqify the digests
for _, d := range digests {
if _, ok := seen[d]; ok {
continue
}
seen[d] = struct{}{}
algs[d.Algorithm()] = append(algs[d.Algorithm()], d)
if len(algs[d.Algorithm()]) > 1 {
return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm())
}
out = append(out, d)
}
return out, nil
}
// digestsMatch compares the two sets of digests to see if they match.
func digestsMatch(as, bs []digest.Digest) bool {
all := append(as, bs...)
uniqified, err := uniqifyDigests(all...)
if err != nil {
// the only error uniqifyDigests returns is when the digests disagree.
return false
}
disjoint := len(as) + len(bs)
if len(uniqified) == disjoint {
// if these two sets have the same cardinality, we know both sides
// didn't share any digests.
return false
}
return true
}
type digestSlice []digest.Digest
func (p digestSlice) Len() int { return len(p) }
func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

113
vendor/github.com/containerd/continuity/groups_unix.go generated vendored Normal file
View file

@ -0,0 +1,113 @@
package continuity
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
)
// TODO(stevvooe): This needs a lot of work before we can call it useful.
type groupIndex struct {
byName map[string]*group
byGID map[int]*group
}
func getGroupIndex() (*groupIndex, error) {
f, err := os.Open("/etc/group")
if err != nil {
return nil, err
}
defer f.Close()
groups, err := parseGroups(f)
if err != nil {
return nil, err
}
return newGroupIndex(groups), nil
}
func newGroupIndex(groups []group) *groupIndex {
gi := &groupIndex{
byName: make(map[string]*group),
byGID: make(map[int]*group),
}
for i, group := range groups {
gi.byGID[group.gid] = &groups[i]
gi.byName[group.name] = &groups[i]
}
return gi
}
type group struct {
name string
gid int
members []string
}
func getGroupName(gid int) (string, error) {
f, err := os.Open("/etc/group")
if err != nil {
return "", err
}
defer f.Close()
groups, err := parseGroups(f)
if err != nil {
return "", err
}
for _, group := range groups {
if group.gid == gid {
return group.name, nil
}
}
return "", fmt.Errorf("no group for gid")
}
// parseGroups parses an /etc/group file for group names, ids and membership.
// This is unix specific.
func parseGroups(rd io.Reader) ([]group, error) {
var groups []group
scanner := bufio.NewScanner(rd)
for scanner.Scan() {
if strings.HasPrefix(scanner.Text(), "#") {
continue // skip comment
}
parts := strings.SplitN(scanner.Text(), ":", 4)
if len(parts) != 4 {
return nil, fmt.Errorf("bad entry: %q", scanner.Text())
}
name, _, sgid, smembers := parts[0], parts[1], parts[2], parts[3]
gid, err := strconv.Atoi(sgid)
if err != nil {
return nil, fmt.Errorf("bad gid: %q", gid)
}
members := strings.Split(smembers, ",")
groups = append(groups, group{
name: name,
gid: gid,
members: members,
})
}
if scanner.Err() != nil {
return nil, scanner.Err()
}
return groups, nil
}

57
vendor/github.com/containerd/continuity/hardlinks.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
package continuity
import (
"fmt"
"os"
)
var (
errNotAHardLink = fmt.Errorf("invalid hardlink")
)
type hardlinkManager struct {
hardlinks map[hardlinkKey][]Resource
}
func newHardlinkManager() *hardlinkManager {
return &hardlinkManager{
hardlinks: map[hardlinkKey][]Resource{},
}
}
// Add attempts to add the resource to the hardlink manager. If the resource
// cannot be considered as a hardlink candidate, errNotAHardLink is returned.
func (hlm *hardlinkManager) Add(fi os.FileInfo, resource Resource) error {
if _, ok := resource.(Hardlinkable); !ok {
return errNotAHardLink
}
key, err := newHardlinkKey(fi)
if err != nil {
return err
}
hlm.hardlinks[key] = append(hlm.hardlinks[key], resource)
return nil
}
// Merge processes the current state of the hardlink manager and merges any
// shared nodes into hardlinked resources.
func (hlm *hardlinkManager) Merge() ([]Resource, error) {
var resources []Resource
for key, linked := range hlm.hardlinks {
if len(linked) < 1 {
return nil, fmt.Errorf("no hardlink entrys for dev, inode pair: %#v", key)
}
merged, err := Merge(linked...)
if err != nil {
return nil, fmt.Errorf("error merging hardlink: %v", err)
}
resources = append(resources, merged)
}
return resources, nil
}

View file

@ -0,0 +1,36 @@
// +build linux darwin freebsd solaris
package continuity
import (
"fmt"
"os"
"syscall"
)
// hardlinkKey provides a tuple-key for managing hardlinks. This is system-
// specific.
type hardlinkKey struct {
dev uint64
inode uint64
}
// newHardlinkKey returns a hardlink key for the provided file info. If the
// resource does not represent a possible hardlink, errNotAHardLink will be
// returned.
func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) {
sys, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return hardlinkKey{}, fmt.Errorf("cannot resolve (*syscall.Stat_t) from os.FileInfo")
}
if sys.Nlink < 2 {
// NOTE(stevvooe): This is not always true for all filesystems. We
// should somehow detect this and provided a slow "polyfill" that
// leverages os.SameFile if we detect a filesystem where link counts
// is not really supported.
return hardlinkKey{}, errNotAHardLink
}
return hardlinkKey{dev: uint64(sys.Dev), inode: uint64(sys.Ino)}, nil
}

View file

@ -0,0 +1,12 @@
package continuity
import "os"
type hardlinkKey struct{}
func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) {
// NOTE(stevvooe): Obviously, this is not yet implemented. However, the
// makings of an implementation are available in src/os/types_windows.go. More
// investigation needs to be done to figure out exactly how to do this.
return hardlinkKey{}, errNotAHardLink
}

39
vendor/github.com/containerd/continuity/ioutils.go generated vendored Normal file
View file

@ -0,0 +1,39 @@
package continuity
import (
"io"
"io/ioutil"
"os"
"path/filepath"
)
// atomicWriteFile writes data to a file by first writing to a temp
// file and calling rename.
func atomicWriteFile(filename string, r io.Reader, rf RegularFile) error {
f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
if err != nil {
return err
}
err = os.Chmod(f.Name(), rf.Mode())
if err != nil {
f.Close()
return err
}
n, err := io.Copy(f, r)
if err == nil && n < rf.Size() {
f.Close()
return io.ErrShortWrite
}
if err != nil {
f.Close()
return err
}
if err := f.Sync(); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
return os.Rename(f.Name(), filename)
}

144
vendor/github.com/containerd/continuity/manifest.go generated vendored Normal file
View file

@ -0,0 +1,144 @@
package continuity
import (
"fmt"
"io"
"log"
"os"
"sort"
pb "github.com/containerd/continuity/proto"
"github.com/golang/protobuf/proto"
)
// Manifest provides the contents of a manifest. Users of this struct should
// not typically modify any fields directly.
type Manifest struct {
// Resources specifies all the resources for a manifest in order by path.
Resources []Resource
}
func Unmarshal(p []byte) (*Manifest, error) {
var bm pb.Manifest
if err := proto.Unmarshal(p, &bm); err != nil {
return nil, err
}
var m Manifest
for _, b := range bm.Resource {
r, err := fromProto(b)
if err != nil {
return nil, err
}
m.Resources = append(m.Resources, r)
}
return &m, nil
}
func Marshal(m *Manifest) ([]byte, error) {
var bm pb.Manifest
for _, resource := range m.Resources {
bm.Resource = append(bm.Resource, toProto(resource))
}
return proto.Marshal(&bm)
}
func MarshalText(w io.Writer, m *Manifest) error {
var bm pb.Manifest
for _, resource := range m.Resources {
bm.Resource = append(bm.Resource, toProto(resource))
}
return proto.MarshalText(w, &bm)
}
// BuildManifest creates the manifest for the given context
func BuildManifest(ctx Context) (*Manifest, error) {
resourcesByPath := map[string]Resource{}
hardlinks := newHardlinkManager()
if err := ctx.Walk(func(p string, fi os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error walking %s: %v", p, err)
}
if p == "/" {
// skip root
return nil
}
resource, err := ctx.Resource(p, fi)
if err != nil {
if err == ErrNotFound {
return nil
}
log.Printf("error getting resource %q: %v", p, err)
return err
}
// add to the hardlink manager
if err := hardlinks.Add(fi, resource); err == nil {
// Resource has been accepted by hardlink manager so we don't add
// it to the resourcesByPath until we merge at the end.
return nil
} else if err != errNotAHardLink {
// handle any other case where we have a proper error.
return fmt.Errorf("adding hardlink %s: %v", p, err)
}
resourcesByPath[p] = resource
return nil
}); err != nil {
return nil, err
}
// merge and post-process the hardlinks.
hardlinked, err := hardlinks.Merge()
if err != nil {
return nil, err
}
for _, resource := range hardlinked {
resourcesByPath[resource.Path()] = resource
}
var resources []Resource
for _, resource := range resourcesByPath {
resources = append(resources, resource)
}
sort.Stable(ByPath(resources))
return &Manifest{
Resources: resources,
}, nil
}
// VerifyManifest verifies all the resources in a manifest
// against files from the given context.
func VerifyManifest(ctx Context, manifest *Manifest) error {
for _, resource := range manifest.Resources {
if err := ctx.Verify(resource); err != nil {
return err
}
}
return nil
}
// ApplyManifest applies on the resources in a manifest to
// the given context.
func ApplyManifest(ctx Context, manifest *Manifest) error {
for _, resource := range manifest.Resources {
if err := ctx.Apply(resource); err != nil {
return err
}
}
return nil
}

View file

@ -0,0 +1,23 @@
// +build ignore
package continuity
import "os"
var (
devNullResource = resource{
kind: chardev,
path: "/dev/null",
major: 3,
minor: 2,
mode: 0666 | os.ModeDevice | os.ModeCharDevice,
}
devZeroResource = resource{
kind: chardev,
path: "/dev/zero",
major: 3,
minor: 3,
mode: 0666 | os.ModeDevice | os.ModeCharDevice,
}
)

View file

@ -0,0 +1,85 @@
package pathdriver
import (
"path/filepath"
)
// PathDriver provides all of the path manipulation functions in a common
// interface. The context should call these and never use the `filepath`
// package or any other package to manipulate paths.
type PathDriver interface {
Join(paths ...string) string
IsAbs(path string) bool
Rel(base, target string) (string, error)
Base(path string) string
Dir(path string) string
Clean(path string) string
Split(path string) (dir, file string)
Separator() byte
Abs(path string) (string, error)
Walk(string, filepath.WalkFunc) error
FromSlash(path string) string
ToSlash(path string) string
Match(pattern, name string) (matched bool, err error)
}
// pathDriver is a simple default implementation calls the filepath package.
type pathDriver struct{}
// LocalPathDriver is the exported pathDriver struct for convenience.
var LocalPathDriver PathDriver = &pathDriver{}
func (*pathDriver) Join(paths ...string) string {
return filepath.Join(paths...)
}
func (*pathDriver) IsAbs(path string) bool {
return filepath.IsAbs(path)
}
func (*pathDriver) Rel(base, target string) (string, error) {
return filepath.Rel(base, target)
}
func (*pathDriver) Base(path string) string {
return filepath.Base(path)
}
func (*pathDriver) Dir(path string) string {
return filepath.Dir(path)
}
func (*pathDriver) Clean(path string) string {
return filepath.Clean(path)
}
func (*pathDriver) Split(path string) (dir, file string) {
return filepath.Split(path)
}
func (*pathDriver) Separator() byte {
return filepath.Separator
}
func (*pathDriver) Abs(path string) (string, error) {
return filepath.Abs(path)
}
// Note that filepath.Walk calls os.Stat, so if the context wants to
// to call Driver.Stat() for Walk, they need to create a new struct that
// overrides this method.
func (*pathDriver) Walk(root string, walkFn filepath.WalkFunc) error {
return filepath.Walk(root, walkFn)
}
func (*pathDriver) FromSlash(path string) string {
return filepath.FromSlash(path)
}
func (*pathDriver) ToSlash(path string) string {
return filepath.ToSlash(path)
}
func (*pathDriver) Match(pattern, name string) (bool, error) {
return filepath.Match(pattern, name)
}

574
vendor/github.com/containerd/continuity/resource.go generated vendored Normal file
View file

@ -0,0 +1,574 @@
package continuity
import (
"errors"
"fmt"
"os"
"reflect"
"sort"
pb "github.com/containerd/continuity/proto"
"github.com/opencontainers/go-digest"
)
// TODO(stevvooe): A record based model, somewhat sketched out at the bottom
// of this file, will be more flexible. Another possibly is to tie the package
// interface directly to the protobuf type. This will have efficiency
// advantages at the cost coupling the nasty codegen types to the exported
// interface.
type Resource interface {
// Path provides the primary resource path relative to the bundle root. In
// cases where resources have more than one path, such as with hard links,
// this will return the primary path, which is often just the first entry.
Path() string
// Mode returns the
Mode() os.FileMode
UID() int64
GID() int64
}
// ByPath provides the canonical sort order for a set of resources. Use with
// sort.Stable for deterministic sorting.
type ByPath []Resource
func (bp ByPath) Len() int { return len(bp) }
func (bp ByPath) Swap(i, j int) { bp[i], bp[j] = bp[j], bp[i] }
func (bp ByPath) Less(i, j int) bool { return bp[i].Path() < bp[j].Path() }
type XAttrer interface {
XAttrs() map[string][]byte
}
// Hardlinkable is an interface that a resource type satisfies if it can be a
// hardlink target.
type Hardlinkable interface {
// Paths returns all paths of the resource, including the primary path
// returned by Resource.Path. If len(Paths()) > 1, the resource is a hard
// link.
Paths() []string
}
type RegularFile interface {
Resource
XAttrer
Hardlinkable
Size() int64
Digests() []digest.Digest
}
// Merge two or more Resources into new file. Typically, this should be
// used to merge regular files as hardlinks. If the files are not identical,
// other than Paths and Digests, the merge will fail and an error will be
// returned.
func Merge(fs ...Resource) (Resource, error) {
if len(fs) < 1 {
return nil, fmt.Errorf("please provide a resource to merge")
}
if len(fs) == 1 {
return fs[0], nil
}
var paths []string
var digests []digest.Digest
bypath := map[string][]Resource{}
// The attributes are all compared against the first to make sure they
// agree before adding to the above collections. If any of these don't
// correctly validate, the merge fails.
prototype := fs[0]
xattrs := make(map[string][]byte)
// initialize xattrs for use below. All files must have same xattrs.
if prototypeXAttrer, ok := prototype.(XAttrer); ok {
for attr, value := range prototypeXAttrer.XAttrs() {
xattrs[attr] = value
}
}
for _, f := range fs {
h, isHardlinkable := f.(Hardlinkable)
if !isHardlinkable {
return nil, errNotAHardLink
}
if f.Mode() != prototype.Mode() {
return nil, fmt.Errorf("modes do not match: %v != %v", f.Mode(), prototype.Mode())
}
if f.UID() != prototype.UID() {
return nil, fmt.Errorf("uid does not match: %v != %v", f.UID(), prototype.UID())
}
if f.GID() != prototype.GID() {
return nil, fmt.Errorf("gid does not match: %v != %v", f.GID(), prototype.GID())
}
if xattrer, ok := f.(XAttrer); ok {
fxattrs := xattrer.XAttrs()
if !reflect.DeepEqual(fxattrs, xattrs) {
return nil, fmt.Errorf("resource %q xattrs do not match: %v != %v", f, fxattrs, xattrs)
}
}
for _, p := range h.Paths() {
pfs, ok := bypath[p]
if !ok {
// ensure paths are unique by only appending on a new path.
paths = append(paths, p)
}
bypath[p] = append(pfs, f)
}
if regFile, isRegFile := f.(RegularFile); isRegFile {
prototypeRegFile, prototypeIsRegFile := prototype.(RegularFile)
if !prototypeIsRegFile {
return nil, errors.New("prototype is not a regular file")
}
if regFile.Size() != prototypeRegFile.Size() {
return nil, fmt.Errorf("size does not match: %v != %v", regFile.Size(), prototypeRegFile.Size())
}
digests = append(digests, regFile.Digests()...)
} else if device, isDevice := f.(Device); isDevice {
prototypeDevice, prototypeIsDevice := prototype.(Device)
if !prototypeIsDevice {
return nil, errors.New("prototype is not a device")
}
if device.Major() != prototypeDevice.Major() {
return nil, fmt.Errorf("major number does not match: %v != %v", device.Major(), prototypeDevice.Major())
}
if device.Minor() != prototypeDevice.Minor() {
return nil, fmt.Errorf("minor number does not match: %v != %v", device.Minor(), prototypeDevice.Minor())
}
} else if _, isNamedPipe := f.(NamedPipe); isNamedPipe {
_, prototypeIsNamedPipe := prototype.(NamedPipe)
if !prototypeIsNamedPipe {
return nil, errors.New("prototype is not a named pipe")
}
} else {
return nil, errNotAHardLink
}
}
sort.Stable(sort.StringSlice(paths))
// Choose a "canonical" file. Really, it is just the first file to sort
// against. We also effectively select the very first digest as the
// "canonical" one for this file.
first := bypath[paths[0]][0]
resource := resource{
paths: paths,
mode: first.Mode(),
uid: first.UID(),
gid: first.GID(),
xattrs: xattrs,
}
switch typedF := first.(type) {
case RegularFile:
var err error
digests, err = uniqifyDigests(digests...)
if err != nil {
return nil, err
}
return &regularFile{
resource: resource,
size: typedF.Size(),
digests: digests,
}, nil
case Device:
return &device{
resource: resource,
major: typedF.Major(),
minor: typedF.Minor(),
}, nil
case NamedPipe:
return &namedPipe{
resource: resource,
}, nil
default:
return nil, errNotAHardLink
}
}
type Directory interface {
Resource
XAttrer
// Directory is a no-op method to identify directory objects by interface.
Directory()
}
type SymLink interface {
Resource
// Target returns the target of the symlink contained in the .
Target() string
}
type NamedPipe interface {
Resource
Hardlinkable
XAttrer
// Pipe is a no-op method to allow consistent resolution of NamedPipe
// interface.
Pipe()
}
type Device interface {
Resource
Hardlinkable
XAttrer
Major() uint64
Minor() uint64
}
type resource struct {
paths []string
mode os.FileMode
uid, gid int64
xattrs map[string][]byte
}
var _ Resource = &resource{}
func (r *resource) Path() string {
if len(r.paths) < 1 {
return ""
}
return r.paths[0]
}
func (r *resource) Mode() os.FileMode {
return r.mode
}
func (r *resource) UID() int64 {
return r.uid
}
func (r *resource) GID() int64 {
return r.gid
}
type regularFile struct {
resource
size int64
digests []digest.Digest
}
var _ RegularFile = &regularFile{}
// newRegularFile returns the RegularFile, using the populated base resource
// and one or more digests of the content.
func newRegularFile(base resource, paths []string, size int64, dgsts ...digest.Digest) (RegularFile, error) {
if !base.Mode().IsRegular() {
return nil, fmt.Errorf("not a regular file")
}
base.paths = make([]string, len(paths))
copy(base.paths, paths)
// make our own copy of digests
ds := make([]digest.Digest, len(dgsts))
copy(ds, dgsts)
return &regularFile{
resource: base,
size: size,
digests: ds,
}, nil
}
func (rf *regularFile) Paths() []string {
paths := make([]string, len(rf.paths))
copy(paths, rf.paths)
return paths
}
func (rf *regularFile) Size() int64 {
return rf.size
}
func (rf *regularFile) Digests() []digest.Digest {
digests := make([]digest.Digest, len(rf.digests))
copy(digests, rf.digests)
return digests
}
func (rf *regularFile) XAttrs() map[string][]byte {
xattrs := make(map[string][]byte, len(rf.xattrs))
for attr, value := range rf.xattrs {
xattrs[attr] = append(xattrs[attr], value...)
}
return xattrs
}
type directory struct {
resource
}
var _ Directory = &directory{}
func newDirectory(base resource) (Directory, error) {
if !base.Mode().IsDir() {
return nil, fmt.Errorf("not a directory")
}
return &directory{
resource: base,
}, nil
}
func (d *directory) Directory() {}
func (d *directory) XAttrs() map[string][]byte {
xattrs := make(map[string][]byte, len(d.xattrs))
for attr, value := range d.xattrs {
xattrs[attr] = append(xattrs[attr], value...)
}
return xattrs
}
type symLink struct {
resource
target string
}
var _ SymLink = &symLink{}
func newSymLink(base resource, target string) (SymLink, error) {
if base.Mode()&os.ModeSymlink == 0 {
return nil, fmt.Errorf("not a symlink")
}
return &symLink{
resource: base,
target: target,
}, nil
}
func (l *symLink) Target() string {
return l.target
}
type namedPipe struct {
resource
}
var _ NamedPipe = &namedPipe{}
func newNamedPipe(base resource, paths []string) (NamedPipe, error) {
if base.Mode()&os.ModeNamedPipe == 0 {
return nil, fmt.Errorf("not a namedpipe")
}
base.paths = make([]string, len(paths))
copy(base.paths, paths)
return &namedPipe{
resource: base,
}, nil
}
func (np *namedPipe) Pipe() {}
func (np *namedPipe) Paths() []string {
paths := make([]string, len(np.paths))
copy(paths, np.paths)
return paths
}
func (np *namedPipe) XAttrs() map[string][]byte {
xattrs := make(map[string][]byte, len(np.xattrs))
for attr, value := range np.xattrs {
xattrs[attr] = append(xattrs[attr], value...)
}
return xattrs
}
type device struct {
resource
major, minor uint64
}
var _ Device = &device{}
func newDevice(base resource, paths []string, major, minor uint64) (Device, error) {
if base.Mode()&os.ModeDevice == 0 {
return nil, fmt.Errorf("not a device")
}
base.paths = make([]string, len(paths))
copy(base.paths, paths)
return &device{
resource: base,
major: major,
minor: minor,
}, nil
}
func (d *device) Paths() []string {
paths := make([]string, len(d.paths))
copy(paths, d.paths)
return paths
}
func (d *device) XAttrs() map[string][]byte {
xattrs := make(map[string][]byte, len(d.xattrs))
for attr, value := range d.xattrs {
xattrs[attr] = append(xattrs[attr], value...)
}
return xattrs
}
func (d device) Major() uint64 {
return d.major
}
func (d device) Minor() uint64 {
return d.minor
}
// toProto converts a resource to a protobuf record. We'd like to push this
// the individual types but we want to keep this all together during
// prototyping.
func toProto(resource Resource) *pb.Resource {
b := &pb.Resource{
Path: []string{resource.Path()},
Mode: uint32(resource.Mode()),
Uid: resource.UID(),
Gid: resource.GID(),
}
if xattrer, ok := resource.(XAttrer); ok {
// Sorts the XAttrs by name for consistent ordering.
keys := []string{}
xattrs := xattrer.XAttrs()
for k := range xattrs {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
b.Xattr = append(b.Xattr, &pb.XAttr{Name: k, Data: xattrs[k]})
}
}
switch r := resource.(type) {
case RegularFile:
b.Path = r.Paths()
b.Size = uint64(r.Size())
for _, dgst := range r.Digests() {
b.Digest = append(b.Digest, dgst.String())
}
case SymLink:
b.Target = r.Target()
case Device:
b.Major, b.Minor = r.Major(), r.Minor()
b.Path = r.Paths()
case NamedPipe:
b.Path = r.Paths()
}
// enforce a few stability guarantees that may not be provided by the
// resource implementation.
sort.Strings(b.Path)
return b
}
// fromProto converts from a protobuf Resource to a Resource interface.
func fromProto(b *pb.Resource) (Resource, error) {
base := &resource{
paths: b.Path,
mode: os.FileMode(b.Mode),
uid: b.Uid,
gid: b.Gid,
}
base.xattrs = make(map[string][]byte, len(b.Xattr))
for _, attr := range b.Xattr {
base.xattrs[attr.Name] = attr.Data
}
switch {
case base.Mode().IsRegular():
dgsts := make([]digest.Digest, len(b.Digest))
for i, dgst := range b.Digest {
// TODO(stevvooe): Should we be validating at this point?
dgsts[i] = digest.Digest(dgst)
}
return newRegularFile(*base, b.Path, int64(b.Size), dgsts...)
case base.Mode().IsDir():
return newDirectory(*base)
case base.Mode()&os.ModeSymlink != 0:
return newSymLink(*base, b.Target)
case base.Mode()&os.ModeNamedPipe != 0:
return newNamedPipe(*base, b.Path)
case base.Mode()&os.ModeDevice != 0:
return newDevice(*base, b.Path, b.Major, b.Minor)
}
return nil, fmt.Errorf("unknown resource record (%#v): %s", b, base.Mode())
}
// NOTE(stevvooe): An alternative model that supports inline declaration.
// Convenient for unit testing where inline declarations may be desirable but
// creates an awkward API for the standard use case.
// type ResourceKind int
// const (
// ResourceRegularFile = iota + 1
// ResourceDirectory
// ResourceSymLink
// Resource
// )
// type Resource struct {
// Kind ResourceKind
// Paths []string
// Mode os.FileMode
// UID string
// GID string
// Size int64
// Digests []digest.Digest
// Target string
// Major, Minor int
// XAttrs map[string][]byte
// }
// type RegularFile struct {
// Paths []string
// Size int64
// Digests []digest.Digest
// Perm os.FileMode // os.ModePerm + sticky, setuid, setgid
// }

View file

@ -0,0 +1,37 @@
// +build linux darwin freebsd solaris
package continuity
import (
"fmt"
"os"
"syscall"
)
// newBaseResource returns a *resource, populated with data from p and fi,
// where p will be populated directly.
func newBaseResource(p string, fi os.FileInfo) (*resource, error) {
// TODO(stevvooe): This need to be resolved for the container's root,
// where here we are really getting the host OS's value. We need to allow
// this be passed in and fixed up to make these uid/gid mappings portable.
// Either this can be part of the driver or we can achieve it through some
// other mechanism.
sys, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
// TODO(stevvooe): This may not be a hard error for all platforms. We
// may want to move this to the driver.
return nil, fmt.Errorf("unable to resolve syscall.Stat_t from (os.FileInfo).Sys(): %#v", fi)
}
return &resource{
paths: []string{p},
mode: fi.Mode(),
uid: int64(sys.Uid),
gid: int64(sys.Gid),
// NOTE(stevvooe): Population of shared xattrs field is deferred to
// the resource types that populate it. Since they are a property of
// the context, they must set there.
}, nil
}

View file

@ -0,0 +1,12 @@
package continuity
import "os"
// newBaseResource returns a *resource, populated with data from p and fi,
// where p will be populated directly.
func newBaseResource(p string, fi os.FileInfo) (*resource, error) {
return &resource{
paths: []string{p},
mode: fi.Mode(),
}, nil
}