mirror of https://github.com/containers/podman.git
Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.3-0.20210216175712-646072ed6524+incompatible to 20.10.6+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md) - [Commits](https://github.com/docker/docker/commits/v20.10.6) Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
parent
034470e5be
commit
a3fb15dffb
2
go.mod
2
go.mod
|
|
@ -25,7 +25,7 @@ require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f
|
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f
|
||||||
github.com/docker/distribution v2.7.1+incompatible
|
github.com/docker/distribution v2.7.1+incompatible
|
||||||
github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible
|
github.com/docker/docker v20.10.6+incompatible
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc
|
github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc
|
||||||
github.com/docker/go-units v0.4.0
|
github.com/docker/go-units v0.4.0
|
||||||
|
|
|
||||||
3
go.sum
3
go.sum
|
|
@ -266,8 +266,9 @@ github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
|
||||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible h1:Yu2uGErhwEoOT/OxAFe+/SiJCqRLs+pgcS5XKrDXnG4=
|
|
||||||
github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
|
||||||
|
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
|
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
|
||||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
Aaron Lehmann <aaron.lehmann@docker.com>
|
|
||||||
Akash Gupta <akagup@microsoft.com>
|
|
||||||
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
|
|
||||||
Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
|
|
||||||
Akihiro Suda <suda.kyoto@gmail.com>
|
|
||||||
Andrew Pennebaker <apennebaker@datapipe.com>
|
|
||||||
Brandon Philips <brandon.philips@coreos.com>
|
|
||||||
Brian Goff <cpuguy83@gmail.com>
|
|
||||||
Christopher Jones <tophj@linux.vnet.ibm.com>
|
|
||||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
|
||||||
Darren Stahl <darst@microsoft.com>
|
|
||||||
Derek McGowan <derek@mcg.dev>
|
|
||||||
Derek McGowan <derek@mcgstyle.net>
|
|
||||||
Edward Pilatowicz <edward.pilatowicz@oracle.com>
|
|
||||||
Ian Campbell <ijc@docker.com>
|
|
||||||
Ivan Markin <sw@nogoegst.net>
|
|
||||||
Justin Cormack <justin.cormack@docker.com>
|
|
||||||
Justin Cummins <sul3n3t@gmail.com>
|
|
||||||
Kasper Fabæch Brandt <poizan@poizan.dk>
|
|
||||||
Kir Kolyshkin <kolyshkin@gmail.com>
|
|
||||||
Michael Crosby <crosbymichael@gmail.com>
|
|
||||||
Michael Crosby <michael@thepasture.io>
|
|
||||||
Michael Wan <zirenwan@gmail.com>
|
|
||||||
Mike Brown <brownwm@us.ibm.com>
|
|
||||||
Niels de Vos <ndevos@redhat.com>
|
|
||||||
Phil Estes <estesp@gmail.com>
|
|
||||||
Phil Estes <estesp@linux.vnet.ibm.com>
|
|
||||||
Samuel Karp <me@samuelkarp.com>
|
|
||||||
Sam Whited <sam@samwhited.com>
|
|
||||||
Sebastiaan van Stijn <github@gone.nl>
|
|
||||||
Shengjing Zhu <zhsj@debian.org>
|
|
||||||
Stephen J Day <stephen.day@docker.com>
|
|
||||||
Tibor Vass <tibor@docker.com>
|
|
||||||
Tobias Klauser <tklauser@distanz.ch>
|
|
||||||
Tom Faulhaber <tffaulha@amazon.com>
|
|
||||||
Tonis Tiigi <tonistiigi@gmail.com>
|
|
||||||
Trevor Porter <trkporter@ucdavis.edu>
|
|
||||||
Wei Fu <fuweid89@gmail.com>
|
|
||||||
Wilbert van de Ridder <wilbert.ridder@gmail.com>
|
|
||||||
Xiaodong Ye <xiaodongy@vmware.com>
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
https://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
|
|
||||||
|
|
||||||
Copyright The containerd Authors
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
https://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.
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var bufferPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
buffer := make([]byte, 32*1024)
|
|
||||||
return &buffer
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// XAttrErrorHandlers transform a non-nil xattr error.
|
|
||||||
// Return nil to ignore an error.
|
|
||||||
// xattrKey can be empty for listxattr operation.
|
|
||||||
type XAttrErrorHandler func(dst, src, xattrKey string, err error) error
|
|
||||||
|
|
||||||
type copyDirOpts struct {
|
|
||||||
xeh XAttrErrorHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
type CopyDirOpt func(*copyDirOpts) error
|
|
||||||
|
|
||||||
// WithXAttrErrorHandler allows specifying XAttrErrorHandler
|
|
||||||
// If nil XAttrErrorHandler is specified (default), CopyDir stops
|
|
||||||
// on a non-nil xattr error.
|
|
||||||
func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
|
|
||||||
return func(o *copyDirOpts) error {
|
|
||||||
o.xeh = xeh
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAllowXAttrErrors allows ignoring xattr errors.
|
|
||||||
func WithAllowXAttrErrors() CopyDirOpt {
|
|
||||||
xeh := func(dst, src, xattrKey string, err error) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return WithXAttrErrorHandler(xeh)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyDir copies the directory from src to dst.
|
|
||||||
// Most efficient copy of files is attempted.
|
|
||||||
func CopyDir(dst, src string, opts ...CopyDirOpt) error {
|
|
||||||
var o copyDirOpts
|
|
||||||
for _, opt := range opts {
|
|
||||||
if err := opt(&o); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inodes := map[uint64]string{}
|
|
||||||
return copyDirectory(dst, src, inodes, &o)
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
|
|
||||||
stat, err := os.Stat(src)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to stat %s", src)
|
|
||||||
}
|
|
||||||
if !stat.IsDir() {
|
|
||||||
return errors.Errorf("source %s is not directory", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
if st, err := os.Stat(dst); err != nil {
|
|
||||||
if err := os.Mkdir(dst, stat.Mode()); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to mkdir %s", dst)
|
|
||||||
}
|
|
||||||
} else if !st.IsDir() {
|
|
||||||
return errors.Errorf("cannot copy to non-directory: %s", dst)
|
|
||||||
} else {
|
|
||||||
if err := os.Chmod(dst, stat.Mode()); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to chmod on %s", dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fis, err := ioutil.ReadDir(src)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to read %s", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := copyFileInfo(stat, dst); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to copy file info for %s", dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := copyXAttrs(dst, src, o.xeh); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to copy xattrs")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fi := range fis {
|
|
||||||
source := filepath.Join(src, fi.Name())
|
|
||||||
target := filepath.Join(dst, fi.Name())
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case fi.IsDir():
|
|
||||||
if err := copyDirectory(target, source, inodes, o); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
case (fi.Mode() & os.ModeType) == 0:
|
|
||||||
link, err := getLinkSource(target, fi, inodes)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to get hardlink")
|
|
||||||
}
|
|
||||||
if link != "" {
|
|
||||||
if err := os.Link(link, target); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to create hard link")
|
|
||||||
}
|
|
||||||
} else if err := CopyFile(target, source); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to copy files")
|
|
||||||
}
|
|
||||||
case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
|
|
||||||
link, err := os.Readlink(source)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to read link: %s", source)
|
|
||||||
}
|
|
||||||
if err := os.Symlink(link, target); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to create symlink: %s", target)
|
|
||||||
}
|
|
||||||
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
|
|
||||||
if err := copyDevice(target, fi); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to create device")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// TODO: Support pipes and sockets
|
|
||||||
return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
|
|
||||||
}
|
|
||||||
if err := copyFileInfo(fi, target); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to copy file info")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := copyXAttrs(target, source, o.xeh); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to copy xattrs")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFile copies the source file to the target.
|
|
||||||
// The most efficient means of copying is used for the platform.
|
|
||||||
func CopyFile(target, source string) error {
|
|
||||||
src, err := os.Open(source)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to open source %s", source)
|
|
||||||
}
|
|
||||||
defer src.Close()
|
|
||||||
tgt, err := os.Create(target)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to open target %s", target)
|
|
||||||
}
|
|
||||||
defer tgt.Close()
|
|
||||||
|
|
||||||
return copyFileContent(tgt, src)
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
// +build darwin openbsd solaris
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func copyDevice(dst string, fi os.FileInfo) error {
|
|
||||||
st, ok := fi.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("unsupported stat type")
|
|
||||||
}
|
|
||||||
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
|
|
||||||
}
|
|
||||||
|
|
||||||
func utimesNano(name string, atime, mtime syscall.Timespec) error {
|
|
||||||
timespec := []syscall.Timespec{atime, mtime}
|
|
||||||
return syscall.UtimesNano(name, timespec)
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
// +build freebsd
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func copyDevice(dst string, fi os.FileInfo) error {
|
|
||||||
st, ok := fi.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("unsupported stat type")
|
|
||||||
}
|
|
||||||
return unix.Mknod(dst, uint32(fi.Mode()), st.Rdev)
|
|
||||||
}
|
|
||||||
|
|
||||||
func utimesNano(name string, atime, mtime syscall.Timespec) error {
|
|
||||||
at := unix.NsecToTimespec(atime.Nano())
|
|
||||||
mt := unix.NsecToTimespec(mtime.Nano())
|
|
||||||
utimes := [2]unix.Timespec{at, mt}
|
|
||||||
return unix.UtimesNanoAt(unix.AT_FDCWD, name, utimes[0:], unix.AT_SYMLINK_NOFOLLOW)
|
|
||||||
}
|
|
||||||
|
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containerd/continuity/sysx"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func copyFileInfo(fi os.FileInfo, name string) error {
|
|
||||||
st := fi.Sys().(*syscall.Stat_t)
|
|
||||||
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
|
|
||||||
if os.IsPermission(err) {
|
|
||||||
// Normally if uid/gid are the same this would be a no-op, but some
|
|
||||||
// filesystems may still return EPERM... for instance NFS does this.
|
|
||||||
// In such a case, this is not an error.
|
|
||||||
if dstStat, err2 := os.Lstat(name); err2 == nil {
|
|
||||||
st2 := dstStat.Sys().(*syscall.Stat_t)
|
|
||||||
if st.Uid == st2.Uid && st.Gid == st2.Gid {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to chown %s", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
|
|
||||||
if err := os.Chmod(name, fi.Mode()); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to chmod %s", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timespec := []unix.Timespec{
|
|
||||||
unix.NsecToTimespec(syscall.TimespecToNsec(StatAtime(st))),
|
|
||||||
unix.NsecToTimespec(syscall.TimespecToNsec(StatMtime(st))),
|
|
||||||
}
|
|
||||||
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to utime %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxSSizeT = int64(^uint(0) >> 1)
|
|
||||||
|
|
||||||
func copyFileContent(dst, src *os.File) error {
|
|
||||||
st, err := src.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "unable to stat source")
|
|
||||||
}
|
|
||||||
|
|
||||||
size := st.Size()
|
|
||||||
first := true
|
|
||||||
srcFd := int(src.Fd())
|
|
||||||
dstFd := int(dst.Fd())
|
|
||||||
|
|
||||||
for size > 0 {
|
|
||||||
// Ensure that we are never trying to copy more than SSIZE_MAX at a
|
|
||||||
// time and at the same time avoids overflows when the file is larger
|
|
||||||
// than 4GB on 32-bit systems.
|
|
||||||
var copySize int
|
|
||||||
if size > maxSSizeT {
|
|
||||||
copySize = int(maxSSizeT)
|
|
||||||
} else {
|
|
||||||
copySize = int(size)
|
|
||||||
}
|
|
||||||
n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0)
|
|
||||||
if err != nil {
|
|
||||||
if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
|
|
||||||
return errors.Wrap(err, "copy file range failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bufferPool.Get().(*[]byte)
|
|
||||||
_, err = io.CopyBuffer(dst, src, *buf)
|
|
||||||
bufferPool.Put(buf)
|
|
||||||
return errors.Wrap(err, "userspace copy failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
first = false
|
|
||||||
size -= int64(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
|
||||||
xattrKeys, err := sysx.LListxattr(src)
|
|
||||||
if err != nil {
|
|
||||||
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
|
|
||||||
if xeh != nil {
|
|
||||||
e = xeh(dst, src, "", e)
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
for _, xattr := range xattrKeys {
|
|
||||||
data, err := sysx.LGetxattr(src, xattr)
|
|
||||||
if err != nil {
|
|
||||||
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
|
|
||||||
if xeh != nil {
|
|
||||||
if e = xeh(dst, src, xattr, e); e == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
|
||||||
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
|
|
||||||
if xeh != nil {
|
|
||||||
if e = xeh(dst, src, xattr, e); e == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyDevice(dst string, fi os.FileInfo) error {
|
|
||||||
st, ok := fi.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("unsupported stat type")
|
|
||||||
}
|
|
||||||
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
|
|
||||||
}
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
// +build darwin freebsd openbsd solaris
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containerd/continuity/sysx"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func copyFileInfo(fi os.FileInfo, name string) error {
|
|
||||||
st := fi.Sys().(*syscall.Stat_t)
|
|
||||||
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
|
|
||||||
if os.IsPermission(err) {
|
|
||||||
// Normally if uid/gid are the same this would be a no-op, but some
|
|
||||||
// filesystems may still return EPERM... for instance NFS does this.
|
|
||||||
// In such a case, this is not an error.
|
|
||||||
if dstStat, err2 := os.Lstat(name); err2 == nil {
|
|
||||||
st2 := dstStat.Sys().(*syscall.Stat_t)
|
|
||||||
if st.Uid == st2.Uid && st.Gid == st2.Gid {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to chown %s", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
|
|
||||||
if err := os.Chmod(name, fi.Mode()); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to chmod %s", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := utimesNano(name, StatAtime(st), StatMtime(st)); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to utime %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFileContent(dst, src *os.File) error {
|
|
||||||
buf := bufferPool.Get().(*[]byte)
|
|
||||||
_, err := io.CopyBuffer(dst, src, *buf)
|
|
||||||
bufferPool.Put(buf)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
|
||||||
xattrKeys, err := sysx.LListxattr(src)
|
|
||||||
if err != nil {
|
|
||||||
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
|
|
||||||
if xeh != nil {
|
|
||||||
e = xeh(dst, src, "", e)
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
for _, xattr := range xattrKeys {
|
|
||||||
data, err := sysx.LGetxattr(src, xattr)
|
|
||||||
if err != nil {
|
|
||||||
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
|
|
||||||
if xeh != nil {
|
|
||||||
if e = xeh(dst, src, xattr, e); e == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
|
||||||
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
|
|
||||||
if xeh != nil {
|
|
||||||
if e = xeh(dst, src, xattr, e); e == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func copyFileInfo(fi os.FileInfo, name string) error {
|
|
||||||
if err := os.Chmod(name, fi.Mode()); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to chmod %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: copy windows specific metadata
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFileContent(dst, src *os.File) error {
|
|
||||||
buf := bufferPool.Get().(*[]byte)
|
|
||||||
_, err := io.CopyBuffer(dst, src, *buf)
|
|
||||||
bufferPool.Put(buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyDevice(dst string, fi os.FileInfo) error {
|
|
||||||
return errors.New("device copy not supported")
|
|
||||||
}
|
|
||||||
|
|
@ -1,326 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChangeKind is the type of modification that
|
|
||||||
// a change is making.
|
|
||||||
type ChangeKind int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ChangeKindUnmodified represents an unmodified
|
|
||||||
// file
|
|
||||||
ChangeKindUnmodified = iota
|
|
||||||
|
|
||||||
// ChangeKindAdd represents an addition of
|
|
||||||
// a file
|
|
||||||
ChangeKindAdd
|
|
||||||
|
|
||||||
// ChangeKindModify represents a change to
|
|
||||||
// an existing file
|
|
||||||
ChangeKindModify
|
|
||||||
|
|
||||||
// ChangeKindDelete represents a delete of
|
|
||||||
// a file
|
|
||||||
ChangeKindDelete
|
|
||||||
)
|
|
||||||
|
|
||||||
func (k ChangeKind) String() string {
|
|
||||||
switch k {
|
|
||||||
case ChangeKindUnmodified:
|
|
||||||
return "unmodified"
|
|
||||||
case ChangeKindAdd:
|
|
||||||
return "add"
|
|
||||||
case ChangeKindModify:
|
|
||||||
return "modify"
|
|
||||||
case ChangeKindDelete:
|
|
||||||
return "delete"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change represents single change between a diff and its parent.
|
|
||||||
type Change struct {
|
|
||||||
Kind ChangeKind
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangeFunc is the type of function called for each change
|
|
||||||
// computed during a directory changes calculation.
|
|
||||||
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
|
|
||||||
|
|
||||||
// Changes computes changes between two directories calling the
|
|
||||||
// given change function for each computed change. The first
|
|
||||||
// directory is intended to the base directory and second
|
|
||||||
// directory the changed directory.
|
|
||||||
//
|
|
||||||
// The change callback is called by the order of path names and
|
|
||||||
// should be appliable in that order.
|
|
||||||
// Due to this apply ordering, the following is true
|
|
||||||
// - Removed directory trees only create a single change for the root
|
|
||||||
// directory removed. Remaining changes are implied.
|
|
||||||
// - A directory which is modified to become a file will not have
|
|
||||||
// delete entries for sub-path items, their removal is implied
|
|
||||||
// by the removal of the parent directory.
|
|
||||||
//
|
|
||||||
// Opaque directories will not be treated specially and each file
|
|
||||||
// removed from the base directory will show up as a removal.
|
|
||||||
//
|
|
||||||
// File content comparisons will be done on files which have timestamps
|
|
||||||
// which may have been truncated. If either of the files being compared
|
|
||||||
// has a zero value nanosecond value, each byte will be compared for
|
|
||||||
// differences. If 2 files have the same seconds value but different
|
|
||||||
// nanosecond values where one of those values is zero, the files will
|
|
||||||
// be considered unchanged if the content is the same. This behavior
|
|
||||||
// is to account for timestamp truncation during archiving.
|
|
||||||
func Changes(ctx context.Context, a, b string, changeFn ChangeFunc) error {
|
|
||||||
if a == "" {
|
|
||||||
logrus.Debugf("Using single walk diff for %s", b)
|
|
||||||
return addDirChanges(ctx, changeFn, b)
|
|
||||||
} else if diffOptions := detectDirDiff(b, a); diffOptions != nil {
|
|
||||||
logrus.Debugf("Using single walk diff for %s from %s", diffOptions.diffDir, a)
|
|
||||||
return diffDirChanges(ctx, changeFn, a, diffOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("Using double walk diff for %s from %s", b, a)
|
|
||||||
return doubleWalkDiff(ctx, changeFn, a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addDirChanges(ctx context.Context, changeFn ChangeFunc, root string) error {
|
|
||||||
return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebase path
|
|
||||||
path, err = filepath.Rel(root, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
path = filepath.Join(string(os.PathSeparator), path)
|
|
||||||
|
|
||||||
// Skip root
|
|
||||||
if path == string(os.PathSeparator) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return changeFn(ChangeKindAdd, path, f, nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// diffDirOptions is used when the diff can be directly calculated from
|
|
||||||
// a diff directory to its base, without walking both trees.
|
|
||||||
type diffDirOptions struct {
|
|
||||||
diffDir string
|
|
||||||
skipChange func(string) (bool, error)
|
|
||||||
deleteChange func(string, string, os.FileInfo) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// diffDirChanges walks the diff directory and compares changes against the base.
|
|
||||||
func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *diffDirOptions) error {
|
|
||||||
changedDirs := make(map[string]struct{})
|
|
||||||
return filepath.Walk(o.diffDir, func(path string, f os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebase path
|
|
||||||
path, err = filepath.Rel(o.diffDir, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
path = filepath.Join(string(os.PathSeparator), path)
|
|
||||||
|
|
||||||
// Skip root
|
|
||||||
if path == string(os.PathSeparator) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: handle opaqueness, start new double walker at this
|
|
||||||
// location to get deletes, and skip tree in single walker
|
|
||||||
|
|
||||||
if o.skipChange != nil {
|
|
||||||
if skip, err := o.skipChange(path); skip {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var kind ChangeKind
|
|
||||||
|
|
||||||
deletedFile, err := o.deleteChange(o.diffDir, path, f)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find out what kind of modification happened
|
|
||||||
if deletedFile != "" {
|
|
||||||
path = deletedFile
|
|
||||||
kind = ChangeKindDelete
|
|
||||||
f = nil
|
|
||||||
} else {
|
|
||||||
// Otherwise, the file was added
|
|
||||||
kind = ChangeKindAdd
|
|
||||||
|
|
||||||
// ...Unless it already existed in a base, in which case, it's a modification
|
|
||||||
stat, err := os.Stat(filepath.Join(base, path))
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
// The file existed in the base, so that's a modification
|
|
||||||
|
|
||||||
// However, if it's a directory, maybe it wasn't actually modified.
|
|
||||||
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
|
||||||
if stat.IsDir() && f.IsDir() {
|
|
||||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
|
|
||||||
// Both directories are the same, don't record the change
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kind = ChangeKindModify
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If /foo/bar/file.txt is modified, then /foo/bar must be part of the changed files.
|
|
||||||
// This block is here to ensure the change is recorded even if the
|
|
||||||
// modify time, mode and size of the parent directory in the rw and ro layers are all equal.
|
|
||||||
// Check https://github.com/docker/docker/pull/13590 for details.
|
|
||||||
if f.IsDir() {
|
|
||||||
changedDirs[path] = struct{}{}
|
|
||||||
}
|
|
||||||
if kind == ChangeKindAdd || kind == ChangeKindDelete {
|
|
||||||
parent := filepath.Dir(path)
|
|
||||||
if _, ok := changedDirs[parent]; !ok && parent != "/" {
|
|
||||||
pi, err := os.Stat(filepath.Join(o.diffDir, parent))
|
|
||||||
if err := changeFn(ChangeKindModify, parent, pi, err); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
changedDirs[parent] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return changeFn(kind, path, f, nil)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// doubleWalkDiff walks both directories to create a diff
|
|
||||||
func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err error) {
|
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
|
||||||
|
|
||||||
var (
|
|
||||||
c1 = make(chan *currentPath)
|
|
||||||
c2 = make(chan *currentPath)
|
|
||||||
|
|
||||||
f1, f2 *currentPath
|
|
||||||
rmdir string
|
|
||||||
)
|
|
||||||
g.Go(func() error {
|
|
||||||
defer close(c1)
|
|
||||||
return pathWalk(ctx, a, c1)
|
|
||||||
})
|
|
||||||
g.Go(func() error {
|
|
||||||
defer close(c2)
|
|
||||||
return pathWalk(ctx, b, c2)
|
|
||||||
})
|
|
||||||
g.Go(func() error {
|
|
||||||
for c1 != nil || c2 != nil {
|
|
||||||
if f1 == nil && c1 != nil {
|
|
||||||
f1, err = nextPath(ctx, c1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if f1 == nil {
|
|
||||||
c1 = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f2 == nil && c2 != nil {
|
|
||||||
f2, err = nextPath(ctx, c2)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if f2 == nil {
|
|
||||||
c2 = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f1 == nil && f2 == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var f os.FileInfo
|
|
||||||
k, p := pathChange(f1, f2)
|
|
||||||
switch k {
|
|
||||||
case ChangeKindAdd:
|
|
||||||
if rmdir != "" {
|
|
||||||
rmdir = ""
|
|
||||||
}
|
|
||||||
f = f2.f
|
|
||||||
f2 = nil
|
|
||||||
case ChangeKindDelete:
|
|
||||||
// Check if this file is already removed by being
|
|
||||||
// under of a removed directory
|
|
||||||
if rmdir != "" && strings.HasPrefix(f1.path, rmdir) {
|
|
||||||
f1 = nil
|
|
||||||
continue
|
|
||||||
} else if f1.f.IsDir() {
|
|
||||||
rmdir = f1.path + string(os.PathSeparator)
|
|
||||||
} else if rmdir != "" {
|
|
||||||
rmdir = ""
|
|
||||||
}
|
|
||||||
f1 = nil
|
|
||||||
case ChangeKindModify:
|
|
||||||
same, err := sameFile(f1, f2)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if f1.f.IsDir() && !f2.f.IsDir() {
|
|
||||||
rmdir = f1.path + string(os.PathSeparator)
|
|
||||||
} else if rmdir != "" {
|
|
||||||
rmdir = ""
|
|
||||||
}
|
|
||||||
f = f2.f
|
|
||||||
f1 = nil
|
|
||||||
f2 = nil
|
|
||||||
if same {
|
|
||||||
if !isLinked(f) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
k = ChangeKindUnmodified
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := changeFn(k, p, f, nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return g.Wait()
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containerd/continuity/sysx"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// detectDirDiff returns diff dir options if a directory could
|
|
||||||
// be found in the mount info for upper which is the direct
|
|
||||||
// diff with the provided lower directory
|
|
||||||
func detectDirDiff(upper, lower string) *diffDirOptions {
|
|
||||||
// TODO: get mount options for upper
|
|
||||||
// TODO: detect AUFS
|
|
||||||
// TODO: detect overlay
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// compareSysStat returns whether the stats are equivalent,
|
|
||||||
// whether the files are considered the same file, and
|
|
||||||
// an error
|
|
||||||
func compareSysStat(s1, s2 interface{}) (bool, error) {
|
|
||||||
ls1, ok := s1.(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
ls2, ok := s2.(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Rdev == ls2.Rdev, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareCapabilities(p1, p2 string) (bool, error) {
|
|
||||||
c1, err := sysx.LGetxattr(p1, "security.capability")
|
|
||||||
if err != nil && err != sysx.ENODATA {
|
|
||||||
return false, errors.Wrapf(err, "failed to get xattr for %s", p1)
|
|
||||||
}
|
|
||||||
c2, err := sysx.LGetxattr(p2, "security.capability")
|
|
||||||
if err != nil && err != sysx.ENODATA {
|
|
||||||
return false, errors.Wrapf(err, "failed to get xattr for %s", p2)
|
|
||||||
}
|
|
||||||
return bytes.Equal(c1, c2), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLinked(f os.FileInfo) bool {
|
|
||||||
s, ok := f.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return !f.IsDir() && s.Nlink > 1
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
func detectDirDiff(upper, lower string) *diffDirOptions {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareSysStat(s1, s2 interface{}) (bool, error) {
|
|
||||||
f1, ok := s1.(windows.Win32FileAttributeData)
|
|
||||||
if !ok {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
f2, ok := s2.(windows.Win32FileAttributeData)
|
|
||||||
if !ok {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
return f1.FileAttributes == f2.FileAttributes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareCapabilities(p1, p2 string) (bool, error) {
|
|
||||||
// TODO: Use windows equivalent
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLinked(os.FileInfo) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func locateDummyIfEmpty(path string) (string, error) {
|
|
||||||
children, err := ioutil.ReadDir(path)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if len(children) != 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
dummyFile, err := ioutil.TempFile(path, "fsutils-dummy")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
name := dummyFile.Name()
|
|
||||||
err = dummyFile.Close()
|
|
||||||
return name, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupportsDType returns whether the filesystem mounted on path supports d_type
|
|
||||||
func SupportsDType(path string) (bool, error) {
|
|
||||||
// locate dummy so that we have at least one dirent
|
|
||||||
dummy, err := locateDummyIfEmpty(path)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if dummy != "" {
|
|
||||||
defer os.Remove(dummy)
|
|
||||||
}
|
|
||||||
|
|
||||||
visited := 0
|
|
||||||
supportsDType := true
|
|
||||||
fn := func(ent *syscall.Dirent) bool {
|
|
||||||
visited++
|
|
||||||
if ent.Type == syscall.DT_UNKNOWN {
|
|
||||||
supportsDType = false
|
|
||||||
// stop iteration
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// continue iteration
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err = iterateReadDir(path, fn); err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if visited == 0 {
|
|
||||||
return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
|
|
||||||
}
|
|
||||||
return supportsDType, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
|
|
||||||
d, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer d.Close()
|
|
||||||
fd := int(d.Fd())
|
|
||||||
buf := make([]byte, 4096)
|
|
||||||
for {
|
|
||||||
nbytes, err := syscall.ReadDirent(fd, buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if nbytes == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for off := 0; off < nbytes; {
|
|
||||||
ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
|
|
||||||
if stop := fn(ent); stop {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
off += int(ent.Reclen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
// Usage of disk information
|
|
||||||
type Usage struct {
|
|
||||||
Inodes int64
|
|
||||||
Size int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiskUsage counts the number of inodes and disk usage for the resources under
|
|
||||||
// path.
|
|
||||||
func DiskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
|
||||||
return diskUsage(ctx, roots...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiffUsage counts the numbers of inodes and disk usage in the
|
|
||||||
// diff between the 2 directories. The first path is intended
|
|
||||||
// as the base directory and the second as the changed directory.
|
|
||||||
func DiffUsage(ctx context.Context, a, b string) (Usage, error) {
|
|
||||||
return diffUsage(ctx, a, b)
|
|
||||||
}
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// blocksUnitSize is the unit used by `st_blocks` in `stat` in bytes.
|
|
||||||
// See https://man7.org/linux/man-pages/man2/stat.2.html
|
|
||||||
// st_blocks
|
|
||||||
// This field indicates the number of blocks allocated to the
|
|
||||||
// file, in 512-byte units. (This may be smaller than
|
|
||||||
// st_size/512 when the file has holes.)
|
|
||||||
const blocksUnitSize = 512
|
|
||||||
|
|
||||||
type inode struct {
|
|
||||||
// TODO(stevvooe): Can probably reduce memory usage by not tracking
|
|
||||||
// device, but we can leave this right for now.
|
|
||||||
dev, ino uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newInode(stat *syscall.Stat_t) inode {
|
|
||||||
return inode{
|
|
||||||
// Dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
|
|
||||||
dev: uint64(stat.Dev), // nolint: unconvert
|
|
||||||
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
|
|
||||||
ino: uint64(stat.Ino), // nolint: unconvert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
|
||||||
|
|
||||||
var (
|
|
||||||
size int64
|
|
||||||
inodes = map[inode]struct{}{} // expensive!
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, root := range roots {
|
|
||||||
if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
stat := fi.Sys().(*syscall.Stat_t)
|
|
||||||
inoKey := newInode(stat)
|
|
||||||
if _, ok := inodes[inoKey]; !ok {
|
|
||||||
inodes[inoKey] = struct{}{}
|
|
||||||
size += stat.Blocks * blocksUnitSize
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return Usage{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Usage{
|
|
||||||
Inodes: int64(len(inodes)),
|
|
||||||
Size: size,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
|
||||||
var (
|
|
||||||
size int64
|
|
||||||
inodes = map[inode]struct{}{} // expensive!
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
|
||||||
stat := fi.Sys().(*syscall.Stat_t)
|
|
||||||
inoKey := newInode(stat)
|
|
||||||
if _, ok := inodes[inoKey]; !ok {
|
|
||||||
inodes[inoKey] = struct{}{}
|
|
||||||
size += stat.Blocks * blocksUnitSize
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return Usage{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Usage{
|
|
||||||
Inodes: int64(len(inodes)),
|
|
||||||
Size: size,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
// +build windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
|
|
||||||
var (
|
|
||||||
size int64
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO(stevvooe): Support inodes (or equivalent) for windows.
|
|
||||||
|
|
||||||
for _, root := range roots {
|
|
||||||
if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
size += fi.Size()
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return Usage{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Usage{
|
|
||||||
Size: size,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
|
||||||
var (
|
|
||||||
size int64
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
|
||||||
size += fi.Size()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}); err != nil {
|
|
||||||
return Usage{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return Usage{
|
|
||||||
Size: size,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
// GetLinkInfo returns an identifier representing the node a hardlink is pointing
|
|
||||||
// to. If the file is not hard linked then 0 will be returned.
|
|
||||||
func GetLinkInfo(fi os.FileInfo) (uint64, bool) {
|
|
||||||
return getLinkInfo(fi)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getLinkSource returns a path for the given name and
|
|
||||||
// file info to its link source in the provided inode
|
|
||||||
// map. If the given file name is not in the map and
|
|
||||||
// has other links, it is added to the inode map
|
|
||||||
// to be a source for other link locations.
|
|
||||||
func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
|
|
||||||
inode, isHardlink := getLinkInfo(fi)
|
|
||||||
if !isHardlink {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
path, ok := inodes[inode]
|
|
||||||
if !ok {
|
|
||||||
inodes[inode] = name
|
|
||||||
}
|
|
||||||
return path, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
|
|
||||||
s, ok := fi.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
|
|
||||||
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 // nolint: unconvert
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errTooManyLinks = errors.New("too many links")
|
|
||||||
)
|
|
||||||
|
|
||||||
type currentPath struct {
|
|
||||||
path string
|
|
||||||
f os.FileInfo
|
|
||||||
fullPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func pathChange(lower, upper *currentPath) (ChangeKind, string) {
|
|
||||||
if lower == nil {
|
|
||||||
if upper == nil {
|
|
||||||
panic("cannot compare nil paths")
|
|
||||||
}
|
|
||||||
return ChangeKindAdd, upper.path
|
|
||||||
}
|
|
||||||
if upper == nil {
|
|
||||||
return ChangeKindDelete, lower.path
|
|
||||||
}
|
|
||||||
|
|
||||||
switch i := directoryCompare(lower.path, upper.path); {
|
|
||||||
case i < 0:
|
|
||||||
// File in lower that is not in upper
|
|
||||||
return ChangeKindDelete, lower.path
|
|
||||||
case i > 0:
|
|
||||||
// File in upper that is not in lower
|
|
||||||
return ChangeKindAdd, upper.path
|
|
||||||
default:
|
|
||||||
return ChangeKindModify, upper.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func directoryCompare(a, b string) int {
|
|
||||||
l := len(a)
|
|
||||||
if len(b) < l {
|
|
||||||
l = len(b)
|
|
||||||
}
|
|
||||||
for i := 0; i < l; i++ {
|
|
||||||
c1, c2 := a[i], b[i]
|
|
||||||
if c1 == filepath.Separator {
|
|
||||||
c1 = byte(0)
|
|
||||||
}
|
|
||||||
if c2 == filepath.Separator {
|
|
||||||
c2 = byte(0)
|
|
||||||
}
|
|
||||||
if c1 < c2 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if c1 > c2 {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(a) < len(b) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
if len(a) > len(b) {
|
|
||||||
return +1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func sameFile(f1, f2 *currentPath) (bool, error) {
|
|
||||||
if os.SameFile(f1.f, f2.f) {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
equalStat, err := compareSysStat(f1.f.Sys(), f2.f.Sys())
|
|
||||||
if err != nil || !equalStat {
|
|
||||||
return equalStat, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if eq, err := compareCapabilities(f1.fullPath, f2.fullPath); err != nil || !eq {
|
|
||||||
return eq, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not a directory also check size, modtime, and content
|
|
||||||
if !f1.f.IsDir() {
|
|
||||||
if f1.f.Size() != f2.f.Size() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
t1 := f1.f.ModTime()
|
|
||||||
t2 := f2.f.ModTime()
|
|
||||||
|
|
||||||
if t1.Unix() != t2.Unix() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the timestamp may have been truncated in both of the
|
|
||||||
// files, check content of file to determine difference
|
|
||||||
if t1.Nanosecond() == 0 && t2.Nanosecond() == 0 {
|
|
||||||
if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink {
|
|
||||||
return compareSymlinkTarget(f1.fullPath, f2.fullPath)
|
|
||||||
}
|
|
||||||
if f1.f.Size() == 0 { // if file sizes are zero length, the files are the same by definition
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return compareFileContent(f1.fullPath, f2.fullPath)
|
|
||||||
} else if t1.Nanosecond() != t2.Nanosecond() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareSymlinkTarget(p1, p2 string) (bool, error) {
|
|
||||||
t1, err := os.Readlink(p1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
t2, err := os.Readlink(p2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return t1 == t2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const compareChuckSize = 32 * 1024
|
|
||||||
|
|
||||||
// compareFileContent compares the content of 2 same sized files
|
|
||||||
// by comparing each byte.
|
|
||||||
func compareFileContent(p1, p2 string) (bool, error) {
|
|
||||||
f1, err := os.Open(p1)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f1.Close()
|
|
||||||
f2, err := os.Open(p2)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f2.Close()
|
|
||||||
|
|
||||||
b1 := make([]byte, compareChuckSize)
|
|
||||||
b2 := make([]byte, compareChuckSize)
|
|
||||||
for {
|
|
||||||
n1, err1 := f1.Read(b1)
|
|
||||||
if err1 != nil && err1 != io.EOF {
|
|
||||||
return false, err1
|
|
||||||
}
|
|
||||||
n2, err2 := f2.Read(b2)
|
|
||||||
if err2 != nil && err2 != io.EOF {
|
|
||||||
return false, err2
|
|
||||||
}
|
|
||||||
if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if err1 == io.EOF && err2 == io.EOF {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pathWalk(ctx context.Context, root string, pathC chan<- *currentPath) error {
|
|
||||||
return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebase path
|
|
||||||
path, err = filepath.Rel(root, path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
path = filepath.Join(string(os.PathSeparator), path)
|
|
||||||
|
|
||||||
// Skip root
|
|
||||||
if path == string(os.PathSeparator) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p := ¤tPath{
|
|
||||||
path: path,
|
|
||||||
f: f,
|
|
||||||
fullPath: filepath.Join(root, path),
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case pathC <- p:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
case p := <-pathC:
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RootPath joins a path with a root, evaluating and bounding any
|
|
||||||
// symlink to the root directory.
|
|
||||||
func RootPath(root, path string) (string, error) {
|
|
||||||
if path == "" {
|
|
||||||
return root, nil
|
|
||||||
}
|
|
||||||
var linksWalked int // to protect against cycles
|
|
||||||
for {
|
|
||||||
i := linksWalked
|
|
||||||
newpath, err := walkLinks(root, path, &linksWalked)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
path = newpath
|
|
||||||
if i == linksWalked {
|
|
||||||
newpath = filepath.Join("/", newpath)
|
|
||||||
if path == newpath {
|
|
||||||
return filepath.Join(root, newpath), nil
|
|
||||||
}
|
|
||||||
path = newpath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func walkLink(root, path string, linksWalked *int) (newpath string, islink bool, err error) {
|
|
||||||
if *linksWalked > 255 {
|
|
||||||
return "", false, errTooManyLinks
|
|
||||||
}
|
|
||||||
|
|
||||||
path = filepath.Join("/", path)
|
|
||||||
if path == "/" {
|
|
||||||
return path, false, nil
|
|
||||||
}
|
|
||||||
realPath := filepath.Join(root, path)
|
|
||||||
|
|
||||||
fi, err := os.Lstat(realPath)
|
|
||||||
if err != nil {
|
|
||||||
// If path does not yet exist, treat as non-symlink
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return path, false, nil
|
|
||||||
}
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
if fi.Mode()&os.ModeSymlink == 0 {
|
|
||||||
return path, false, nil
|
|
||||||
}
|
|
||||||
newpath, err = os.Readlink(realPath)
|
|
||||||
if err != nil {
|
|
||||||
return "", false, err
|
|
||||||
}
|
|
||||||
*linksWalked++
|
|
||||||
return newpath, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func walkLinks(root, path string, linksWalked *int) (string, error) {
|
|
||||||
switch dir, file := filepath.Split(path); {
|
|
||||||
case dir == "":
|
|
||||||
newpath, _, err := walkLink(root, file, linksWalked)
|
|
||||||
return newpath, err
|
|
||||||
case file == "":
|
|
||||||
if os.IsPathSeparator(dir[len(dir)-1]) {
|
|
||||||
if dir == "/" {
|
|
||||||
return dir, nil
|
|
||||||
}
|
|
||||||
return walkLinks(root, dir[:len(dir)-1], linksWalked)
|
|
||||||
}
|
|
||||||
newpath, _, err := walkLink(root, dir, linksWalked)
|
|
||||||
return newpath, err
|
|
||||||
default:
|
|
||||||
newdir, err := walkLinks(root, dir, linksWalked)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
newpath, islink, err := walkLink(root, filepath.Join(newdir, file), linksWalked)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if !islink {
|
|
||||||
return newpath, nil
|
|
||||||
}
|
|
||||||
if filepath.IsAbs(newpath) {
|
|
||||||
return newpath, nil
|
|
||||||
}
|
|
||||||
return filepath.Join(newdir, newpath), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
// +build darwin freebsd
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StatAtime returns the access time from a stat struct
|
|
||||||
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
|
|
||||||
return st.Atimespec
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatCtime returns the created time from a stat struct
|
|
||||||
func StatCtime(st *syscall.Stat_t) syscall.Timespec {
|
|
||||||
return st.Ctimespec
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatMtime returns the modified time from a stat struct
|
|
||||||
func StatMtime(st *syscall.Stat_t) syscall.Timespec {
|
|
||||||
return st.Mtimespec
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatATimeAsTime returns the access time as a time.Time
|
|
||||||
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
|
|
||||||
return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// +build linux openbsd
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StatAtime returns the Atim
|
|
||||||
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
|
|
||||||
return st.Atim
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatCtime returns the Ctim
|
|
||||||
func StatCtime(st *syscall.Stat_t) syscall.Timespec {
|
|
||||||
return st.Ctim
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatMtime returns the Mtim
|
|
||||||
func StatMtime(st *syscall.Stat_t) syscall.Timespec {
|
|
||||||
return st.Mtim
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatATimeAsTime returns st.Atim as a time.Time
|
|
||||||
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
|
|
||||||
// The int64 conversions ensure the line compiles for 32-bit systems as well.
|
|
||||||
return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package fs
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Gnu tar and the go tar writer don't have sub-second mtime
|
|
||||||
// precision, which is problematic when we apply changes via tar
|
|
||||||
// files, we handle this by comparing for exact times, *or* same
|
|
||||||
// second count and either a or b having exactly 0 nanoseconds
|
|
||||||
func sameFsTime(a, b time.Time) bool {
|
|
||||||
return a == b ||
|
|
||||||
(a.Unix() == b.Unix() &&
|
|
||||||
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
This package is for internal use only. It is intended to only have
|
|
||||||
temporary changes before they are upstreamed to golang.org/x/sys/
|
|
||||||
(a.k.a. https://github.com/golang/sys).
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright The containerd Authors.
|
|
||||||
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
mksyscall="$(go env GOROOT)/src/syscall/mksyscall.pl"
|
|
||||||
|
|
||||||
fix() {
|
|
||||||
sed 's,^package syscall$,package sysx,' \
|
|
||||||
| sed 's,^import "unsafe"$,import (\n\t"syscall"\n\t"unsafe"\n),' \
|
|
||||||
| gofmt -r='BytePtrFromString -> syscall.BytePtrFromString' \
|
|
||||||
| gofmt -r='Syscall6 -> syscall.Syscall6' \
|
|
||||||
| gofmt -r='Syscall -> syscall.Syscall' \
|
|
||||||
| gofmt -r='SYS_GETXATTR -> syscall.SYS_GETXATTR' \
|
|
||||||
| gofmt -r='SYS_LISTXATTR -> syscall.SYS_LISTXATTR' \
|
|
||||||
| gofmt -r='SYS_SETXATTR -> syscall.SYS_SETXATTR' \
|
|
||||||
| gofmt -r='SYS_REMOVEXATTR -> syscall.SYS_REMOVEXATTR' \
|
|
||||||
| gofmt -r='SYS_LGETXATTR -> syscall.SYS_LGETXATTR' \
|
|
||||||
| gofmt -r='SYS_LLISTXATTR -> syscall.SYS_LLISTXATTR' \
|
|
||||||
| gofmt -r='SYS_LSETXATTR -> syscall.SYS_LSETXATTR' \
|
|
||||||
| gofmt -r='SYS_LREMOVEXATTR -> syscall.SYS_LREMOVEXATTR'
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$GOARCH" == "" ] || [ "$GOOS" == "" ]; then
|
|
||||||
echo "Must specify \$GOARCH and \$GOOS"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkargs=""
|
|
||||||
|
|
||||||
if [ "$GOARCH" == "386" ] || [ "$GOARCH" == "arm" ]; then
|
|
||||||
mkargs="-l32"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for f in "$@"; do
|
|
||||||
$mksyscall $mkargs "${f}_${GOOS}.go" | fix > "${f}_${GOOS}_${GOARCH}.go"
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sysx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ENODATA = syscall.ENODATA
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sysx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This should actually be a set that contains ENOENT and EPERM
|
|
||||||
const ENODATA = syscall.ENOENT
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
// +build darwin freebsd openbsd
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sysx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ENODATA = syscall.ENOATTR
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
// +build linux darwin
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sysx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Listxattr calls syscall listxattr and reads all content
|
|
||||||
// and returns a string array
|
|
||||||
func Listxattr(path string) ([]string, error) {
|
|
||||||
return listxattrAll(path, unix.Listxattr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removexattr calls syscall removexattr
|
|
||||||
func Removexattr(path string, attr string) (err error) {
|
|
||||||
return unix.Removexattr(path, attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setxattr calls syscall setxattr
|
|
||||||
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
|
||||||
return unix.Setxattr(path, attr, data, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getxattr calls syscall getxattr
|
|
||||||
func Getxattr(path, attr string) ([]byte, error) {
|
|
||||||
return getxattrAll(path, attr, unix.Getxattr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LListxattr lists xattrs, not following symlinks
|
|
||||||
func LListxattr(path string) ([]string, error) {
|
|
||||||
return listxattrAll(path, unix.Llistxattr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LRemovexattr removes an xattr, not following symlinks
|
|
||||||
func LRemovexattr(path string, attr string) (err error) {
|
|
||||||
return unix.Lremovexattr(path, attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LSetxattr sets an xattr, not following symlinks
|
|
||||||
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
|
|
||||||
return unix.Lsetxattr(path, attr, data, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LGetxattr gets an xattr, not following symlinks
|
|
||||||
func LGetxattr(path, attr string) ([]byte, error) {
|
|
||||||
return getxattrAll(path, attr, unix.Lgetxattr)
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultXattrBufferSize = 128
|
|
||||||
|
|
||||||
type listxattrFunc func(path string, dest []byte) (int, error)
|
|
||||||
|
|
||||||
func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
|
|
||||||
buf := make([]byte, defaultXattrBufferSize)
|
|
||||||
n, err := listFunc(path, buf)
|
|
||||||
for err == unix.ERANGE {
|
|
||||||
// Buffer too small, use zero-sized buffer to get the actual size
|
|
||||||
n, err = listFunc(path, []byte{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buf = make([]byte, n)
|
|
||||||
n, err = listFunc(path, buf)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := bytes.Split(bytes.TrimSuffix(buf[:n], []byte{0}), []byte{0})
|
|
||||||
var entries []string
|
|
||||||
for _, p := range ps {
|
|
||||||
if len(p) > 0 {
|
|
||||||
entries = append(entries, string(p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type getxattrFunc func(string, string, []byte) (int, error)
|
|
||||||
|
|
||||||
func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
|
|
||||||
buf := make([]byte, defaultXattrBufferSize)
|
|
||||||
n, err := getFunc(path, attr, buf)
|
|
||||||
for err == unix.ERANGE {
|
|
||||||
// Buffer too small, use zero-sized buffer to get the actual size
|
|
||||||
n, err = getFunc(path, attr, []byte{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buf = make([]byte, n)
|
|
||||||
n, err = getFunc(path, attr, buf)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf[:n], nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
// +build !linux,!darwin
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sysx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errUnsupported = errors.New("extended attributes unsupported on " + runtime.GOOS)
|
|
||||||
|
|
||||||
// Listxattr calls syscall listxattr and reads all content
|
|
||||||
// and returns a string array
|
|
||||||
func Listxattr(path string) ([]string, error) {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removexattr calls syscall removexattr
|
|
||||||
func Removexattr(path string, attr string) (err error) {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setxattr calls syscall setxattr
|
|
||||||
func Setxattr(path string, attr string, data []byte, flags int) (err error) {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getxattr calls syscall getxattr
|
|
||||||
func Getxattr(path, attr string) ([]byte, error) {
|
|
||||||
return []byte{}, errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// LListxattr lists xattrs, not following symlinks
|
|
||||||
func LListxattr(path string) ([]string, error) {
|
|
||||||
return []string{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LRemovexattr removes an xattr, not following symlinks
|
|
||||||
func LRemovexattr(path string, attr string) (err error) {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// LSetxattr sets an xattr, not following symlinks
|
|
||||||
func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// LGetxattr gets an xattr, not following symlinks
|
|
||||||
func LGetxattr(path, attr string) ([]byte, error) {
|
|
||||||
return []byte{}, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1274,7 +1274,7 @@ definitions:
|
||||||
type: "object"
|
type: "object"
|
||||||
properties:
|
properties:
|
||||||
Bridge:
|
Bridge:
|
||||||
description: Name of the network's bridge (for example, `docker0`).
|
description: Name of the network'a bridge (for example, `docker0`).
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "docker0"
|
example: "docker0"
|
||||||
SandboxID:
|
SandboxID:
|
||||||
|
|
@ -10050,7 +10050,7 @@ paths:
|
||||||
description: |
|
description: |
|
||||||
Address or interface to use for data path traffic (format:
|
Address or interface to use for data path traffic (format:
|
||||||
`<ip|interface>`), for example, `192.168.1.1`, or an interface,
|
`<ip|interface>`), for example, `192.168.1.1`, or an interface,
|
||||||
like `eth0`. If `DataPathAddr` is unspecified, the same address
|
like `eth0`. If `DataPathAddr` is unspecified, the same addres
|
||||||
as `AdvertiseAddr` is used.
|
as `AdvertiseAddr` is used.
|
||||||
|
|
||||||
The `DataPathAddr` specifies the address that global scope
|
The `DataPathAddr` specifies the address that global scope
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildCancel requests the daemon to cancel the ongoing build request.
|
// BuildCancel requests the daemon to cancel ongoing build request
|
||||||
func (cli *Client) BuildCancel(ctx context.Context, id string) error {
|
func (cli *Client) BuildCancel(ctx context.Context, id string) error {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("id", id)
|
query.Set("id", id)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
Package client is a Go client for the Docker Engine API.
|
Package client is a Go client for the Docker Engine API.
|
||||||
|
|
||||||
For more information about the Engine API, see the documentation:
|
For more information about the Engine API, see the documentation:
|
||||||
https://docs.docker.com/engine/reference/api/
|
https://docs.docker.com/engine/api/
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigCreate creates a new config.
|
// ConfigCreate creates a new Config.
|
||||||
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
func (cli *Client) ConfigCreate(ctx context.Context, config swarm.ConfigSpec) (types.ConfigCreateResponse, error) {
|
||||||
var response types.ConfigCreateResponse
|
var response types.ConfigCreateResponse
|
||||||
if err := cli.NewVersionError("1.30", "config create"); err != nil {
|
if err := cli.NewVersionError("1.30", "config create"); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package client // import "github.com/docker/docker/client"
|
||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
// ConfigRemove removes a config.
|
// ConfigRemove removes a Config.
|
||||||
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
||||||
if err := cli.NewVersionError("1.30", "config remove"); err != nil {
|
if err := cli.NewVersionError("1.30", "config remove"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigUpdate attempts to update a config
|
// ConfigUpdate attempts to update a Config
|
||||||
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
|
func (cli *Client) ConfigUpdate(ctx context.Context, id string, version swarm.Version, config swarm.ConfigSpec) error {
|
||||||
if err := cli.NewVersionError("1.30", "config update"); err != nil {
|
if err := cli.NewVersionError("1.30", "config update"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerCommit applies changes to a container and creates a new tagged image.
|
// ContainerCommit applies changes into a container and creates a new tagged image.
|
||||||
func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
|
func (cli *Client) ContainerCommit(ctx context.Context, container string, options types.ContainerCommitOptions) (types.IDResponse, error) {
|
||||||
var repository, tag string
|
var repository, tag string
|
||||||
if options.Reference != "" {
|
if options.Reference != "" {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerStatPath returns stat information about a path inside the container filesystem.
|
// ContainerStatPath returns Stat information about a path inside the container filesystem.
|
||||||
func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) {
|
func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path string) (types.ContainerPathStat, error) {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
|
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ type configWrapper struct {
|
||||||
Platform *specs.Platform
|
Platform *specs.Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerCreate creates a new container based on the given configuration.
|
// ContainerCreate creates a new container based in the given configuration.
|
||||||
// It can be associated with a name, but it's not mandatory.
|
// It can be associated with a name, but it's not mandatory.
|
||||||
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
|
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
|
||||||
var response container.ContainerCreateCreatedBody
|
var response container.ContainerCreateCreatedBody
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerRestart stops and starts a container again.
|
// ContainerRestart stops and starts a container again.
|
||||||
// It makes the daemon wait for the container to be up again for
|
// It makes the daemon to wait for the container to be up again for
|
||||||
// a specific amount of time, given the timeout.
|
// a specific amount of time, given the timeout.
|
||||||
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
|
func (cli *Client) ContainerRestart(ctx context.Context, containerID string, timeout *time.Duration) error {
|
||||||
query := url.Values{}
|
query := url.Values{}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerUpdate updates the resources of a container.
|
// ContainerUpdate updates resources of a container
|
||||||
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
|
func (cli *Client) ContainerUpdate(ctx context.Context, containerID string, updateConfig container.UpdateConfig) (container.ContainerUpdateOKBody, error) {
|
||||||
var response container.ContainerUpdateOKBody
|
var response container.ContainerUpdateOKBody
|
||||||
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
|
serverResp, err := cli.post(ctx, "/containers/"+containerID+"/update", nil, updateConfig, nil)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
registrytypes "github.com/docker/docker/api/types/registry"
|
registrytypes "github.com/docker/docker/api/types/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DistributionInspect returns the image digest with the full manifest.
|
// DistributionInspect returns the image digest with full Manifest
|
||||||
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) {
|
func (cli *Client) DistributionInspect(ctx context.Context, image, encodedRegistryAuth string) (registrytypes.DistributionInspect, error) {
|
||||||
// Contact the registry to retrieve digest and platform information
|
// Contact the registry to retrieve digest and platform information
|
||||||
var distributionInspect registrytypes.DistributionInspect
|
var distributionInspect registrytypes.DistributionInspect
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import (
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageBuild sends a request to the daemon to build images.
|
// ImageBuild sends request to the daemon to build images.
|
||||||
// The Body in the response implements an io.ReadCloser and it's up to the caller to
|
// The Body in the response implement an io.ReadCloser and it's up to the caller to
|
||||||
// close it.
|
// close it.
|
||||||
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||||
query, err := cli.imageBuildOptionsToQuery(options)
|
query, err := cli.imageBuildOptionsToQuery(options)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageCreate creates a new image based on the parent options.
|
// ImageCreate creates a new image based in the parent options.
|
||||||
// It returns the JSON content in the response body.
|
// It returns the JSON content in the response body.
|
||||||
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
|
func (cli *Client) ImageCreate(ctx context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
|
||||||
ref, err := reference.ParseNormalizedNamed(parentReference)
|
ref, err := reference.ParseNormalizedNamed(parentReference)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageImport creates a new image based on the source options.
|
// ImageImport creates a new image based in the source options.
|
||||||
// It returns the JSON content in the response body.
|
// It returns the JSON content in the response body.
|
||||||
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
|
func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
|
||||||
if ref != "" {
|
if ref != "" {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageSearch makes the docker host search by a term in a remote registry.
|
// ImageSearch makes the docker host to search by a term in a remote registry.
|
||||||
// The list of results is not sorted in any fashion.
|
// The list of results is not sorted in any fashion.
|
||||||
func (cli *Client) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) {
|
func (cli *Client) ImageSearch(ctx context.Context, term string, options types.ImageSearchOptions) ([]registry.SearchResult, error) {
|
||||||
var results []registry.SearchResult
|
var results []registry.SearchResult
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecretCreate creates a new secret.
|
// SecretCreate creates a new Secret.
|
||||||
func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
func (cli *Client) SecretCreate(ctx context.Context, secret swarm.SecretSpec) (types.SecretCreateResponse, error) {
|
||||||
var response types.SecretCreateResponse
|
var response types.SecretCreateResponse
|
||||||
if err := cli.NewVersionError("1.25", "secret create"); err != nil {
|
if err := cli.NewVersionError("1.25", "secret create"); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package client // import "github.com/docker/docker/client"
|
||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
// SecretRemove removes a secret.
|
// SecretRemove removes a Secret.
|
||||||
func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
||||||
if err := cli.NewVersionError("1.25", "secret remove"); err != nil {
|
if err := cli.NewVersionError("1.25", "secret remove"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecretUpdate attempts to update a secret.
|
// SecretUpdate attempts to update a Secret
|
||||||
func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
func (cli *Client) SecretUpdate(ctx context.Context, id string, version swarm.Version, secret swarm.SecretSpec) error {
|
||||||
if err := cli.NewVersionError("1.25", "secret update"); err != nil {
|
if err := cli.NewVersionError("1.25", "secret update"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceCreate creates a new service.
|
// ServiceCreate creates a new Service.
|
||||||
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
|
func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec, options types.ServiceCreateOptions) (types.ServiceCreateResponse, error) {
|
||||||
var response types.ServiceCreateResponse
|
var response types.ServiceCreateResponse
|
||||||
headers := map[string][]string{
|
headers := map[string][]string{
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskInspectWithRaw returns the task information and its raw representation.
|
// TaskInspectWithRaw returns the task information and its raw representation..
|
||||||
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm.Task, []byte, error) {
|
||||||
if taskID == "" {
|
if taskID == "" {
|
||||||
return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID}
|
return swarm.Task{}, nil, objectNotFoundError{object: "task", id: taskID}
|
||||||
|
|
|
||||||
|
|
@ -402,24 +402,10 @@ func fillGo18FileTypeBits(mode int64, fi os.FileInfo) int64 {
|
||||||
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
|
// ReadSecurityXattrToTarHeader reads security.capability xattr from filesystem
|
||||||
// to a tar header
|
// to a tar header
|
||||||
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
|
func ReadSecurityXattrToTarHeader(path string, hdr *tar.Header) error {
|
||||||
const (
|
|
||||||
// Values based on linux/include/uapi/linux/capability.h
|
|
||||||
xattrCapsSz2 = 20
|
|
||||||
versionOffset = 3
|
|
||||||
vfsCapRevision2 = 2
|
|
||||||
vfsCapRevision3 = 3
|
|
||||||
)
|
|
||||||
capability, _ := system.Lgetxattr(path, "security.capability")
|
capability, _ := system.Lgetxattr(path, "security.capability")
|
||||||
if capability != nil {
|
if capability != nil {
|
||||||
length := len(capability)
|
|
||||||
if capability[versionOffset] == vfsCapRevision3 {
|
|
||||||
// Convert VFS_CAP_REVISION_3 to VFS_CAP_REVISION_2 as root UID makes no
|
|
||||||
// sense outside the user namespace the archive is built in.
|
|
||||||
capability[versionOffset] = vfsCapRevision2
|
|
||||||
length = xattrCapsSz2
|
|
||||||
}
|
|
||||||
hdr.Xattrs = make(map[string]string)
|
hdr.Xattrs = make(map[string]string)
|
||||||
hdr.Xattrs["security.capability"] = string(capability[:length])
|
hdr.Xattrs["security.capability"] = string(capability)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -753,13 +739,18 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ta := newTarAppender(
|
ta := newTarAppender(
|
||||||
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
|
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
|
||||||
compressWriter,
|
compressWriter,
|
||||||
options.ChownOpts,
|
options.ChownOpts,
|
||||||
)
|
)
|
||||||
ta.WhiteoutConverter = getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
|
ta.WhiteoutConverter = whiteoutConverter
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// Make sure to check the error on Close.
|
// Make sure to check the error on Close.
|
||||||
|
|
@ -917,7 +908,10 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
|
||||||
var dirs []*tar.Header
|
var dirs []*tar.Header
|
||||||
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||||
rootIDs := idMapping.RootPair()
|
rootIDs := idMapping.RootPair()
|
||||||
whiteoutConverter := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
|
whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate through the files in the archive.
|
// Iterate through the files in the archive.
|
||||||
loop:
|
loop:
|
||||||
|
|
@ -956,7 +950,7 @@ loop:
|
||||||
parent := filepath.Dir(hdr.Name)
|
parent := filepath.Dir(hdr.Name)
|
||||||
parentPath := filepath.Join(dest, parent)
|
parentPath := filepath.Join(dest, parent)
|
||||||
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) {
|
||||||
err = idtools.MkdirAllAndChownNew(parentPath, 0777, rootIDs)
|
err = idtools.MkdirAllAndChownNew(parentPath, 0755, rootIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,29 +2,26 @@ package archive // import "github.com/docker/docker/pkg/archive"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containerd/continuity/fs"
|
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/moby/sys/mount"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) tarWhiteoutConverter {
|
func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) (tarWhiteoutConverter, error) {
|
||||||
if format == OverlayWhiteoutFormat {
|
if format == OverlayWhiteoutFormat {
|
||||||
return overlayWhiteoutConverter{inUserNS: inUserNS}
|
if inUserNS {
|
||||||
|
return nil, errors.New("specifying OverlayWhiteoutFormat is not allowed in userns")
|
||||||
|
}
|
||||||
|
return overlayWhiteoutConverter{}, nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type overlayWhiteoutConverter struct {
|
type overlayWhiteoutConverter struct {
|
||||||
inUserNS bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
|
func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
|
||||||
|
|
@ -77,13 +74,7 @@ func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (boo
|
||||||
if base == WhiteoutOpaqueDir {
|
if base == WhiteoutOpaqueDir {
|
||||||
err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
|
err := unix.Setxattr(dir, "trusted.overlay.opaque", []byte{'y'}, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.inUserNS {
|
return false, errors.Wrapf(err, "setxattr(%q, trusted.overlay.opaque=y)", dir)
|
||||||
if err = replaceDirWithOverlayOpaque(dir); err != nil {
|
|
||||||
return false, errors.Wrapf(err, "replaceDirWithOverlayOpaque(%q) failed", dir)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false, errors.Wrapf(err, "setxattr(%q, trusted.overlay.opaque=y)", dir)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// don't write the file itself
|
// don't write the file itself
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -95,19 +86,7 @@ func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (boo
|
||||||
originalPath := filepath.Join(dir, originalBase)
|
originalPath := filepath.Join(dir, originalBase)
|
||||||
|
|
||||||
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
|
if err := unix.Mknod(originalPath, unix.S_IFCHR, 0); err != nil {
|
||||||
if c.inUserNS {
|
return false, errors.Wrapf(err, "failed to mknod(%q, S_IFCHR, 0)", originalPath)
|
||||||
// Ubuntu and a few distros support overlayfs in userns.
|
|
||||||
//
|
|
||||||
// Although we can't call mknod directly in userns (at least on bionic kernel 4.15),
|
|
||||||
// we can still create 0,0 char device using mknodChar0Overlay().
|
|
||||||
//
|
|
||||||
// NOTE: we don't need this hack for the containerd snapshotter+unpack model.
|
|
||||||
if err := mknodChar0Overlay(originalPath); err != nil {
|
|
||||||
return false, errors.Wrapf(err, "failed to mknodChar0UserNS(%q)", originalPath)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false, errors.Wrapf(err, "failed to mknod(%q, S_IFCHR, 0)", originalPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
|
if err := os.Chown(originalPath, hdr.Uid, hdr.Gid); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -119,146 +98,3 @@ func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (boo
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mknodChar0Overlay creates 0,0 char device by mounting overlayfs and unlinking.
|
|
||||||
// This function can be used for creating 0,0 char device in userns on Ubuntu.
|
|
||||||
//
|
|
||||||
// Steps:
|
|
||||||
// * Mkdir lower,upper,merged,work
|
|
||||||
// * Create lower/dummy
|
|
||||||
// * Mount overlayfs
|
|
||||||
// * Unlink merged/dummy
|
|
||||||
// * Unmount overlayfs
|
|
||||||
// * Make sure a 0,0 char device is created as upper/dummy
|
|
||||||
// * Rename upper/dummy to cleansedOriginalPath
|
|
||||||
func mknodChar0Overlay(cleansedOriginalPath string) error {
|
|
||||||
dir := filepath.Dir(cleansedOriginalPath)
|
|
||||||
tmp, err := ioutil.TempDir(dir, "mc0o")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to create a tmp directory under %s", dir)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
lower := filepath.Join(tmp, "l")
|
|
||||||
upper := filepath.Join(tmp, "u")
|
|
||||||
work := filepath.Join(tmp, "w")
|
|
||||||
merged := filepath.Join(tmp, "m")
|
|
||||||
for _, s := range []string{lower, upper, work, merged} {
|
|
||||||
if err := os.MkdirAll(s, 0700); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to mkdir %s", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dummyBase := "d"
|
|
||||||
lowerDummy := filepath.Join(lower, dummyBase)
|
|
||||||
if err := ioutil.WriteFile(lowerDummy, []byte{}, 0600); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to create a dummy lower file %s", lowerDummy)
|
|
||||||
}
|
|
||||||
// lowerdir needs ":" to be escaped: https://github.com/moby/moby/issues/40939#issuecomment-627098286
|
|
||||||
lowerEscaped := strings.ReplaceAll(lower, ":", "\\:")
|
|
||||||
mOpts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerEscaped, upper, work)
|
|
||||||
if err := mount.Mount("overlay", merged, "overlay", mOpts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mergedDummy := filepath.Join(merged, dummyBase)
|
|
||||||
if err := os.Remove(mergedDummy); err != nil {
|
|
||||||
syscall.Unmount(merged, 0)
|
|
||||||
return errors.Wrapf(err, "failed to unlink %s", mergedDummy)
|
|
||||||
}
|
|
||||||
if err := syscall.Unmount(merged, 0); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to unmount %s", merged)
|
|
||||||
}
|
|
||||||
upperDummy := filepath.Join(upper, dummyBase)
|
|
||||||
if err := isChar0(upperDummy); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.Rename(upperDummy, cleansedOriginalPath); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to rename %s to %s", upperDummy, cleansedOriginalPath)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isChar0(path string) error {
|
|
||||||
osStat, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to stat %s", path)
|
|
||||||
}
|
|
||||||
st, ok := osStat.Sys().(*syscall.Stat_t)
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("got unsupported stat for %s", path)
|
|
||||||
}
|
|
||||||
if os.FileMode(st.Mode)&syscall.S_IFMT != syscall.S_IFCHR {
|
|
||||||
return errors.Errorf("%s is not a character device, got mode=%d", path, st.Mode)
|
|
||||||
}
|
|
||||||
if st.Rdev != 0 {
|
|
||||||
return errors.Errorf("%s is not a 0,0 character device, got Rdev=%d", path, st.Rdev)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// replaceDirWithOverlayOpaque replaces path with a new directory with trusted.overlay.opaque
|
|
||||||
// xattr. The contents of the directory are preserved.
|
|
||||||
func replaceDirWithOverlayOpaque(path string) error {
|
|
||||||
if path == "/" {
|
|
||||||
return errors.New("replaceDirWithOverlayOpaque: path must not be \"/\"")
|
|
||||||
}
|
|
||||||
dir := filepath.Dir(path)
|
|
||||||
tmp, err := ioutil.TempDir(dir, "rdwoo")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to create a tmp directory under %s", dir)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmp)
|
|
||||||
// newPath is a new empty directory crafted with trusted.overlay.opaque xattr.
|
|
||||||
// we copy the content of path into newPath, remove path, and rename newPath to path.
|
|
||||||
newPath, err := createDirWithOverlayOpaque(tmp)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "createDirWithOverlayOpaque(%q) failed", tmp)
|
|
||||||
}
|
|
||||||
if err := fs.CopyDir(newPath, path); err != nil {
|
|
||||||
return errors.Wrapf(err, "CopyDir(%q, %q) failed", newPath, path)
|
|
||||||
}
|
|
||||||
if err := os.RemoveAll(path); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Rename(newPath, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// createDirWithOverlayOpaque creates a directory with trusted.overlay.opaque xattr,
|
|
||||||
// without calling setxattr, so as to allow creating opaque dir in userns on Ubuntu.
|
|
||||||
func createDirWithOverlayOpaque(tmp string) (string, error) {
|
|
||||||
lower := filepath.Join(tmp, "l")
|
|
||||||
upper := filepath.Join(tmp, "u")
|
|
||||||
work := filepath.Join(tmp, "w")
|
|
||||||
merged := filepath.Join(tmp, "m")
|
|
||||||
for _, s := range []string{lower, upper, work, merged} {
|
|
||||||
if err := os.MkdirAll(s, 0700); err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to mkdir %s", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dummyBase := "d"
|
|
||||||
lowerDummy := filepath.Join(lower, dummyBase)
|
|
||||||
if err := os.MkdirAll(lowerDummy, 0700); err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to create a dummy lower directory %s", lowerDummy)
|
|
||||||
}
|
|
||||||
// lowerdir needs ":" to be escaped: https://github.com/moby/moby/issues/40939#issuecomment-627098286
|
|
||||||
lowerEscaped := strings.ReplaceAll(lower, ":", "\\:")
|
|
||||||
mOpts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerEscaped, upper, work)
|
|
||||||
if err := mount.Mount("overlay", merged, "overlay", mOpts); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
mergedDummy := filepath.Join(merged, dummyBase)
|
|
||||||
if err := os.Remove(mergedDummy); err != nil {
|
|
||||||
syscall.Unmount(merged, 0)
|
|
||||||
return "", errors.Wrapf(err, "failed to rmdir %s", mergedDummy)
|
|
||||||
}
|
|
||||||
// upperDummy becomes a 0,0-char device file here
|
|
||||||
if err := os.Mkdir(mergedDummy, 0700); err != nil {
|
|
||||||
syscall.Unmount(merged, 0)
|
|
||||||
return "", errors.Wrapf(err, "failed to mkdir %s", mergedDummy)
|
|
||||||
}
|
|
||||||
// upperDummy becomes a directory with trusted.overlay.opaque xattr
|
|
||||||
// (but can't be verified in userns)
|
|
||||||
if err := syscall.Unmount(merged, 0); err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to unmount %s", merged)
|
|
||||||
}
|
|
||||||
upperDummy := filepath.Join(upper, dummyBase)
|
|
||||||
return upperDummy, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
package archive // import "github.com/docker/docker/pkg/archive"
|
package archive // import "github.com/docker/docker/pkg/archive"
|
||||||
|
|
||||||
func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) tarWhiteoutConverter {
|
func getWhiteoutConverter(format WhiteoutFormat, inUserNS bool) (tarWhiteoutConverter, error) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package errgroup provides synchronization, error propagation, and Context
|
|
||||||
// cancelation for groups of goroutines working on subtasks of a common task.
|
|
||||||
package errgroup
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Group is a collection of goroutines working on subtasks that are part of
|
|
||||||
// the same overall task.
|
|
||||||
//
|
|
||||||
// A zero Group is valid and does not cancel on error.
|
|
||||||
type Group struct {
|
|
||||||
cancel func()
|
|
||||||
|
|
||||||
wg sync.WaitGroup
|
|
||||||
|
|
||||||
errOnce sync.Once
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext returns a new Group and an associated Context derived from ctx.
|
|
||||||
//
|
|
||||||
// The derived Context is canceled the first time a function passed to Go
|
|
||||||
// returns a non-nil error or the first time Wait returns, whichever occurs
|
|
||||||
// first.
|
|
||||||
func WithContext(ctx context.Context) (*Group, context.Context) {
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
|
||||||
return &Group{cancel: cancel}, ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait blocks until all function calls from the Go method have returned, then
|
|
||||||
// returns the first non-nil error (if any) from them.
|
|
||||||
func (g *Group) Wait() error {
|
|
||||||
g.wg.Wait()
|
|
||||||
if g.cancel != nil {
|
|
||||||
g.cancel()
|
|
||||||
}
|
|
||||||
return g.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go calls the given function in a new goroutine.
|
|
||||||
//
|
|
||||||
// The first call to return a non-nil error cancels the group; its error will be
|
|
||||||
// returned by Wait.
|
|
||||||
func (g *Group) Go(f func() error) {
|
|
||||||
g.wg.Add(1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer g.wg.Done()
|
|
||||||
|
|
||||||
if err := f(); err != nil {
|
|
||||||
g.errOnce.Do(func() {
|
|
||||||
g.err = err
|
|
||||||
if g.cancel != nil {
|
|
||||||
g.cancel()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
@ -59,9 +59,6 @@ github.com/containerd/containerd/errdefs
|
||||||
github.com/containerd/containerd/log
|
github.com/containerd/containerd/log
|
||||||
github.com/containerd/containerd/platforms
|
github.com/containerd/containerd/platforms
|
||||||
github.com/containerd/containerd/sys
|
github.com/containerd/containerd/sys
|
||||||
# github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e
|
|
||||||
github.com/containerd/continuity/fs
|
|
||||||
github.com/containerd/continuity/sysx
|
|
||||||
# github.com/containernetworking/cni v0.8.1
|
# github.com/containernetworking/cni v0.8.1
|
||||||
github.com/containernetworking/cni/libcni
|
github.com/containernetworking/cni/libcni
|
||||||
github.com/containernetworking/cni/pkg/invoke
|
github.com/containernetworking/cni/pkg/invoke
|
||||||
|
|
@ -279,7 +276,7 @@ github.com/docker/distribution/registry/client/auth/challenge
|
||||||
github.com/docker/distribution/registry/client/transport
|
github.com/docker/distribution/registry/client/transport
|
||||||
github.com/docker/distribution/registry/storage/cache
|
github.com/docker/distribution/registry/storage/cache
|
||||||
github.com/docker/distribution/registry/storage/cache/memory
|
github.com/docker/distribution/registry/storage/cache/memory
|
||||||
# github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible
|
# github.com/docker/docker v20.10.6+incompatible
|
||||||
github.com/docker/docker/api
|
github.com/docker/docker/api
|
||||||
github.com/docker/docker/api/types
|
github.com/docker/docker/api/types
|
||||||
github.com/docker/docker/api/types/blkiodev
|
github.com/docker/docker/api/types/blkiodev
|
||||||
|
|
@ -665,7 +662,6 @@ golang.org/x/net/internal/timeseries
|
||||||
golang.org/x/net/proxy
|
golang.org/x/net/proxy
|
||||||
golang.org/x/net/trace
|
golang.org/x/net/trace
|
||||||
# golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
# golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||||
golang.org/x/sync/errgroup
|
|
||||||
golang.org/x/sync/semaphore
|
golang.org/x/sync/semaphore
|
||||||
# golang.org/x/sys v0.0.0-20210423082822-04245dca01da
|
# golang.org/x/sys v0.0.0-20210423082822-04245dca01da
|
||||||
golang.org/x/sys/cpu
|
golang.org/x/sys/cpu
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue