vendor: update c/{common,storage}
Closes: https://github.com/containers/podman/issues/25572 Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
b58250b35d
commit
7f592742b8
6
go.mod
6
go.mod
|
|
@ -14,14 +14,14 @@ require (
|
|||
github.com/checkpoint-restore/go-criu/v7 v7.2.0
|
||||
github.com/containernetworking/plugins v1.6.2
|
||||
github.com/containers/buildah v1.39.1-0.20250324153001-6d9381d08265
|
||||
github.com/containers/common v0.62.3-0.20250324121725-e360699fb3e3
|
||||
github.com/containers/common v0.62.3-0.20250326101258-442957aa798d
|
||||
github.com/containers/conmon v2.0.20+incompatible
|
||||
github.com/containers/gvisor-tap-vsock v0.8.5
|
||||
github.com/containers/image/v5 v5.34.3-0.20250311194052-d84dbab374e7
|
||||
github.com/containers/libhvee v0.10.0
|
||||
github.com/containers/ocicrypt v1.2.1
|
||||
github.com/containers/psgo v1.9.0
|
||||
github.com/containers/storage v1.57.3-0.20250310120440-ab85543c3c6a
|
||||
github.com/containers/storage v1.57.3-0.20250325222852-4d1ae4a7983d
|
||||
github.com/containers/winquit v1.1.0
|
||||
github.com/coreos/go-systemd/v22 v22.5.1-0.20231103132048-7d375ecc2b09
|
||||
github.com/crc-org/crc/v2 v2.45.0
|
||||
|
|
@ -185,7 +185,7 @@ require (
|
|||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/sftp v1.13.8 // indirect
|
||||
github.com/pkg/sftp v1.13.9 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
|
|
|
|||
12
go.sum
12
go.sum
|
|
@ -78,8 +78,8 @@ github.com/containernetworking/plugins v1.6.2 h1:pqP8Mq923TLyef5g97XfJ/xpDeVek4y
|
|||
github.com/containernetworking/plugins v1.6.2/go.mod h1:SP5UG3jDO9LtmfbBJdP+nl3A1atOtbj2MBOYsnaxy64=
|
||||
github.com/containers/buildah v1.39.1-0.20250324153001-6d9381d08265 h1:3cFRoMP4Up4sN/f2TOcCKSxiX/mbHCN5FwqHc+rw2B8=
|
||||
github.com/containers/buildah v1.39.1-0.20250324153001-6d9381d08265/go.mod h1:8DuzWORynpU4q7coSL0aElpPVMDZFoCOnz9gzqU8Ics=
|
||||
github.com/containers/common v0.62.3-0.20250324121725-e360699fb3e3 h1:+bpRXBlU6CBT1PeA5CJ0Yvc6M6jDyWb6Szja+wN3KBo=
|
||||
github.com/containers/common v0.62.3-0.20250324121725-e360699fb3e3/go.mod h1:n5C1/ox2S2r/5fcklbCkSV7Y++je/Rz+953Ecmhrnv4=
|
||||
github.com/containers/common v0.62.3-0.20250326101258-442957aa798d h1:xvajXgoMD3wfumCQqI3h+950b5areKa8BEBE2SAmZCA=
|
||||
github.com/containers/common v0.62.3-0.20250326101258-442957aa798d/go.mod h1:jEHYmXVWsev+dizV4Oh2mZrPFAEzlVeUj/zrCx1jse0=
|
||||
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
|
||||
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
|
||||
github.com/containers/gvisor-tap-vsock v0.8.5 h1:s7PA8znsZ4mamev5nNLsQqduYSlz1Ze5TWjfXnAfpEs=
|
||||
|
|
@ -96,8 +96,8 @@ github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpV
|
|||
github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ=
|
||||
github.com/containers/psgo v1.9.0 h1:eJ74jzSaCHnWt26OlKZROSyUyRcGDf+gYBdXnxrMW4g=
|
||||
github.com/containers/psgo v1.9.0/go.mod h1:0YoluUm43Mz2UnBIh1P+6V6NWcbpTL5uRtXyOcH0B5A=
|
||||
github.com/containers/storage v1.57.3-0.20250310120440-ab85543c3c6a h1:mg4hUluPeujWDzJ+UYYCkp+vI6tQp1NtNGY1lXzpVX4=
|
||||
github.com/containers/storage v1.57.3-0.20250310120440-ab85543c3c6a/go.mod h1:+TX1GlBD/Aj65Yr4duNoeBIk7Ka3k+nf3HjQ4qLJaLQ=
|
||||
github.com/containers/storage v1.57.3-0.20250325222852-4d1ae4a7983d h1:fSr4oA7haKOptg2NaPkow6yfn0YlhUgechjsofRMkDI=
|
||||
github.com/containers/storage v1.57.3-0.20250325222852-4d1ae4a7983d/go.mod h1:9/SUJfBaxCWnQv9C2RsQfiG7hU1ryHk5ToIV5N29zYY=
|
||||
github.com/containers/winquit v1.1.0 h1:jArun04BNDQvt2W0Y78kh9TazN2EIEMG5Im6/JY7+pE=
|
||||
github.com/containers/winquit v1.1.0/go.mod h1:PsPeZlnbkmGGIToMPHF1zhWjBUkd8aHjMOr/vFcPxw8=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
|
||||
|
|
@ -418,8 +418,8 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ
|
|||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.8 h1:Xt7eJ/xqXv7s0VuzFw7JXhZj6Oc1zI6l4GK8KP9sFB0=
|
||||
github.com/pkg/sftp v1.13.8/go.mod h1:DmvEkvKE2lshEeuo2JMp06yqcx9HVnR7e3zqQl42F3U=
|
||||
github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw=
|
||||
github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
|
|||
|
|
@ -597,7 +597,7 @@ func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tree, err := image.Tree(opts.WhatRequires)
|
||||
tree, err := image.Tree(ctx, opts.WhatRequires)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func (i *Image) History(ctx context.Context) ([]ImageHistory, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
layerTree, err := i.runtime.newFreshLayerTree()
|
||||
layerTree, err := i.runtime.newFreshLayerTree(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,13 +191,21 @@ func (i *Image) IsReadOnly() bool {
|
|||
}
|
||||
|
||||
// IsDangling returns true if the image is dangling, that is an untagged image
|
||||
// without children.
|
||||
// without children and not used in a manifest list.
|
||||
func (i *Image) IsDangling(ctx context.Context) (bool, error) {
|
||||
return i.isDangling(ctx, nil)
|
||||
images, layers, err := i.runtime.getImagesAndLayers()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
tree, err := i.runtime.newLayerTreeFromData(ctx, images, layers, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return i.isDangling(ctx, tree)
|
||||
}
|
||||
|
||||
// isDangling returns true if the image is dangling, that is an untagged image
|
||||
// without children. If tree is nil, it will created for this invocation only.
|
||||
// without children and not used in a manifest list. If tree is nil, it will created for this invocation only.
|
||||
func (i *Image) isDangling(ctx context.Context, tree *layerTree) (bool, error) {
|
||||
if len(i.Names()) > 0 {
|
||||
return false, nil
|
||||
|
|
@ -206,7 +214,8 @@ func (i *Image) isDangling(ctx context.Context, tree *layerTree) (bool, error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(children) == 0, nil
|
||||
_, usedInManfiestList := tree.manifestListDigests[i.Digest()]
|
||||
return (len(children) == 0 && !usedInManfiestList), nil
|
||||
}
|
||||
|
||||
// IsIntermediate returns true if the image is an intermediate image, that is
|
||||
|
|
@ -258,7 +267,7 @@ func (i *Image) TopLayer() string {
|
|||
|
||||
// Parent returns the parent image or nil if there is none
|
||||
func (i *Image) Parent(ctx context.Context) (*Image, error) {
|
||||
tree, err := i.runtime.newFreshLayerTree()
|
||||
tree, err := i.runtime.newFreshLayerTree(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -292,7 +301,7 @@ func (i *Image) Children(ctx context.Context) ([]*Image, error) {
|
|||
// created for this invocation only.
|
||||
func (i *Image) getChildren(ctx context.Context, all bool, tree *layerTree) ([]*Image, error) {
|
||||
if tree == nil {
|
||||
t, err := i.runtime.newFreshLayerTree()
|
||||
t, err := i.runtime.newFreshLayerTree(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -789,27 +798,25 @@ func (i *Image) Mount(_ context.Context, mountOptions []string, mountLabel strin
|
|||
// Mountpoint returns the path to image's mount point. The path is empty if
|
||||
// the image is not mounted.
|
||||
func (i *Image) Mountpoint() (string, error) {
|
||||
mountedTimes, err := i.runtime.store.Mounted(i.TopLayer())
|
||||
if err != nil || mountedTimes == 0 {
|
||||
if errors.Is(err, storage.ErrLayerUnknown) {
|
||||
// Can happen, Podman did it, but there's no
|
||||
// explanation why.
|
||||
err = nil
|
||||
for _, layerID := range append([]string{i.TopLayer()}, i.storageImage.MappedTopLayers...) {
|
||||
mountedTimes, err := i.runtime.store.Mounted(layerID)
|
||||
if err != nil {
|
||||
if errors.Is(err, storage.ErrLayerUnknown) {
|
||||
// Can happen, Podman did it, but there's no
|
||||
// explanation why.
|
||||
continue
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
if mountedTimes > 0 {
|
||||
layer, err := i.runtime.store.Layer(layerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.EvalSymlinks(layer.MountPoint)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
layer, err := i.runtime.store.Layer(i.TopLayer())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mountPoint, err := filepath.EvalSymlinks(layer.MountPoint)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return mountPoint, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Unmount the image. Use force to ignore the reference counter and forcefully
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
package libimage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ import (
|
|||
// Tree generates a tree for the specified image and its layers. Use
|
||||
// `traverseChildren` to traverse the layers of all children. By default, only
|
||||
// layers of the image are printed.
|
||||
func (i *Image) Tree(traverseChildren bool) (string, error) {
|
||||
func (i *Image) Tree(ctx context.Context, traverseChildren bool) (string, error) {
|
||||
// NOTE: a string builder prevents us from copying to much data around
|
||||
// and compile the string when and where needed.
|
||||
sb := &strings.Builder{}
|
||||
|
|
@ -37,7 +38,7 @@ func (i *Image) Tree(traverseChildren bool) (string, error) {
|
|||
fmt.Fprintf(sb, "No Image Layers")
|
||||
}
|
||||
|
||||
layerTree, err := i.runtime.newFreshLayerTree()
|
||||
layerTree, err := i.runtime.newFreshLayerTree(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/containers/storage"
|
||||
storageTypes "github.com/containers/storage/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -22,6 +23,10 @@ type layerTree struct {
|
|||
// emptyImages do not have any top-layer so we cannot create a
|
||||
// *layerNode for them.
|
||||
emptyImages []*Image
|
||||
// manifestList keep track of images based on their digest.
|
||||
// Library will use this map when checking if a image is dangling.
|
||||
// If an image is used in a manifestList it is NOT dangling
|
||||
manifestListDigests map[digest.Digest]struct{}
|
||||
}
|
||||
|
||||
// node returns a layerNode for the specified layerID.
|
||||
|
|
@ -90,20 +95,21 @@ func (l *layerNode) repoTags() ([]string, error) {
|
|||
}
|
||||
|
||||
// newFreshLayerTree extracts a layerTree from consistent layers and images in the local storage.
|
||||
func (r *Runtime) newFreshLayerTree() (*layerTree, error) {
|
||||
func (r *Runtime) newFreshLayerTree(ctx context.Context) (*layerTree, error) {
|
||||
images, layers, err := r.getImagesAndLayers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.newLayerTreeFromData(images, layers)
|
||||
return r.newLayerTreeFromData(ctx, images, layers, false)
|
||||
}
|
||||
|
||||
// newLayerTreeFromData extracts a layerTree from the given the layers and images.
|
||||
// The caller is responsible for (layers, images) being consistent.
|
||||
func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer) (*layerTree, error) {
|
||||
func (r *Runtime) newLayerTreeFromData(ctx context.Context, images []*Image, layers []storage.Layer, generateManifestDigestList bool) (*layerTree, error) {
|
||||
tree := layerTree{
|
||||
nodes: make(map[string]*layerNode),
|
||||
ociCache: make(map[string]*ociv1.Image),
|
||||
nodes: make(map[string]*layerNode),
|
||||
ociCache: make(map[string]*ociv1.Image),
|
||||
manifestListDigests: make(map[digest.Digest]struct{}),
|
||||
}
|
||||
|
||||
// First build a tree purely based on layer information.
|
||||
|
|
@ -124,6 +130,21 @@ func (r *Runtime) newLayerTreeFromData(images []*Image, layers []storage.Layer)
|
|||
topLayer := img.TopLayer()
|
||||
if topLayer == "" {
|
||||
tree.emptyImages = append(tree.emptyImages, img)
|
||||
// When img is a manifest list, cache the lists of
|
||||
// digests refereenced in manifest list. Digests can
|
||||
// be used to check for dangling images.
|
||||
if !generateManifestDigestList {
|
||||
continue
|
||||
}
|
||||
if manifestList, _ := img.IsManifestList(ctx); manifestList {
|
||||
mlist, err := img.ToManifestList()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, digest := range mlist.list.Instances() {
|
||||
tree.manifestListDigests[digest] = struct{}{}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
node, exists := tree.nodes[topLayer]
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ func (r *Runtime) ListImages(ctx context.Context, options *ListImagesOptions) ([
|
|||
|
||||
var tree *layerTree
|
||||
if needsLayerTree {
|
||||
tree, err = r.newLayerTreeFromData(images, snapshot.Layers)
|
||||
tree, err = r.newLayerTreeFromData(ctx, images, snapshot.Layers, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ env:
|
|||
# GCE project where images live
|
||||
IMAGE_PROJECT: "libpod-218412"
|
||||
# VM Image built in containers/automation_images
|
||||
IMAGE_SUFFIX: "c20250131t121915z-f41f40d13"
|
||||
IMAGE_SUFFIX: "c20250324t111922z-f41f40d13"
|
||||
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
|
||||
DEBIAN_CACHE_IMAGE_NAME: "debian-${IMAGE_SUFFIX}"
|
||||
|
||||
|
|
@ -170,13 +170,13 @@ vendor_task:
|
|||
cross_task:
|
||||
alias: cross
|
||||
container:
|
||||
image: golang:1.22
|
||||
image: golang:1.23
|
||||
build_script: make cross
|
||||
|
||||
gofix_task:
|
||||
alias: gofix
|
||||
container:
|
||||
image: golang:1.22
|
||||
image: golang:1.23
|
||||
build_script: go fix ./...
|
||||
test_script: git diff --exit-code
|
||||
|
||||
|
|
|
|||
|
|
@ -1,123 +1,14 @@
|
|||
# Contributing to Containers/Storage
|
||||
# Contributing to Containers/Storage
|
||||
|
||||
We'd love to have you join the community! Below summarizes the processes
|
||||
that we follow.
|
||||
We'd love to have you join the community! [Learn how to contribute](https://github.com/containers/common/blob/main/CONTRIBUTING.md) to the Containers Group Projects.
|
||||
|
||||
## Topics
|
||||
Please note that the following information is specific to this project:
|
||||
|
||||
* [Reporting Issues](#reporting-issues)
|
||||
* [Submitting Pull Requests](#submitting-pull-requests)
|
||||
* [Communications](#communications)
|
||||
<!--
|
||||
* [Becoming a Maintainer](#becoming-a-maintainer)
|
||||
-->
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Before reporting an issue, check our backlog of
|
||||
[open issues](https://github.com/containers/storage/issues)
|
||||
to see if someone else has already reported it. If so, feel free to add
|
||||
your scenario, or additional information, to the discussion. Or simply
|
||||
"subscribe" to it to be notified when it is updated.
|
||||
|
||||
If you find a new issue with the project we'd love to hear about it! The most
|
||||
important aspect of a bug report is that it includes enough information for
|
||||
us to reproduce it. So, please include as much detail as possible and try
|
||||
to remove the extra stuff that doesn't really relate to the issue itself.
|
||||
The easier it is for us to reproduce it, the faster it'll be fixed!
|
||||
|
||||
Please don't include any private/sensitive information in your issue!
|
||||
|
||||
## Submitting Pull Requests
|
||||
|
||||
No Pull Request (PR) is too small! Typos, additional comments in the code,
|
||||
new testcases, bug fixes, new features, more documentation, ... it's all
|
||||
welcome!
|
||||
|
||||
While bug fixes can first be identified via an "issue", that is not required.
|
||||
It's ok to just open up a PR with the fix, but make sure you include the same
|
||||
information you would have included in an issue - like how to reproduce it.
|
||||
|
||||
PRs for new features should include some background on what use cases the
|
||||
new code is trying to address. When possible and when it makes sense, try to break-up
|
||||
larger PRs into smaller ones - it's easier to review smaller
|
||||
code changes. But only if those smaller ones make sense as stand-alone PRs.
|
||||
|
||||
Regardless of the type of PR, all PRs should include:
|
||||
* well documented code changes
|
||||
* additional testcases. Ideally, they should fail w/o your code change applied
|
||||
* documentation changes
|
||||
|
||||
Squash your commits into logical pieces of work that might want to be reviewed
|
||||
separate from the rest of the PRs. But, squashing down to just one commit is ok
|
||||
too since in the end the entire PR will be reviewed anyway. When in doubt,
|
||||
squash.
|
||||
|
||||
PRs that fix issues should include a reference like `Closes #XXXX` in the
|
||||
commit message so that github will automatically close the referenced issue
|
||||
when the PR is merged.
|
||||
|
||||
<!--
|
||||
All PRs require at least two LGTMs (Looks Good To Me) from maintainers.
|
||||
-->
|
||||
|
||||
### Sign your PRs
|
||||
|
||||
The sign-off is a line at the end of the explanation for the patch. Your
|
||||
signature certifies that you wrote the patch or otherwise have the right to pass
|
||||
it on as an open-source patch. The rules are simple: if you can certify
|
||||
the below (from [developercertificate.org](http://developercertificate.org/)):
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
Then you just add a line to every git commit message:
|
||||
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
|
||||
Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can sign your
|
||||
commit automatically with `git commit -s`.
|
||||
* We don’t typically require 2 LGTMs for this repository.
|
||||
|
||||
## Communications
|
||||
|
||||
For general questions, or discussions, please use the
|
||||
For general questions, or discussions, please use the
|
||||
IRC group on `irc.freenode.net` called `container-projects`
|
||||
that has been setup.
|
||||
|
||||
|
|
@ -139,6 +30,6 @@ approval, or if the person requests to be removed then it is automatic.
|
|||
Normally, a maintainer will only be removed if they are considered to be
|
||||
inactive for a long period of time or are viewed as disruptive to the community.
|
||||
|
||||
The current list of maintainers can be found in the
|
||||
The current list of maintainers can be found in the
|
||||
[MAINTAINERS](MAINTAINERS) file.
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ TESTFLAGS := $(shell $(GO) test -race $(BUILDFLAGS) ./pkg/stringutils 2>&1 > /de
|
|||
# N/B: This value is managed by Renovate, manual changes are
|
||||
# possible, as long as they don't disturb the formatting
|
||||
# (i.e. DO NOT ADD A 'v' prefix!)
|
||||
GOLANGCI_LINT_VERSION := 1.64.6
|
||||
GOLANGCI_LINT_VERSION := 1.64.8
|
||||
|
||||
default all: local-binary docs local-validate local-cross ## validate all checks, build and cross-build\nbinaries and docs
|
||||
|
||||
|
|
|
|||
|
|
@ -859,23 +859,26 @@ func Tar(path string, compression Compression) (io.ReadCloser, error) {
|
|||
// TarWithOptions creates an archive from the directory at `path`, only including files whose relative
|
||||
// paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`.
|
||||
func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) {
|
||||
// Fix the source path to work with long path names. This is a no-op
|
||||
// on platforms other than Windows.
|
||||
srcPath = fixVolumePathPrefix(srcPath)
|
||||
tarWithOptionsTo := func(dest io.WriteCloser, srcPath string, options *TarOptions) (result error) {
|
||||
// Fix the source path to work with long path names. This is a no-op
|
||||
// on platforms other than Windows.
|
||||
srcPath = fixVolumePathPrefix(srcPath)
|
||||
defer func() {
|
||||
if err := dest.Close(); err != nil && result == nil {
|
||||
result = err
|
||||
}
|
||||
}()
|
||||
|
||||
pm, err := fileutils.NewPatternMatcher(options.ExcludePatterns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pm, err := fileutils.NewPatternMatcher(options.ExcludePatterns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
compressWriter, err := CompressStream(dest, options.Compression)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
compressWriter, err := CompressStream(pipeWriter, options.Compression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
ta := newTarWriter(
|
||||
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
|
||||
compressWriter,
|
||||
|
|
@ -885,16 +888,10 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|||
ta.WhiteoutConverter = GetWhiteoutConverter(options.WhiteoutFormat, options.WhiteoutData)
|
||||
ta.CopyPass = options.CopyPass
|
||||
|
||||
includeFiles := options.IncludeFiles
|
||||
defer func() {
|
||||
// Make sure to check the error on Close.
|
||||
if err := ta.TarWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close tar writer: %s", err)
|
||||
}
|
||||
if err := compressWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close compress writer: %s", err)
|
||||
}
|
||||
if err := pipeWriter.Close(); err != nil {
|
||||
logrus.Errorf("Can't close pipe writer: %s", err)
|
||||
if err := compressWriter.Close(); err != nil && result == nil {
|
||||
result = err
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
@ -908,7 +905,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|||
|
||||
stat, err := os.Lstat(srcPath)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
|
|
@ -916,22 +913,22 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|||
// 'walk' will error if "file/." is stat-ed and "file" is not a
|
||||
// directory. So, we must split the source path and use the
|
||||
// basename as the include.
|
||||
if len(options.IncludeFiles) > 0 {
|
||||
if len(includeFiles) > 0 {
|
||||
logrus.Warn("Tar: Can't archive a file with includes")
|
||||
}
|
||||
|
||||
dir, base := SplitPathDirEntry(srcPath)
|
||||
srcPath = dir
|
||||
options.IncludeFiles = []string{base}
|
||||
includeFiles = []string{base}
|
||||
}
|
||||
|
||||
if len(options.IncludeFiles) == 0 {
|
||||
options.IncludeFiles = []string{"."}
|
||||
if len(includeFiles) == 0 {
|
||||
includeFiles = []string{"."}
|
||||
}
|
||||
|
||||
seen := make(map[string]bool)
|
||||
|
||||
for _, include := range options.IncludeFiles {
|
||||
for _, include := range includeFiles {
|
||||
rebaseName := options.RebaseNames[include]
|
||||
|
||||
walkRoot := getWalkRoot(srcPath, include)
|
||||
|
|
@ -1026,10 +1023,18 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
logrus.Errorf("%s", err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
return ta.TarWriter.Close()
|
||||
}
|
||||
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
go func() {
|
||||
err := tarWithOptionsTo(pipeWriter, srcPath, options)
|
||||
if pipeErr := pipeWriter.CloseWithError(err); pipeErr != nil {
|
||||
logrus.Errorf("Can't close pipe writer: %s", pipeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
return pipeReader, nil
|
||||
|
|
@ -1216,9 +1221,6 @@ func untarHandler(tarArchive io.Reader, dest string, options *TarOptions, decomp
|
|||
if options == nil {
|
||||
options = &TarOptions{}
|
||||
}
|
||||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
|
||||
r := tarArchive
|
||||
if decompress {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
|||
if options == nil {
|
||||
options = &TarOptions{}
|
||||
}
|
||||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||
|
||||
aufsTempdir := ""
|
||||
|
|
|
|||
|
|
@ -69,9 +69,6 @@ func untarHandler(tarArchive io.Reader, dest string, options *archive.TarOptions
|
|||
options = &archive.TarOptions{}
|
||||
options.InUserNS = unshare.IsRootless()
|
||||
}
|
||||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
|
||||
idMappings := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||
rootIDs := idMappings.RootPair()
|
||||
|
|
|
|||
|
|
@ -98,9 +98,6 @@ func applyLayerHandler(dest string, layer io.Reader, options *archive.TarOptions
|
|||
options.InUserNS = true
|
||||
}
|
||||
}
|
||||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
|
||||
data, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
package chrootarchive
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
package chrootarchive
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import (
|
|||
func Exists(path string) error {
|
||||
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
|
||||
// simply checking the existence of a file.
|
||||
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, 0)
|
||||
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_EACCESS)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "faccessat", Path: path, Err: err}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ func Exists(path string) error {
|
|||
func Lexists(path string) error {
|
||||
// It uses unix.Faccessat which is a faster operation compared to os.Stat for
|
||||
// simply checking the existence of a file.
|
||||
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW)
|
||||
err := unix.Faccessat(unix.AT_FDCWD, path, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW|unix.AT_EACCESS)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "faccessat", Path: path, Err: err}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ package idtools
|
|||
import (
|
||||
"errors"
|
||||
"os/user"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
|
@ -13,16 +14,14 @@ import (
|
|||
#include <shadow/subid.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
const char *Prog = "storage";
|
||||
FILE *shadow_logfd = NULL;
|
||||
|
||||
struct subid_range get_range(struct subid_range *ranges, int i)
|
||||
{
|
||||
shadow_logfd = stderr;
|
||||
return ranges[i];
|
||||
return ranges[i];
|
||||
}
|
||||
|
||||
#if !defined(SUBID_ABI_MAJOR) || (SUBID_ABI_MAJOR < 4)
|
||||
# define subid_init libsubid_init
|
||||
# define subid_get_uid_ranges get_subuid_ranges
|
||||
# define subid_get_gid_ranges get_subgid_ranges
|
||||
#endif
|
||||
|
|
@ -30,6 +29,10 @@ struct subid_range get_range(struct subid_range *ranges, int i)
|
|||
*/
|
||||
import "C"
|
||||
|
||||
var (
|
||||
onceInit sync.Once
|
||||
)
|
||||
|
||||
func readSubid(username string, isUser bool) (ranges, error) {
|
||||
var ret ranges
|
||||
uidstr := ""
|
||||
|
|
@ -42,6 +45,10 @@ func readSubid(username string, isUser bool) (ranges, error) {
|
|||
uidstr = u.Uid
|
||||
}
|
||||
|
||||
onceInit.Do(func() {
|
||||
C.subid_init(C.CString("storage"), C.stderr)
|
||||
})
|
||||
|
||||
cUsername := C.CString(username)
|
||||
defer C.free(unsafe.Pointer(cUsername))
|
||||
|
||||
|
|
|
|||
|
|
@ -2830,7 +2830,11 @@ func (s *store) Version() ([][2]string, error) {
|
|||
return [][2]string{}, nil
|
||||
}
|
||||
|
||||
func (s *store) mount(id string, options drivers.MountOpts) (string, error) {
|
||||
func (s *store) MountImage(id string, mountOpts []string, mountLabel string) (string, error) {
|
||||
if err := validateMountOptions(mountOpts); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// We need to make sure the home mount is present when the Mount is done, which happens by possibly reinitializing the graph driver
|
||||
// in startUsingGraphDriver().
|
||||
if err := s.startUsingGraphDriver(); err != nil {
|
||||
|
|
@ -2842,57 +2846,61 @@ func (s *store) mount(id string, options drivers.MountOpts) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if options.UidMaps != nil || options.GidMaps != nil {
|
||||
options.DisableShifting = !s.canUseShifting(options.UidMaps, options.GidMaps)
|
||||
}
|
||||
var imageHomeStore roImageStore
|
||||
|
||||
// function used to have a scope for rlstore.StopWriting()
|
||||
tryMount := func() (string, error) {
|
||||
if err := rlstore.startWriting(); err != nil {
|
||||
if err := rlstore.startWriting(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rlstore.stopWriting()
|
||||
for _, s := range lstores {
|
||||
if err := s.startReading(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rlstore.stopWriting()
|
||||
if rlstore.Exists(id) {
|
||||
return rlstore.Mount(id, options)
|
||||
}
|
||||
return "", nil
|
||||
defer s.stopReading()
|
||||
}
|
||||
mountPoint, err := tryMount()
|
||||
if mountPoint != "" || err != nil {
|
||||
return mountPoint, err
|
||||
if err := s.imageStore.startWriting(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer s.imageStore.stopWriting()
|
||||
|
||||
// check if the layer is in a read-only store, and return a better error message
|
||||
for _, store := range lstores {
|
||||
if err := store.startReading(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
exists := store.Exists(id)
|
||||
store.stopReading()
|
||||
if exists {
|
||||
return "", fmt.Errorf("mounting read/only store images is not allowed: %w", ErrStoreIsReadOnly)
|
||||
cimage, err := s.imageStore.Get(id)
|
||||
if err == nil {
|
||||
imageHomeStore = s.imageStore
|
||||
} else {
|
||||
for _, s := range s.roImageStores {
|
||||
if err := s.startReading(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer s.stopReading()
|
||||
cimage, err = s.Get(id)
|
||||
if err == nil {
|
||||
imageHomeStore = s
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if cimage == nil {
|
||||
return "", fmt.Errorf("locating image with ID %q: %w", id, ErrImageUnknown)
|
||||
}
|
||||
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (s *store) MountImage(id string, mountOpts []string, mountLabel string) (string, error) {
|
||||
// Append ReadOnly option to mountOptions
|
||||
img, err := s.Image(id)
|
||||
idmappingsOpts := types.IDMappingOptions{
|
||||
HostUIDMapping: true,
|
||||
HostGIDMapping: true,
|
||||
}
|
||||
ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, rlstore, lstores, idmappingsOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := validateMountOptions(mountOpts); err != nil {
|
||||
return "", err
|
||||
if len(ilayer.UIDMap) > 0 || len(ilayer.GIDMap) > 0 {
|
||||
return "", fmt.Errorf("cannot create an image with canonical UID/GID mappings in a read-only store")
|
||||
}
|
||||
|
||||
options := drivers.MountOpts{
|
||||
MountLabel: mountLabel,
|
||||
Options: append(mountOpts, "ro"),
|
||||
}
|
||||
|
||||
return s.mount(img.TopLayer, options)
|
||||
return rlstore.Mount(ilayer.ID, options)
|
||||
}
|
||||
|
||||
func (s *store) Mount(id, mountLabel string) (string, error) {
|
||||
|
|
@ -2914,7 +2922,43 @@ func (s *store) Mount(id, mountLabel string) (string, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return s.mount(id, options)
|
||||
|
||||
// We need to make sure the home mount is present when the Mount is done, which happens by possibly reinitializing the graph driver
|
||||
// in startUsingGraphDriver().
|
||||
if err := s.startUsingGraphDriver(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer s.stopUsingGraphDriver()
|
||||
|
||||
rlstore, lstores, err := s.bothLayerStoreKindsLocked()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if options.UidMaps != nil || options.GidMaps != nil {
|
||||
options.DisableShifting = !s.canUseShifting(options.UidMaps, options.GidMaps)
|
||||
}
|
||||
|
||||
if err := rlstore.startWriting(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer rlstore.stopWriting()
|
||||
if rlstore.Exists(id) {
|
||||
return rlstore.Mount(id, options)
|
||||
}
|
||||
|
||||
// check if the layer is in a read-only store, and return a better error message
|
||||
for _, store := range lstores {
|
||||
if err := store.startReading(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
exists := store.Exists(id)
|
||||
store.stopReading()
|
||||
if exists {
|
||||
return "", fmt.Errorf("mounting read/only store images is not allowed: %w", ErrStoreIsReadOnly)
|
||||
}
|
||||
}
|
||||
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (s *store) Mounted(id string) (int, error) {
|
||||
|
|
@ -2938,7 +2982,23 @@ func (s *store) UnmountImage(id string, force bool) (bool, error) {
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return s.Unmount(img.TopLayer, force)
|
||||
|
||||
return writeToLayerStore(s, func(lstore rwLayerStore) (bool, error) {
|
||||
for _, layerID := range img.MappedTopLayers {
|
||||
l, err := lstore.Get(layerID)
|
||||
if err != nil {
|
||||
if err == ErrLayerUnknown {
|
||||
continue
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
// check if the layer with the canonical mapping is in the mapped top layers
|
||||
if len(l.UIDMap) == 0 && len(l.GIDMap) == 0 {
|
||||
return lstore.unmount(l.ID, force, false)
|
||||
}
|
||||
}
|
||||
return lstore.unmount(img.TopLayer, force, false)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *store) Unmount(id string, force bool) (bool, error) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
|
||||
"github.com/kr/fs"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -758,20 +760,39 @@ func (c *Client) Join(elem ...string) string { return path.Join(elem...) }
|
|||
// file or directory with the specified path exists, or if the specified directory
|
||||
// is not empty.
|
||||
func (c *Client) Remove(path string) error {
|
||||
err := c.removeFile(path)
|
||||
// some servers, *cough* osx *cough*, return EPERM, not ENODIR.
|
||||
// serv-u returns ssh_FX_FILE_IS_A_DIRECTORY
|
||||
// EPERM is converted to os.ErrPermission so it is not a StatusError
|
||||
if err, ok := err.(*StatusError); ok {
|
||||
switch err.Code {
|
||||
case sshFxFailure, sshFxFileIsADirectory:
|
||||
return c.RemoveDirectory(path)
|
||||
errF := c.removeFile(path)
|
||||
if errF == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errD := c.RemoveDirectory(path)
|
||||
if errD == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Both failed: figure out which error to return.
|
||||
|
||||
if errF, ok := errF.(*os.PathError); ok {
|
||||
// The only time it makes sense to compare errors, is when both are `*os.PathError`.
|
||||
// We cannot test these directly with errF == errD, as that would be a pointer comparison.
|
||||
|
||||
if errD, ok := errD.(*os.PathError); ok && errors.Is(errF.Err, errD.Err) {
|
||||
// If they are both pointers to PathError,
|
||||
// and the same underlying error, then return that.
|
||||
return errF
|
||||
}
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
return c.RemoveDirectory(path)
|
||||
|
||||
fi, err := c.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
|
||||
if fi.IsDir() {
|
||||
return errD
|
||||
}
|
||||
|
||||
return errF
|
||||
}
|
||||
|
||||
func (c *Client) removeFile(path string) error {
|
||||
|
|
@ -785,7 +806,15 @@ func (c *Client) removeFile(path string) error {
|
|||
}
|
||||
switch typ {
|
||||
case sshFxpStatus:
|
||||
return normaliseError(unmarshalStatus(id, data))
|
||||
err = normaliseError(unmarshalStatus(id, data))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: path,
|
||||
Err: err,
|
||||
}
|
||||
default:
|
||||
return unimplementedPacketErr(typ)
|
||||
}
|
||||
|
|
@ -803,7 +832,15 @@ func (c *Client) RemoveDirectory(path string) error {
|
|||
}
|
||||
switch typ {
|
||||
case sshFxpStatus:
|
||||
return normaliseError(unmarshalStatus(id, data))
|
||||
err = normaliseError(unmarshalStatus(id, data))
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{
|
||||
Op: "remove",
|
||||
Path: path,
|
||||
Err: err,
|
||||
}
|
||||
default:
|
||||
return unimplementedPacketErr(typ)
|
||||
}
|
||||
|
|
@ -1805,7 +1842,8 @@ func (f *File) readFromWithConcurrency(r io.Reader, concurrency int) (read int64
|
|||
off := f.offset
|
||||
|
||||
for {
|
||||
n, err := r.Read(b)
|
||||
// Fill the entire buffer.
|
||||
n, err := io.ReadFull(r, b)
|
||||
|
||||
if n > 0 {
|
||||
read += int64(n)
|
||||
|
|
@ -1831,7 +1869,7 @@ func (f *File) readFromWithConcurrency(r io.Reader, concurrency int) (read int64
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
errCh <- rwErr{off, err}
|
||||
}
|
||||
return
|
||||
|
|
@ -1985,7 +2023,8 @@ func (f *File) ReadFrom(r io.Reader) (int64, error) {
|
|||
|
||||
var read int64
|
||||
for {
|
||||
n, err := r.Read(b)
|
||||
// Fill the entire buffer.
|
||||
n, err := io.ReadFull(r, b)
|
||||
if n < 0 {
|
||||
panic("sftp.File: reader returned negative count from Read")
|
||||
}
|
||||
|
|
@ -2002,7 +2041,7 @@ func (f *File) ReadFrom(r io.Reader) (int64, error) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return read, nil // return nil explicitly.
|
||||
}
|
||||
|
||||
|
|
@ -2122,6 +2161,13 @@ func (f *File) Sync() error {
|
|||
return os.ErrClosed
|
||||
}
|
||||
|
||||
if data, ok := f.c.HasExtension(openssh.ExtensionFSync().Name); !ok || data != "1" {
|
||||
return &StatusError{
|
||||
Code: sshFxOPUnsupported,
|
||||
msg: "fsync not supported",
|
||||
}
|
||||
}
|
||||
|
||||
id := f.c.nextID()
|
||||
typ, data, err := f.c.sendPacket(context.Background(), nil, &sshFxpFsyncPacket{
|
||||
ID: id,
|
||||
|
|
|
|||
73
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/fsync.go
generated
vendored
Normal file
73
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/fsync.go
generated
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionFSync = "fsync@openssh.com"
|
||||
|
||||
// RegisterExtensionFSync registers the "fsync@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionFSync() {
|
||||
sshfx.RegisterExtendedPacketType(extensionFSync, func() sshfx.ExtendedData {
|
||||
return new(FSyncExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionFSync returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionFSync() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionFSync,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// FSyncExtendedPacket defines the fsync@openssh.com extend packet.
|
||||
type FSyncExtendedPacket struct {
|
||||
Handle string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *FSyncExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *FSyncExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionFSync,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the fsync@openssh.com extended packet-specific data.
|
||||
func (ep *FSyncExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.Handle)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the fsync@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *FSyncExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(handle)
|
||||
size := 4 + len(ep.Handle)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the fsync@openssh.com extended packet-specific data from buf.
|
||||
func (ep *FSyncExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = FSyncExtendedPacket{
|
||||
Handle: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the fsync@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FSyncExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/hardlink.go
generated
vendored
Normal file
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/hardlink.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionHardlink = "hardlink@openssh.com"
|
||||
|
||||
// RegisterExtensionHardlink registers the "hardlink@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionHardlink() {
|
||||
sshfx.RegisterExtendedPacketType(extensionHardlink, func() sshfx.ExtendedData {
|
||||
return new(HardlinkExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionHardlink returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionHardlink() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionHardlink,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// HardlinkExtendedPacket defines the hardlink@openssh.com extend packet.
|
||||
type HardlinkExtendedPacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *HardlinkExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *HardlinkExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionHardlink,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
func (ep *HardlinkExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.OldPath)
|
||||
buf.AppendString(ep.NewPath)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *HardlinkExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
|
||||
func (ep *HardlinkExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = HardlinkExtendedPacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
|
||||
func (ep *HardlinkExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
2
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/openssh.go
generated
vendored
Normal file
2
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/openssh.go
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Package openssh implements the openssh secsh-filexfer extensions as described in https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
package openssh
|
||||
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/posix-rename.go
generated
vendored
Normal file
76
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/posix-rename.go
generated
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionPOSIXRename = "posix-rename@openssh.com"
|
||||
|
||||
// RegisterExtensionPOSIXRename registers the "posix-rename@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionPOSIXRename() {
|
||||
sshfx.RegisterExtendedPacketType(extensionPOSIXRename, func() sshfx.ExtendedData {
|
||||
return new(POSIXRenameExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionPOSIXRename returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionPOSIXRename() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionPOSIXRename,
|
||||
Data: "1",
|
||||
}
|
||||
}
|
||||
|
||||
// POSIXRenameExtendedPacket defines the posix-rename@openssh.com extend packet.
|
||||
type POSIXRenameExtendedPacket struct {
|
||||
OldPath string
|
||||
NewPath string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *POSIXRenameExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionPOSIXRename,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.OldPath)
|
||||
buf.AppendString(ep.NewPath)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the hardlink@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *POSIXRenameExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
// string(oldpath) + string(newpath)
|
||||
size := 4 + len(ep.OldPath) + 4 + len(ep.NewPath)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the hardlink@openssh.com extended packet-specific data from buf.
|
||||
func (ep *POSIXRenameExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = POSIXRenameExtendedPacket{
|
||||
OldPath: buf.ConsumeString(),
|
||||
NewPath: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the hardlink@openssh.com extended packet-specific data into ep.
|
||||
func (ep *POSIXRenameExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
236
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/statvfs.go
generated
vendored
Normal file
236
vendor/github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh/statvfs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
package openssh
|
||||
|
||||
import (
|
||||
sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
|
||||
)
|
||||
|
||||
const extensionStatVFS = "statvfs@openssh.com"
|
||||
|
||||
// RegisterExtensionStatVFS registers the "statvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionStatVFS() {
|
||||
sshfx.RegisterExtendedPacketType(extensionStatVFS, func() sshfx.ExtendedData {
|
||||
return new(StatVFSExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionStatVFS() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionStatVFS,
|
||||
Data: "2",
|
||||
}
|
||||
}
|
||||
|
||||
// StatVFSExtendedPacket defines the statvfs@openssh.com extend packet.
|
||||
type StatVFSExtendedPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *StatVFSExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *StatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionStatVFS,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
func (ep *StatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.Path)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *StatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 4 + len(ep.Path) // string(path)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
ep.MarshalInto(buf)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = StatVFSExtendedPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
|
||||
const extensionFStatVFS = "fstatvfs@openssh.com"
|
||||
|
||||
// RegisterExtensionFStatVFS registers the "fstatvfs@openssh.com" extended packet with the encoding/ssh/filexfer package.
|
||||
func RegisterExtensionFStatVFS() {
|
||||
sshfx.RegisterExtendedPacketType(extensionFStatVFS, func() sshfx.ExtendedData {
|
||||
return new(FStatVFSExtendedPacket)
|
||||
})
|
||||
}
|
||||
|
||||
// ExtensionFStatVFS returns an ExtensionPair suitable to append into an sshfx.InitPacket or sshfx.VersionPacket.
|
||||
func ExtensionFStatVFS() *sshfx.ExtensionPair {
|
||||
return &sshfx.ExtensionPair{
|
||||
Name: extensionFStatVFS,
|
||||
Data: "2",
|
||||
}
|
||||
}
|
||||
|
||||
// FStatVFSExtendedPacket defines the fstatvfs@openssh.com extend packet.
|
||||
type FStatVFSExtendedPacket struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED packet type.
|
||||
func (ep *FStatVFSExtendedPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtended
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended packet.
|
||||
func (ep *FStatVFSExtendedPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedPacket{
|
||||
ExtendedRequest: extensionFStatVFS,
|
||||
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
func (ep *FStatVFSExtendedPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendString(ep.Path)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the statvfs@openssh.com extended packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended packet.
|
||||
func (ep *FStatVFSExtendedPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 4 + len(ep.Path) // string(path)
|
||||
|
||||
buf := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
ep.MarshalInto(buf)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FStatVFSExtendedPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = FStatVFSExtendedPacket{
|
||||
Path: buf.ConsumeString(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the statvfs@openssh.com extended packet-specific data into ep.
|
||||
func (ep *FStatVFSExtendedPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
|
||||
// The values for the MountFlags field.
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL
|
||||
const (
|
||||
MountFlagsReadOnly = 0x1 // SSH_FXE_STATVFS_ST_RDONLY
|
||||
MountFlagsNoSUID = 0x2 // SSH_FXE_STATVFS_ST_NOSUID
|
||||
)
|
||||
|
||||
// StatVFSExtendedReplyPacket defines the extended reply packet for statvfs@openssh.com and fstatvfs@openssh.com requests.
|
||||
type StatVFSExtendedReplyPacket struct {
|
||||
BlockSize uint64 /* f_bsize: file system block size */
|
||||
FragmentSize uint64 /* f_frsize: fundamental fs block size / fagment size */
|
||||
Blocks uint64 /* f_blocks: number of blocks (unit f_frsize) */
|
||||
BlocksFree uint64 /* f_bfree: free blocks in filesystem */
|
||||
BlocksAvail uint64 /* f_bavail: free blocks for non-root */
|
||||
Files uint64 /* f_files: total file inodes */
|
||||
FilesFree uint64 /* f_ffree: free file inodes */
|
||||
FilesAvail uint64 /* f_favail: free file inodes for to non-root */
|
||||
FilesystemID uint64 /* f_fsid: file system id */
|
||||
MountFlags uint64 /* f_flag: bit mask of mount flag values */
|
||||
MaxNameLength uint64 /* f_namemax: maximum filename length */
|
||||
}
|
||||
|
||||
// Type returns the SSH_FXP_EXTENDED_REPLY packet type.
|
||||
func (ep *StatVFSExtendedReplyPacket) Type() sshfx.PacketType {
|
||||
return sshfx.PacketTypeExtendedReply
|
||||
}
|
||||
|
||||
// MarshalPacket returns ep as a two-part binary encoding of the full extended reply packet.
|
||||
func (ep *StatVFSExtendedReplyPacket) MarshalPacket(reqid uint32, b []byte) (header, payload []byte, err error) {
|
||||
p := &sshfx.ExtendedReplyPacket{
|
||||
Data: ep,
|
||||
}
|
||||
return p.MarshalPacket(reqid, b)
|
||||
}
|
||||
|
||||
// UnmarshalPacketBody returns ep as a two-part binary encoding of the full extended reply packet.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalPacketBody(buf *sshfx.Buffer) (err error) {
|
||||
p := &sshfx.ExtendedReplyPacket{
|
||||
Data: ep,
|
||||
}
|
||||
return p.UnmarshalPacketBody(buf)
|
||||
}
|
||||
|
||||
// MarshalInto encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
|
||||
func (ep *StatVFSExtendedReplyPacket) MarshalInto(buf *sshfx.Buffer) {
|
||||
buf.AppendUint64(ep.BlockSize)
|
||||
buf.AppendUint64(ep.FragmentSize)
|
||||
buf.AppendUint64(ep.Blocks)
|
||||
buf.AppendUint64(ep.BlocksFree)
|
||||
buf.AppendUint64(ep.BlocksAvail)
|
||||
buf.AppendUint64(ep.Files)
|
||||
buf.AppendUint64(ep.FilesFree)
|
||||
buf.AppendUint64(ep.FilesAvail)
|
||||
buf.AppendUint64(ep.FilesystemID)
|
||||
buf.AppendUint64(ep.MountFlags)
|
||||
buf.AppendUint64(ep.MaxNameLength)
|
||||
}
|
||||
|
||||
// MarshalBinary encodes ep into the binary encoding of the (f)statvfs@openssh.com extended reply packet-specific data.
|
||||
//
|
||||
// NOTE: This _only_ encodes the packet-specific data, it does not encode the full extended reply packet.
|
||||
func (ep *StatVFSExtendedReplyPacket) MarshalBinary() ([]byte, error) {
|
||||
size := 11 * 8 // 11 × uint64(various)
|
||||
|
||||
b := sshfx.NewBuffer(make([]byte, 0, size))
|
||||
ep.MarshalInto(b)
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalFrom decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalFrom(buf *sshfx.Buffer) (err error) {
|
||||
*ep = StatVFSExtendedReplyPacket{
|
||||
BlockSize: buf.ConsumeUint64(),
|
||||
FragmentSize: buf.ConsumeUint64(),
|
||||
Blocks: buf.ConsumeUint64(),
|
||||
BlocksFree: buf.ConsumeUint64(),
|
||||
BlocksAvail: buf.ConsumeUint64(),
|
||||
Files: buf.ConsumeUint64(),
|
||||
FilesFree: buf.ConsumeUint64(),
|
||||
FilesAvail: buf.ConsumeUint64(),
|
||||
FilesystemID: buf.ConsumeUint64(),
|
||||
MountFlags: buf.ConsumeUint64(),
|
||||
MaxNameLength: buf.ConsumeUint64(),
|
||||
}
|
||||
|
||||
return buf.Err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the fstatvfs@openssh.com extended reply packet-specific data into ep.
|
||||
func (ep *StatVFSExtendedReplyPacket) UnmarshalBinary(data []byte) (err error) {
|
||||
return ep.UnmarshalFrom(sshfx.NewBuffer(data))
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ github.com/containers/buildah/pkg/sshagent
|
|||
github.com/containers/buildah/pkg/util
|
||||
github.com/containers/buildah/pkg/volumes
|
||||
github.com/containers/buildah/util
|
||||
# github.com/containers/common v0.62.3-0.20250324121725-e360699fb3e3
|
||||
# github.com/containers/common v0.62.3-0.20250326101258-442957aa798d
|
||||
## explicit; go 1.23.0
|
||||
github.com/containers/common/internal
|
||||
github.com/containers/common/internal/attributedstring
|
||||
|
|
@ -363,8 +363,8 @@ github.com/containers/psgo/internal/dev
|
|||
github.com/containers/psgo/internal/host
|
||||
github.com/containers/psgo/internal/proc
|
||||
github.com/containers/psgo/internal/process
|
||||
# github.com/containers/storage v1.57.3-0.20250310120440-ab85543c3c6a
|
||||
## explicit; go 1.22.0
|
||||
# github.com/containers/storage v1.57.3-0.20250325222852-4d1ae4a7983d
|
||||
## explicit; go 1.23.0
|
||||
github.com/containers/storage
|
||||
github.com/containers/storage/drivers
|
||||
github.com/containers/storage/drivers/aufs
|
||||
|
|
@ -958,10 +958,11 @@ github.com/pelletier/go-toml/v2/unstable
|
|||
# github.com/pkg/errors v0.9.1
|
||||
## explicit
|
||||
github.com/pkg/errors
|
||||
# github.com/pkg/sftp v1.13.8
|
||||
# github.com/pkg/sftp v1.13.9
|
||||
## explicit; go 1.15
|
||||
github.com/pkg/sftp
|
||||
github.com/pkg/sftp/internal/encoding/ssh/filexfer
|
||||
github.com/pkg/sftp/internal/encoding/ssh/filexfer/openssh
|
||||
# github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
|
||||
## explicit; go 1.20
|
||||
github.com/planetscale/vtprotobuf/protohelpers
|
||||
|
|
|
|||
Loading…
Reference in New Issue