Vendor continuity
Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
parent
c6446b93da
commit
5f08e609c0
36 changed files with 3446 additions and 0 deletions
|
@ -68,3 +68,5 @@ github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
|
||||||
golang.org/x/sys/unix d75a52659825e75fff6158388dddc6a5b04f9ba5
|
golang.org/x/sys/unix d75a52659825e75fff6158388dddc6a5b04f9ba5
|
||||||
# image-spec master as of 1/17/2017
|
# image-spec master as of 1/17/2017
|
||||||
github.com/opencontainers/image-spec 0ff14aabcda3b2ee62621174f1b29fc157bdf335
|
github.com/opencontainers/image-spec 0ff14aabcda3b2ee62621174f1b29fc157bdf335
|
||||||
|
# continuity master as of 1/10/2017
|
||||||
|
github.com/stevvooe/continuity 6c9282fa1546987eefc2b123fe087b818d821725
|
||||||
|
|
202
vendor/github.com/stevvooe/continuity/LICENSE
generated
vendored
Normal file
202
vendor/github.com/stevvooe/continuity/LICENSE
generated
vendored
Normal 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.
|
||||||
|
|
640
vendor/github.com/stevvooe/continuity/context.go
generated
vendored
Normal file
640
vendor/github.com/stevvooe/continuity/context.go
generated
vendored
Normal file
|
@ -0,0 +1,640 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"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 Driver
|
||||||
|
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 Driver
|
||||||
|
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
|
||||||
|
root, err := filepath.Abs(filepath.Clean(root))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
driver := options.Driver
|
||||||
|
if driver == nil {
|
||||||
|
driver, err = 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,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err := newBaseResource(p, fi.Mode(), fmt.Sprint(sys.Uid), fmt.Sprint(sys.Gid))
|
||||||
|
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.(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 err := c.driver.Remove(fp); err != nil { // RemoveAll?
|
||||||
|
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 := 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.(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.(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 filepath.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 = filepath.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 := filepath.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 filepath.Join("/", filepath.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(filepath.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.(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.(LXAttrDriver)
|
||||||
|
if !ok {
|
||||||
|
log.Println("xattr extraction for symlinks not supported")
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
return lxattrDriver.LGetxattr(fp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
15
vendor/github.com/stevvooe/continuity/devices_darwin.go
generated
vendored
Normal file
15
vendor/github.com/stevvooe/continuity/devices_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
// from /usr/include/sys/types.h
|
||||||
|
|
||||||
|
func getmajor(dev int32) uint64 {
|
||||||
|
return (uint64(dev) >> 24) & 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
func getminor(dev int32) uint64 {
|
||||||
|
return uint64(dev) & 0xffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
func makedev(major int, minor int) int {
|
||||||
|
return ((major << 24) | minor)
|
||||||
|
}
|
15
vendor/github.com/stevvooe/continuity/devices_linux.go
generated
vendored
Normal file
15
vendor/github.com/stevvooe/continuity/devices_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
// from /usr/include/linux/kdev_t.h
|
||||||
|
|
||||||
|
func getmajor(dev uint64) uint64 {
|
||||||
|
return dev >> 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func getminor(dev uint64) uint64 {
|
||||||
|
return dev & 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
func makedev(major int, minor int) int {
|
||||||
|
return ((major << 8) | minor)
|
||||||
|
}
|
55
vendor/github.com/stevvooe/continuity/devices_unix.go
generated
vendored
Normal file
55
vendor/github.com/stevvooe/continuity/devices_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// +build linux darwin
|
||||||
|
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func deviceInfo(fi os.FileInfo) (uint64, uint64, error) {
|
||||||
|
sys, ok := fi.Sys().(*syscall.Stat_t)
|
||||||
|
if !ok {
|
||||||
|
return 0, 0, fmt.Errorf("cannot extract device from os.FileInfo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return getmajor(sys.Rdev), getminor(sys.Rdev), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mknod provides a shortcut for syscall.Mknod
|
||||||
|
func mknod(p string, mode os.FileMode, maj, min int) error {
|
||||||
|
var (
|
||||||
|
m = syscallMode(mode.Perm())
|
||||||
|
dev int
|
||||||
|
)
|
||||||
|
|
||||||
|
if mode&os.ModeDevice != 0 {
|
||||||
|
dev = makedev(maj, min)
|
||||||
|
|
||||||
|
if mode&os.ModeCharDevice != 0 {
|
||||||
|
m |= syscall.S_IFCHR
|
||||||
|
} else {
|
||||||
|
m |= syscall.S_IFBLK
|
||||||
|
}
|
||||||
|
} else if mode&os.ModeNamedPipe != 0 {
|
||||||
|
m |= syscall.S_IFIFO
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.Mknod(p, m, dev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
|
||||||
|
func syscallMode(i os.FileMode) (o uint32) {
|
||||||
|
o |= uint32(i.Perm())
|
||||||
|
if i&os.ModeSetuid != 0 {
|
||||||
|
o |= syscall.S_ISUID
|
||||||
|
}
|
||||||
|
if i&os.ModeSetgid != 0 {
|
||||||
|
o |= syscall.S_ISGID
|
||||||
|
}
|
||||||
|
if i&os.ModeSticky != 0 {
|
||||||
|
o |= syscall.S_ISVTX
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
88
vendor/github.com/stevvooe/continuity/digests.go
generated
vendored
Normal file
88
vendor/github.com/stevvooe/continuity/digests.go
generated
vendored
Normal 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] }
|
158
vendor/github.com/stevvooe/continuity/driver.go
generated
vendored
Normal file
158
vendor/github.com/stevvooe/continuity/driver.go
generated
vendored
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Driver provides all of the system-level functions in a common interface.
|
||||||
|
// The context should call these with full paths and should never use the `os`
|
||||||
|
// package or any other package to access resources on the filesystem. This
|
||||||
|
// mechanism let's us carefully control access to the context and maintain
|
||||||
|
// path and resource integrity. It also gives us an interface to reason about
|
||||||
|
// direct resource access.
|
||||||
|
//
|
||||||
|
// Implementations don't need to do much other than meet the interface. For
|
||||||
|
// example, it is not required to wrap os.FileInfo to return correct paths for
|
||||||
|
// the call to Name().
|
||||||
|
type Driver interface {
|
||||||
|
Open(path string) (*os.File, error)
|
||||||
|
Stat(path string) (os.FileInfo, error)
|
||||||
|
Lstat(path string) (os.FileInfo, error)
|
||||||
|
Readlink(p string) (string, error)
|
||||||
|
Mkdir(path string, mode os.FileMode) error
|
||||||
|
Remove(path string) error
|
||||||
|
|
||||||
|
Link(oldname, newname string) error
|
||||||
|
Lchmod(path string, mode os.FileMode) error
|
||||||
|
Lchown(path, uid, gid string) error
|
||||||
|
Symlink(oldname, newname string) error
|
||||||
|
|
||||||
|
// TODO(aaronl): These methods might move outside the main Driver
|
||||||
|
// interface in the future as more platforms are added.
|
||||||
|
Mknod(path string, mode os.FileMode, major int, minor int) error
|
||||||
|
Mkfifo(path string, mode os.FileMode) error
|
||||||
|
|
||||||
|
// NOTE(stevvooe): We may want to actually include the path manipulation
|
||||||
|
// functions here, as well. They have been listed below to make the
|
||||||
|
// discovery process easier.
|
||||||
|
|
||||||
|
// Join(path ...string) string
|
||||||
|
// IsAbs(string) bool
|
||||||
|
// Abs(string) (string, error)
|
||||||
|
// Rel(base, target string) (string, error)
|
||||||
|
// Walk(string, filepath.WalkFunc) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSystemDriver() (Driver, error) {
|
||||||
|
// TODO(stevvooe): Consider having this take a "hint" path argument, which
|
||||||
|
// would be the context root. The hint could be used to resolve required
|
||||||
|
// filesystem support when assembling the driver to use.
|
||||||
|
return &driver{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XAttrDriver should be implemented on operation systems and filesystems that
|
||||||
|
// have xattr support for regular files and directories.
|
||||||
|
type XAttrDriver interface {
|
||||||
|
// Getxattr returns all of the extended attributes for the file at path.
|
||||||
|
// Typically, this takes a syscall call to Listxattr and Getxattr.
|
||||||
|
Getxattr(path string) (map[string][]byte, error)
|
||||||
|
|
||||||
|
// Setxattr sets all of the extended attributes on file at path, following
|
||||||
|
// any symbolic links, if necessary. All attributes on the target are
|
||||||
|
// replaced by the values from attr. If the operation fails to set any
|
||||||
|
// attribute, those already applied will not be rolled back.
|
||||||
|
Setxattr(path string, attr map[string][]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LXAttrDriver should be implemented by drivers on operating systems and
|
||||||
|
// filesystems that support setting and getting extended attributes on
|
||||||
|
// symbolic links. If this is not implemented, extended attributes will be
|
||||||
|
// ignored on symbolic links.
|
||||||
|
type LXAttrDriver interface {
|
||||||
|
// LGetxattr returns all of the extended attributes for the file at path
|
||||||
|
// and does not follow symlinks. Typically, this takes a syscall call to
|
||||||
|
// Llistxattr and Lgetxattr.
|
||||||
|
LGetxattr(path string) (map[string][]byte, error)
|
||||||
|
|
||||||
|
// LSetxattr sets all of the extended attributes on file at path, without
|
||||||
|
// following symbolic links. All attributes on the target are replaced by
|
||||||
|
// the values from attr. If the operation fails to set any attribute,
|
||||||
|
// those already applied will not be rolled back.
|
||||||
|
LSetxattr(path string, attr map[string][]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceInfoDriver interface {
|
||||||
|
DeviceInfo(fi os.FileInfo) (maj uint64, min uint64, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// driver is a simple default implementation that sends calls out to the "os"
|
||||||
|
// package. Extend the "driver" type in system-specific files to add support,
|
||||||
|
// such as xattrs, which can add support at compile time.
|
||||||
|
type driver struct{}
|
||||||
|
|
||||||
|
var _ Driver = &driver{}
|
||||||
|
|
||||||
|
func (d *driver) Open(p string) (*os.File, error) {
|
||||||
|
return os.Open(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Stat(p string) (os.FileInfo, error) {
|
||||||
|
return os.Stat(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Lstat(p string) (os.FileInfo, error) {
|
||||||
|
return os.Lstat(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Readlink(p string) (string, error) {
|
||||||
|
return os.Readlink(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Mkdir(p string, mode os.FileMode) error {
|
||||||
|
return os.Mkdir(p, mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove is used to unlink files and remove directories.
|
||||||
|
// This is following the golang os package api which
|
||||||
|
// combines the operations into a higher level Remove
|
||||||
|
// function. If explicit unlinking or directory removal
|
||||||
|
// to mirror system call is required, they should be
|
||||||
|
// split up at that time.
|
||||||
|
func (d *driver) Remove(path string) error {
|
||||||
|
return os.Remove(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Link(oldname, newname string) error {
|
||||||
|
return os.Link(oldname, newname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Lchown(name, uidStr, gidStr string) error {
|
||||||
|
uid, err := strconv.Atoi(uidStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gid, err := strconv.Atoi(gidStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Lchown(name, uid, gid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Symlink(oldname, newname string) error {
|
||||||
|
return os.Symlink(oldname, newname)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error {
|
||||||
|
return mknod(path, mode, major, minor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) Mkfifo(path string, mode os.FileMode) error {
|
||||||
|
if mode&os.ModeNamedPipe == 0 {
|
||||||
|
return errors.New("mode passed to Mkfifo does not have the named pipe bit set")
|
||||||
|
}
|
||||||
|
// mknod with a mode that has ModeNamedPipe set creates a fifo, not a
|
||||||
|
// device.
|
||||||
|
return mknod(path, mode, 0, 0)
|
||||||
|
}
|
7
vendor/github.com/stevvooe/continuity/driver_darwin.go
generated
vendored
Normal file
7
vendor/github.com/stevvooe/continuity/driver_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func (d *driver) DeviceInfo(fi os.FileInfo) (maj uint64, min uint64, err error) {
|
||||||
|
return deviceInfo(fi)
|
||||||
|
}
|
107
vendor/github.com/stevvooe/continuity/driver_unix.go
generated
vendored
Normal file
107
vendor/github.com/stevvooe/continuity/driver_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
// +build linux darwin
|
||||||
|
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/stevvooe/continuity/sysx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Lchmod changes the mode of an file not following symlinks.
|
||||||
|
func (d *driver) Lchmod(path string, mode os.FileMode) (err error) {
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
path, err = filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sysx.Fchmodat(0, path, uint32(mode), sysx.AtSymlinkNofollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getxattr returns all of the extended attributes for the file at path p.
|
||||||
|
func (d *driver) Getxattr(p string) (map[string][]byte, error) {
|
||||||
|
xattrs, err := sysx.Listxattr(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing %s xattrs: %v", p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(xattrs)
|
||||||
|
m := make(map[string][]byte, len(xattrs))
|
||||||
|
|
||||||
|
for _, attr := range xattrs {
|
||||||
|
value, err := sysx.Getxattr(p, attr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting %q xattr on %s: %v", attr, p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(stevvooe): This append/copy tricky relies on unique
|
||||||
|
// xattrs. Break this out into an alloc/copy if xattrs are no
|
||||||
|
// longer unique.
|
||||||
|
m[attr] = append(m[attr], value...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setxattr sets all of the extended attributes on file at path, following
|
||||||
|
// any symbolic links, if necessary. All attributes on the target are
|
||||||
|
// replaced by the values from attr. If the operation fails to set any
|
||||||
|
// attribute, those already applied will not be rolled back.
|
||||||
|
func (d *driver) Setxattr(path string, attrMap map[string][]byte) error {
|
||||||
|
for attr, value := range attrMap {
|
||||||
|
if err := sysx.Setxattr(path, attr, value, 0); err != nil {
|
||||||
|
return fmt.Errorf("error setting xattr %q on %s: %v", attr, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LGetxattr returns all of the extended attributes for the file at path p
|
||||||
|
// not following symbolic links.
|
||||||
|
func (d *driver) LGetxattr(p string) (map[string][]byte, error) {
|
||||||
|
xattrs, err := sysx.LListxattr(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing %s xattrs: %v", p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(xattrs)
|
||||||
|
m := make(map[string][]byte, len(xattrs))
|
||||||
|
|
||||||
|
for _, attr := range xattrs {
|
||||||
|
value, err := sysx.LGetxattr(p, attr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting %q xattr on %s: %v", attr, p, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(stevvooe): This append/copy tricky relies on unique
|
||||||
|
// xattrs. Break this out into an alloc/copy if xattrs are no
|
||||||
|
// longer unique.
|
||||||
|
m[attr] = append(m[attr], value...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSetxattr sets all of the extended attributes on file at path, not
|
||||||
|
// following any symbolic links. All attributes on the target are
|
||||||
|
// replaced by the values from attr. If the operation fails to set any
|
||||||
|
// attribute, those already applied will not be rolled back.
|
||||||
|
func (d *driver) LSetxattr(path string, attrMap map[string][]byte) error {
|
||||||
|
for attr, value := range attrMap {
|
||||||
|
if err := sysx.LSetxattr(path, attr, value, 0); err != nil {
|
||||||
|
return fmt.Errorf("error setting xattr %q on %s: %v", attr, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *driver) DeviceInfo(fi os.FileInfo) (maj uint64, min uint64, err error) {
|
||||||
|
return deviceInfo(fi)
|
||||||
|
}
|
113
vendor/github.com/stevvooe/continuity/groups_unix.go
generated
vendored
Normal file
113
vendor/github.com/stevvooe/continuity/groups_unix.go
generated
vendored
Normal 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/stevvooe/continuity/hardlinks.go
generated
vendored
Normal file
57
vendor/github.com/stevvooe/continuity/hardlinks.go
generated
vendored
Normal 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
|
||||||
|
}
|
34
vendor/github.com/stevvooe/continuity/hardlinks_unix.go
generated
vendored
Normal file
34
vendor/github.com/stevvooe/continuity/hardlinks_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
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: sys.Ino}, nil
|
||||||
|
}
|
5
vendor/github.com/stevvooe/continuity/hardlinks_windows.go
generated
vendored
Normal file
5
vendor/github.com/stevvooe/continuity/hardlinks_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
// 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.
|
39
vendor/github.com/stevvooe/continuity/ioutils.go
generated
vendored
Normal file
39
vendor/github.com/stevvooe/continuity/ioutils.go
generated
vendored
Normal 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/stevvooe/continuity/manifest.go
generated
vendored
Normal file
144
vendor/github.com/stevvooe/continuity/manifest.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
pb "github.com/stevvooe/continuity/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
|
||||||
|
}
|
23
vendor/github.com/stevvooe/continuity/manifest_test_darwin.go
generated
vendored
Normal file
23
vendor/github.com/stevvooe/continuity/manifest_test_darwin.go
generated
vendored
Normal 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,
|
||||||
|
}
|
||||||
|
)
|
3
vendor/github.com/stevvooe/continuity/proto/gen.go
generated
vendored
Normal file
3
vendor/github.com/stevvooe/continuity/proto/gen.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package proto
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=. manifest.proto
|
98
vendor/github.com/stevvooe/continuity/proto/manifest.pb.go
generated
vendored
Normal file
98
vendor/github.com/stevvooe/continuity/proto/manifest.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// Code generated by protoc-gen-go.
|
||||||
|
// source: manifest.proto
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package proto is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
manifest.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
Manifest
|
||||||
|
Resource
|
||||||
|
*/
|
||||||
|
package proto
|
||||||
|
|
||||||
|
import proto1 "github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto1.Marshal
|
||||||
|
|
||||||
|
// Manifest specifies the entries in a container bundle, keyed and sorted by
|
||||||
|
// path.
|
||||||
|
type Manifest struct {
|
||||||
|
Resource []*Resource `protobuf:"bytes,1,rep,name=resource" json:"resource,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manifest) Reset() { *m = Manifest{} }
|
||||||
|
func (m *Manifest) String() string { return proto1.CompactTextString(m) }
|
||||||
|
func (*Manifest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Manifest) GetResource() []*Resource {
|
||||||
|
if m != nil {
|
||||||
|
return m.Resource
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
// Path specifies the path from the bundle root. If more than one
|
||||||
|
// path is present, the entry may represent a hardlink, rather than using
|
||||||
|
// a link target. The path format is operating system specific.
|
||||||
|
Path []string `protobuf:"bytes,1,rep,name=path" json:"path,omitempty"`
|
||||||
|
// Uid specifies the user id for the resource. A string type is used for
|
||||||
|
// compatibility across different OS.
|
||||||
|
Uid string `protobuf:"bytes,2,opt,name=uid" json:"uid,omitempty"`
|
||||||
|
// Gid specifies the group id for the resource. A string type is used for
|
||||||
|
// compatibility across different OS.
|
||||||
|
Gid string `protobuf:"bytes,3,opt,name=gid" json:"gid,omitempty"`
|
||||||
|
// user and group are not currently used but their field numbers have been
|
||||||
|
// reserved for future use. As such, they are marked as deprecated.
|
||||||
|
User string `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"`
|
||||||
|
Group string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"`
|
||||||
|
// Mode defines the file mode and permissions. We've used the same
|
||||||
|
// bit-packing from Go's os package,
|
||||||
|
// http://golang.org/pkg/os/#FileMode, since they've done the work of
|
||||||
|
// creating a cross-platform layout.
|
||||||
|
Mode uint32 `protobuf:"varint,6,opt,name=mode" json:"mode,omitempty"`
|
||||||
|
// Size specifies the size in bytes of the resource. This is only valid
|
||||||
|
// for regular files.
|
||||||
|
Size uint64 `protobuf:"varint,7,opt,name=size" json:"size,omitempty"`
|
||||||
|
// Digest specifies the content digest of the target file. Only valid for
|
||||||
|
// regular files. The strings are formatted as <alg>:<digest hex bytes>.
|
||||||
|
// The digests are sorted in lexical order and implementations may choose
|
||||||
|
// which algorithms they prefer.
|
||||||
|
Digest []string `protobuf:"bytes,8,rep,name=digest" json:"digest,omitempty"`
|
||||||
|
// Target defines the target of a hard or soft link. Absolute links start
|
||||||
|
// with a slash and specify the resource relative to the bundle root.
|
||||||
|
// Relative links do not start with a slash and are relative to the
|
||||||
|
// resource path.
|
||||||
|
Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"`
|
||||||
|
// Major specifies the major device number for charactor and block devices.
|
||||||
|
Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"`
|
||||||
|
// Minor specifies the minor device number for charactor and block devices.
|
||||||
|
Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"`
|
||||||
|
// Xattr provides storage for extended attributes for the target resource.
|
||||||
|
Xattr map[string][]byte `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
// Ads stores one or more alternate data streams for the target resource.
|
||||||
|
Ads map[string][]byte `protobuf:"bytes,13,rep,name=ads" json:"ads,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Resource) Reset() { *m = Resource{} }
|
||||||
|
func (m *Resource) String() string { return proto1.CompactTextString(m) }
|
||||||
|
func (*Resource) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (m *Resource) GetXattr() map[string][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Xattr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Resource) GetAds() map[string][]byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Ads
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
77
vendor/github.com/stevvooe/continuity/proto/manifest.proto
generated
vendored
Normal file
77
vendor/github.com/stevvooe/continuity/proto/manifest.proto
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package proto;
|
||||||
|
|
||||||
|
// Manifest specifies the entries in a container bundle, keyed and sorted by
|
||||||
|
// path.
|
||||||
|
message Manifest {
|
||||||
|
repeated Resource resource = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Resource {
|
||||||
|
// Path specifies the path from the bundle root. If more than one
|
||||||
|
// path is present, the entry may represent a hardlink, rather than using
|
||||||
|
// a link target. The path format is operating system specific.
|
||||||
|
repeated string path = 1;
|
||||||
|
|
||||||
|
// NOTE(stevvooe): Need to define clear precedence for user/group/uid/gid precedence.
|
||||||
|
|
||||||
|
// Uid specifies the user id for the resource. A string type is used for
|
||||||
|
// compatibility across different OS.
|
||||||
|
string uid = 2;
|
||||||
|
|
||||||
|
// Gid specifies the group id for the resource. A string type is used for
|
||||||
|
// compatibility across different OS.
|
||||||
|
string gid = 3;
|
||||||
|
|
||||||
|
// user and group are not currently used but their field numbers have been
|
||||||
|
// reserved for future use. As such, they are marked as deprecated.
|
||||||
|
string user = 4 [deprecated=true];
|
||||||
|
string group = 5 [deprecated=true];
|
||||||
|
|
||||||
|
// Mode defines the file mode and permissions. We've used the same
|
||||||
|
// bit-packing from Go's os package,
|
||||||
|
// http://golang.org/pkg/os/#FileMode, since they've done the work of
|
||||||
|
// creating a cross-platform layout.
|
||||||
|
uint32 mode = 6;
|
||||||
|
|
||||||
|
// NOTE(stevvooe): Beyond here, we start defining type specific fields.
|
||||||
|
|
||||||
|
// Size specifies the size in bytes of the resource. This is only valid
|
||||||
|
// for regular files.
|
||||||
|
uint64 size = 7;
|
||||||
|
|
||||||
|
// Digest specifies the content digest of the target file. Only valid for
|
||||||
|
// regular files. The strings are formatted as <alg>:<digest hex bytes>.
|
||||||
|
// The digests are sorted in lexical order and implementations may choose
|
||||||
|
// which algorithms they prefer.
|
||||||
|
repeated string digest = 8;
|
||||||
|
|
||||||
|
// Target defines the target of a hard or soft link. Absolute links start
|
||||||
|
// with a slash and specify the resource relative to the bundle root.
|
||||||
|
// Relative links do not start with a slash and are relative to the
|
||||||
|
// resource path.
|
||||||
|
string target = 9;
|
||||||
|
|
||||||
|
// Major specifies the major device number for charactor and block devices.
|
||||||
|
uint64 major = 10;
|
||||||
|
|
||||||
|
// Minor specifies the minor device number for charactor and block devices.
|
||||||
|
uint64 minor = 11;
|
||||||
|
|
||||||
|
// TODO(stevvooe): The use of maps here may be problematic for
|
||||||
|
// deterministic generation. Check out this comment:
|
||||||
|
// https://developers.google.com/protocol-buffers/docs/proto3#backwards-compatibility
|
||||||
|
// Fortunately, the Go implementation correctly sorts the map keys to
|
||||||
|
// ensure deterministic generation, but this is not guaranteed for all
|
||||||
|
// implementations. If this is problem, we should generate that schema and
|
||||||
|
// sort by key. We can do this at any time and retain backwards
|
||||||
|
// compatibility.
|
||||||
|
|
||||||
|
// Xattr provides storage for extended attributes for the target resource.
|
||||||
|
map<string, bytes> xattr = 12;
|
||||||
|
|
||||||
|
// Ads stores one or more alternate data streams for the target resource.
|
||||||
|
map<string, bytes> ads = 13;
|
||||||
|
|
||||||
|
}
|
578
vendor/github.com/stevvooe/continuity/resource.go
generated
vendored
Normal file
578
vendor/github.com/stevvooe/continuity/resource.go
generated
vendored
Normal file
|
@ -0,0 +1,578 @@
|
||||||
|
package continuity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
pb "github.com/stevvooe/continuity/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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() string
|
||||||
|
GID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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", 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 ®ularFile{
|
||||||
|
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 string
|
||||||
|
xattrs map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Resource = &resource{}
|
||||||
|
|
||||||
|
// newBaseResource returns a *resource, populated with data from p and fi,
|
||||||
|
// where p will be populated directly.
|
||||||
|
func newBaseResource(p string, mode os.FileMode, uid, gid string) (*resource, error) {
|
||||||
|
return &resource{
|
||||||
|
paths: []string{p},
|
||||||
|
mode: mode,
|
||||||
|
|
||||||
|
uid: uid,
|
||||||
|
gid: 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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() string {
|
||||||
|
return r.uid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *resource) GID() string {
|
||||||
|
return r.gid
|
||||||
|
}
|
||||||
|
|
||||||
|
type regularFile struct {
|
||||||
|
resource
|
||||||
|
size int64
|
||||||
|
digests []digest.Digest
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ RegularFile = ®ularFile{}
|
||||||
|
|
||||||
|
// 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 ®ularFile{
|
||||||
|
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 {
|
||||||
|
b.Xattr = xattrer.XAttrs()
|
||||||
|
}
|
||||||
|
|
||||||
|
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, err := newBaseResource(b.Path[0], os.FileMode(b.Mode), b.Uid, b.Gid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
base.xattrs = make(map[string][]byte, len(b.Xattr))
|
||||||
|
|
||||||
|
for attr, value := range b.Xattr {
|
||||||
|
base.xattrs[attr] = append(base.xattrs[attr], value...)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// }
|
10
vendor/github.com/stevvooe/continuity/sysx/asm.s
generated
vendored
Normal file
10
vendor/github.com/stevvooe/continuity/sysx/asm.s
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2014 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"
|
||||||
|
|
||||||
|
TEXT ·use(SB),NOSPLIT,$0
|
||||||
|
RET
|
18
vendor/github.com/stevvooe/continuity/sysx/chmod_darwin.go
generated
vendored
Normal file
18
vendor/github.com/stevvooe/continuity/sysx/chmod_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in <sys/fcntl.h>
|
||||||
|
AtSymlinkNofollow = 0x20
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// SYS_FCHMODAT defined from golang.org/sys/unix
|
||||||
|
SYS_FCHMODAT = 467
|
||||||
|
)
|
||||||
|
|
||||||
|
// These functions will be generated by generate.sh
|
||||||
|
// $ GOOS=darwin GOARCH=386 ./generate.sh chmod
|
||||||
|
// $ GOOS=darwin GOARCH=amd64 ./generate.sh chmod
|
||||||
|
|
||||||
|
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
|
25
vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_386.go
generated
vendored
Normal file
25
vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_386.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// mksyscall.pl -l32 chmod_darwin.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
25
vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_amd64.go
generated
vendored
Normal file
25
vendor/github.com/stevvooe/continuity/sysx/chmod_darwin_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// mksyscall.pl chmod_darwin.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
12
vendor/github.com/stevvooe/continuity/sysx/chmod_linux.go
generated
vendored
Normal file
12
vendor/github.com/stevvooe/continuity/sysx/chmod_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in /usr/include/linux/fcntl.h
|
||||||
|
AtSymlinkNofollow = 0x100
|
||||||
|
)
|
||||||
|
|
||||||
|
func Fchmodat(dirfd int, path string, mode uint32, flags int) error {
|
||||||
|
return syscall.Fchmodat(dirfd, path, mode, flags)
|
||||||
|
}
|
37
vendor/github.com/stevvooe/continuity/sysx/sys.go
generated
vendored
Normal file
37
vendor/github.com/stevvooe/continuity/sysx/sys.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _zero uintptr
|
||||||
|
|
||||||
|
// use is a no-op, but the compiler cannot see that it is.
|
||||||
|
// Calling use(p) ensures that p is kept live until that point.
|
||||||
|
//go:noescape
|
||||||
|
func use(p unsafe.Pointer)
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
var (
|
||||||
|
errEAGAIN error = syscall.EAGAIN
|
||||||
|
errEINVAL error = syscall.EINVAL
|
||||||
|
errENOENT error = syscall.ENOENT
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case syscall.EAGAIN:
|
||||||
|
return errEAGAIN
|
||||||
|
case syscall.EINVAL:
|
||||||
|
return errEINVAL
|
||||||
|
case syscall.ENOENT:
|
||||||
|
return errENOENT
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
64
vendor/github.com/stevvooe/continuity/sysx/xattr.go
generated
vendored
Normal file
64
vendor/github.com/stevvooe/continuity/sysx/xattr.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultXattrBufferSize = 5
|
||||||
|
|
||||||
|
type listxattrFunc func(path string, dest []byte) (int, error)
|
||||||
|
|
||||||
|
func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
|
||||||
|
var p []byte // nil on first execution
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := listFunc(path, p) // first call gets buffer size.
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > len(p) {
|
||||||
|
p = make([]byte, n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
p = p[:n]
|
||||||
|
|
||||||
|
ps := bytes.Split(bytes.TrimSuffix(p, []byte{0}), []byte{0})
|
||||||
|
var entries []string
|
||||||
|
for _, p := range ps {
|
||||||
|
s := string(p)
|
||||||
|
if s != "" {
|
||||||
|
entries = append(entries, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type getxattrFunc func(string, string, []byte) (int, error)
|
||||||
|
|
||||||
|
func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
|
||||||
|
p := make([]byte, defaultXattrBufferSize)
|
||||||
|
for {
|
||||||
|
n, err := getFunc(path, attr, p)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERANGE {
|
||||||
|
p = make([]byte, len(p)*2) // this can't be ideal.
|
||||||
|
continue // try again!
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// realloc to correct size and repeat
|
||||||
|
if n > len(p) {
|
||||||
|
p = make([]byte, n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return p[:n], nil
|
||||||
|
}
|
||||||
|
}
|
71
vendor/github.com/stevvooe/continuity/sysx/xattr_darwin.go
generated
vendored
Normal file
71
vendor/github.com/stevvooe/continuity/sysx/xattr_darwin.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
// These functions will be generated by generate.sh
|
||||||
|
// $ GOOS=darwin GOARCH=386 ./generate.sh xattr
|
||||||
|
// $ GOOS=darwin GOARCH=amd64 ./generate.sh xattr
|
||||||
|
|
||||||
|
//sys getxattr(path string, attr string, dest []byte, pos int, options int) (sz int, err error)
|
||||||
|
//sys setxattr(path string, attr string, data []byte, flags int) (err error)
|
||||||
|
//sys removexattr(path string, attr string, options int) (err error)
|
||||||
|
//sys listxattr(path string, dest []byte, options int) (sz int, err error)
|
||||||
|
//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error)
|
||||||
|
|
||||||
|
const (
|
||||||
|
xattrNoFollow = 0x01
|
||||||
|
)
|
||||||
|
|
||||||
|
func listxattrFollow(path string, dest []byte) (sz int, err error) {
|
||||||
|
return listxattr(path, dest, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listxattr calls syscall getxattr
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
return listxattrAll(path, listxattrFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removexattr calls syscall getxattr
|
||||||
|
func Removexattr(path string, attr string) (err error) {
|
||||||
|
return removexattr(path, attr, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setxattr calls syscall setxattr
|
||||||
|
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return setxattr(path, attr, data, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getxattrFollow(path, attr string, dest []byte) (sz int, err error) {
|
||||||
|
return getxattr(path, attr, dest, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getxattr calls syscall getxattr
|
||||||
|
func Getxattr(path, attr string) ([]byte, error) {
|
||||||
|
return getxattrAll(path, attr, getxattrFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listxattrNoFollow(path string, dest []byte) (sz int, err error) {
|
||||||
|
return listxattr(path, dest, xattrNoFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LListxattr calls syscall listxattr with XATTR_NOFOLLOW
|
||||||
|
func LListxattr(path string) ([]string, error) {
|
||||||
|
return listxattrAll(path, listxattrNoFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LRemovexattr calls syscall removexattr with XATTR_NOFOLLOW
|
||||||
|
func LRemovexattr(path string, attr string) (err error) {
|
||||||
|
return removexattr(path, attr, xattrNoFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setxattr calls syscall setxattr with XATTR_NOFOLLOW
|
||||||
|
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return setxattr(path, attr, data, flags|xattrNoFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getxattrNoFollow(path, attr string, dest []byte) (sz int, err error) {
|
||||||
|
return getxattr(path, attr, dest, 0, xattrNoFollow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LGetxattr calls syscall getxattr with XATTR_NOFOLLOW
|
||||||
|
func LGetxattr(path, attr string) ([]byte, error) {
|
||||||
|
return getxattrAll(path, attr, getxattrNoFollow)
|
||||||
|
}
|
111
vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_386.go
generated
vendored
Normal file
111
vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_386.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// mksyscall.pl -l32 xattr_darwin.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func getxattr(path string, attr string, dest []byte, pos int, options int) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), uintptr(pos), uintptr(options))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&data[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func removexattr(path string, attr string, options int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func listxattr(path string, dest []byte, options int) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p1 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p1 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(options), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
111
vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_amd64.go
generated
vendored
Normal file
111
vendor/github.com/stevvooe/continuity/sysx/xattr_darwin_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// mksyscall.pl xattr_darwin.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func getxattr(path string, attr string, dest []byte, pos int, options int) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), uintptr(pos), uintptr(options))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&data[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func removexattr(path string, attr string, options int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func listxattr(path string, dest []byte, options int) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p1 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p1 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(options), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
58
vendor/github.com/stevvooe/continuity/sysx/xattr_linux.go
generated
vendored
Normal file
58
vendor/github.com/stevvooe/continuity/sysx/xattr_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
// These functions will be generated by generate.sh
|
||||||
|
// $ GOOS=linux GOARCH=386 ./generate.sh xattr
|
||||||
|
// $ GOOS=linux GOARCH=amd64 ./generate.sh xattr
|
||||||
|
// $ GOOS=linux GOARCH=arm ./generate.sh xattr
|
||||||
|
// $ GOOS=linux GOARCH=arm64 ./generate.sh xattr
|
||||||
|
|
||||||
|
// Listxattr calls syscall listxattr and reads all content
|
||||||
|
// and returns a string array
|
||||||
|
func Listxattr(path string) ([]string, error) {
|
||||||
|
return listxattrAll(path, syscall.Listxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removexattr calls syscall removexattr
|
||||||
|
func Removexattr(path string, attr string) (err error) {
|
||||||
|
return syscall.Removexattr(path, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setxattr calls syscall setxattr
|
||||||
|
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return syscall.Setxattr(path, attr, data, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getxattr calls syscall getxattr
|
||||||
|
func Getxattr(path, attr string) ([]byte, error) {
|
||||||
|
return getxattrAll(path, attr, syscall.Getxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys llistxattr(path string, dest []byte) (sz int, err error)
|
||||||
|
|
||||||
|
// LListxattr lists xattrs, not following symlinks
|
||||||
|
func LListxattr(path string) ([]string, error) {
|
||||||
|
return listxattrAll(path, llistxattr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys lremovexattr(path string, attr string) (err error)
|
||||||
|
|
||||||
|
// LRemovexattr removes an xattr, not following symlinks
|
||||||
|
func LRemovexattr(path string, attr string) (err error) {
|
||||||
|
return lremovexattr(path, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys lsetxattr(path string, attr string, data []byte, flags int) (err error)
|
||||||
|
|
||||||
|
// LSetxattr sets an xattr, not following symlinks
|
||||||
|
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
return lsetxattr(path, attr, data, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys lgetxattr(path string, attr string, dest []byte) (sz int, err error)
|
||||||
|
|
||||||
|
// LGetxattr gets an xattr, not following symlinks
|
||||||
|
func LGetxattr(path, attr string) ([]byte, error) {
|
||||||
|
return getxattrAll(path, attr, lgetxattr)
|
||||||
|
}
|
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_386.go
generated
vendored
Normal file
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_386.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// mksyscall.pl -l32 xattr_linux.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func llistxattr(path string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p1 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p1 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lremovexattr(path string, attr string) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall(syscall.SYS_LREMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&data[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_amd64.go
generated
vendored
Normal file
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// mksyscall.pl xattr_linux.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func llistxattr(path string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p1 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p1 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lremovexattr(path string, attr string) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall(syscall.SYS_LREMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&data[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm.go
generated
vendored
Normal file
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// mksyscall.pl -l32 xattr_linux.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func llistxattr(path string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p1 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p1 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lremovexattr(path string, attr string) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall(syscall.SYS_LREMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&data[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm64.go
generated
vendored
Normal file
111
vendor/github.com/stevvooe/continuity/sysx/xattr_linux_arm64.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// mksyscall.pl xattr_linux.go
|
||||||
|
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||||
|
|
||||||
|
package sysx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func llistxattr(path string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p1 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p1 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lremovexattr(path string, attr string) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall(syscall.SYS_LREMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lsetxattr(path string, attr string, data []byte, flags int) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(data) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&data[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
_, _, e1 := syscall.Syscall6(syscall.SYS_LSETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||||
|
|
||||||
|
func lgetxattr(path string, attr string, dest []byte) (sz int, err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
_p0, err = syscall.BytePtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *byte
|
||||||
|
_p1, err = syscall.BytePtrFromString(attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 unsafe.Pointer
|
||||||
|
if len(dest) > 0 {
|
||||||
|
_p2 = unsafe.Pointer(&dest[0])
|
||||||
|
} else {
|
||||||
|
_p2 = unsafe.Pointer(&_zero)
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
|
||||||
|
use(unsafe.Pointer(_p0))
|
||||||
|
use(unsafe.Pointer(_p1))
|
||||||
|
sz = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in a new issue