mirror of
https://github.com/containers/skopeo.git
synced 2025-09-02 07:06:04 +00:00
Merge pull request #2112 from containers/renovate/github.com-containers-common-0.x
fix(deps): update module github.com/containers/common to v0.56.0
This commit is contained in:
5
go.mod
5
go.mod
@@ -3,7 +3,7 @@ module github.com/containers/skopeo
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/containers/common v0.55.4
|
github.com/containers/common v0.56.0
|
||||||
github.com/containers/image/v5 v5.28.0
|
github.com/containers/image/v5 v5.28.0
|
||||||
github.com/containers/ocicrypt v1.1.8
|
github.com/containers/ocicrypt v1.1.8
|
||||||
github.com/containers/storage v1.50.2
|
github.com/containers/storage v1.50.2
|
||||||
@@ -30,7 +30,7 @@ require (
|
|||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/containerd/cgroups/v3 v3.0.2 // indirect
|
github.com/containerd/cgroups/v3 v3.0.2 // indirect
|
||||||
github.com/containerd/containerd v1.7.2 // indirect
|
github.com/containerd/containerd v1.7.6 // indirect
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||||
github.com/coreos/go-oidc/v3 v3.6.0 // indirect
|
github.com/coreos/go-oidc/v3 v3.6.0 // indirect
|
||||||
@@ -42,6 +42,7 @@ require (
|
|||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
17
go.sum
17
go.sum
@@ -26,12 +26,12 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
|
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
|
||||||
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
|
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
|
||||||
github.com/containerd/containerd v1.7.2 h1:UF2gdONnxO8I6byZXDi5sXWiWvlW3D/sci7dTQimEJo=
|
github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8=
|
||||||
github.com/containerd/containerd v1.7.2/go.mod h1:afcz74+K10M/+cjGHIVQrCt3RAQhUSCAjJ9iMYhhkuI=
|
github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4=
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
||||||
github.com/containers/common v0.55.4 h1:7IxB/G5qtDU+rp1YiVWkDpd+ZC4ZlCQ7k2jZJYkB/R8=
|
github.com/containers/common v0.56.0 h1:hysHUsEai1EkMXanU26UV55wMXns/a6AYmaFqJ4fEMY=
|
||||||
github.com/containers/common v0.55.4/go.mod h1:5mVCpfMBWyO+zaD7Fw+DBHFa42YFKROwle1qpEKcX3U=
|
github.com/containers/common v0.56.0/go.mod h1:IjaDdfUtcs2CfCcJMZxuut4XlvkTkY9Nlqkso9xCOq4=
|
||||||
github.com/containers/image/v5 v5.28.0 h1:H4cWbdI88UA/mDb6SxMo3IxpmS1BSs/Kifvhwt9g048=
|
github.com/containers/image/v5 v5.28.0 h1:H4cWbdI88UA/mDb6SxMo3IxpmS1BSs/Kifvhwt9g048=
|
||||||
github.com/containers/image/v5 v5.28.0/go.mod h1:9aPnNkwHNHgGl9VlQxXEshvmOJRbdRAc1rNDD6sP2eU=
|
github.com/containers/image/v5 v5.28.0/go.mod h1:9aPnNkwHNHgGl9VlQxXEshvmOJRbdRAc1rNDD6sP2eU=
|
||||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
|
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
|
||||||
@@ -71,6 +71,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||||||
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
|
github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
|
||||||
github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVBPlx3viJT9Md8if8IxxJnO+x0JCGb054heg=
|
github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVBPlx3viJT9Md8if8IxxJnO+x0JCGb054heg=
|
||||||
github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8=
|
github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8=
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
@@ -266,8 +268,8 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
|
||||||
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
|
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
|
||||||
@@ -304,7 +306,7 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
|||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||||
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
||||||
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
@@ -469,6 +471,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
172
vendor/github.com/containerd/containerd/log/context.go
generated
vendored
172
vendor/github.com/containerd/containerd/log/context.go
generated
vendored
@@ -14,59 +14,169 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Package log provides types and functions related to logging, passing
|
||||||
|
// loggers through a context, and attaching context to the logger.
|
||||||
|
//
|
||||||
|
// # Transitional types
|
||||||
|
//
|
||||||
|
// This package contains various types that are aliases for types in [logrus].
|
||||||
|
// These aliases are intended for transitioning away from hard-coding logrus
|
||||||
|
// as logging implementation. Consumers of this package are encouraged to use
|
||||||
|
// the type-aliases from this package instead of directly using their logrus
|
||||||
|
// equivalent.
|
||||||
|
//
|
||||||
|
// The intent is to replace these aliases with locally defined types and
|
||||||
|
// interfaces once all consumers are no longer directly importing logrus
|
||||||
|
// types.
|
||||||
|
//
|
||||||
|
// IMPORTANT: due to the transitional purpose of this package, it is not
|
||||||
|
// guaranteed for the full logrus API to be provided in the future. As
|
||||||
|
// outlined, these aliases are provided as a step to transition away from
|
||||||
|
// a specific implementation which, as a result, exposes the full logrus API.
|
||||||
|
// While no decisions have been made on the ultimate design and interface
|
||||||
|
// provided by this package, we do not expect carrying "less common" features.
|
||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// G is a shorthand for [GetLogger].
|
||||||
// G is an alias for GetLogger.
|
//
|
||||||
//
|
// We may want to define this locally to a package to get package tagged log
|
||||||
// We may want to define this locally to a package to get package tagged log
|
// messages.
|
||||||
// messages.
|
var G = GetLogger
|
||||||
G = GetLogger
|
|
||||||
|
|
||||||
// L is an alias for the standard logger.
|
// L is an alias for the standard logger.
|
||||||
L = logrus.NewEntry(logrus.StandardLogger())
|
var L = &Entry{
|
||||||
)
|
Logger: logrus.StandardLogger(),
|
||||||
|
// Default is three fields plus a little extra room.
|
||||||
|
Data: make(Fields, 6),
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type loggerKey struct{}
|
||||||
loggerKey struct{}
|
|
||||||
|
|
||||||
// Fields type to pass to `WithFields`, alias from `logrus`.
|
// Fields type to pass to "WithFields".
|
||||||
Fields = logrus.Fields
|
type Fields = map[string]any
|
||||||
)
|
|
||||||
|
|
||||||
|
// Entry is a logging entry. It contains all the fields passed with
|
||||||
|
// [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn,
|
||||||
|
// Error, Fatal or Panic is called on it. These objects can be reused and
|
||||||
|
// passed around as much as you wish to avoid field duplication.
|
||||||
|
//
|
||||||
|
// Entry is a transitional type, and currently an alias for [logrus.Entry].
|
||||||
|
type Entry = logrus.Entry
|
||||||
|
|
||||||
|
// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
|
||||||
|
// zeros to ensure the formatted time is always the same number of
|
||||||
|
// characters.
|
||||||
|
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
|
||||||
|
|
||||||
|
// Level is a logging level.
|
||||||
|
type Level = logrus.Level
|
||||||
|
|
||||||
|
// Supported log levels.
|
||||||
const (
|
const (
|
||||||
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
|
// TraceLevel level. Designates finer-grained informational events
|
||||||
// ensure the formatted time is always the same number of characters.
|
// than [DebugLevel].
|
||||||
RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
|
TraceLevel Level = logrus.TraceLevel
|
||||||
|
|
||||||
// TextFormat represents the text logging format
|
// DebugLevel level. Usually only enabled when debugging. Very verbose
|
||||||
TextFormat = "text"
|
// logging.
|
||||||
|
DebugLevel Level = logrus.DebugLevel
|
||||||
|
|
||||||
// JSONFormat represents the JSON logging format
|
// InfoLevel level. General operational entries about what's going on
|
||||||
JSONFormat = "json"
|
// inside the application.
|
||||||
|
InfoLevel Level = logrus.InfoLevel
|
||||||
|
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel Level = logrus.WarnLevel
|
||||||
|
|
||||||
|
// ErrorLevel level. Logs errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel Level = logrus.ErrorLevel
|
||||||
|
|
||||||
|
// FatalLevel level. Logs and then calls "logger.Exit(1)". It exits
|
||||||
|
// even if the logging level is set to Panic.
|
||||||
|
FatalLevel Level = logrus.FatalLevel
|
||||||
|
|
||||||
|
// PanicLevel level. This is the highest level of severity. Logs and
|
||||||
|
// then calls panic with the message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = logrus.PanicLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SetLevel sets log level globally. It returns an error if the given
|
||||||
|
// level is not supported.
|
||||||
|
//
|
||||||
|
// level can be one of:
|
||||||
|
//
|
||||||
|
// - "trace" ([TraceLevel])
|
||||||
|
// - "debug" ([DebugLevel])
|
||||||
|
// - "info" ([InfoLevel])
|
||||||
|
// - "warn" ([WarnLevel])
|
||||||
|
// - "error" ([ErrorLevel])
|
||||||
|
// - "fatal" ([FatalLevel])
|
||||||
|
// - "panic" ([PanicLevel])
|
||||||
|
func SetLevel(level string) error {
|
||||||
|
lvl, err := logrus.ParseLevel(level)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Logger.SetLevel(lvl)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns the current log level.
|
||||||
|
func GetLevel() Level {
|
||||||
|
return L.Logger.GetLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputFormat specifies a log output format.
|
||||||
|
type OutputFormat string
|
||||||
|
|
||||||
|
// Supported log output formats.
|
||||||
|
const (
|
||||||
|
// TextFormat represents the text logging format.
|
||||||
|
TextFormat OutputFormat = "text"
|
||||||
|
|
||||||
|
// JSONFormat represents the JSON logging format.
|
||||||
|
JSONFormat OutputFormat = "json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetFormat sets the log output format ([TextFormat] or [JSONFormat]).
|
||||||
|
func SetFormat(format OutputFormat) error {
|
||||||
|
switch format {
|
||||||
|
case TextFormat:
|
||||||
|
L.Logger.SetFormatter(&logrus.TextFormatter{
|
||||||
|
TimestampFormat: RFC3339NanoFixed,
|
||||||
|
FullTimestamp: true,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
case JSONFormat:
|
||||||
|
L.Logger.SetFormatter(&logrus.JSONFormatter{
|
||||||
|
TimestampFormat: RFC3339NanoFixed,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown log format: %s", format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithLogger returns a new context with the provided logger. Use in
|
// WithLogger returns a new context with the provided logger. Use in
|
||||||
// combination with logger.WithField(s) for great effect.
|
// combination with logger.WithField(s) for great effect.
|
||||||
func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context {
|
func WithLogger(ctx context.Context, logger *Entry) context.Context {
|
||||||
e := logger.WithContext(ctx)
|
return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx))
|
||||||
return context.WithValue(ctx, loggerKey{}, e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLogger retrieves the current logger from the context. If no logger is
|
// GetLogger retrieves the current logger from the context. If no logger is
|
||||||
// available, the default logger is returned.
|
// available, the default logger is returned.
|
||||||
func GetLogger(ctx context.Context) *logrus.Entry {
|
func GetLogger(ctx context.Context) *Entry {
|
||||||
logger := ctx.Value(loggerKey{})
|
if logger := ctx.Value(loggerKey{}); logger != nil {
|
||||||
|
return logger.(*Entry)
|
||||||
if logger == nil {
|
|
||||||
return L.WithContext(ctx)
|
|
||||||
}
|
}
|
||||||
|
return L.WithContext(ctx)
|
||||||
return logger.(*logrus.Entry)
|
|
||||||
}
|
}
|
||||||
|
59
vendor/github.com/containers/common/libnetwork/types/const.go
generated
vendored
Normal file
59
vendor/github.com/containers/common/libnetwork/types/const.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BridgeNetworkDriver defines the bridge driver
|
||||||
|
BridgeNetworkDriver = "bridge"
|
||||||
|
// DefaultNetworkDriver is the default network type used
|
||||||
|
DefaultNetworkDriver = BridgeNetworkDriver
|
||||||
|
// MacVLANNetworkDriver defines the macvlan driver
|
||||||
|
MacVLANNetworkDriver = "macvlan"
|
||||||
|
// MacVLANNetworkDriver defines the macvlan driver
|
||||||
|
IPVLANNetworkDriver = "ipvlan"
|
||||||
|
|
||||||
|
// IPAM drivers
|
||||||
|
Driver = "driver"
|
||||||
|
// HostLocalIPAMDriver store the ip locally in a db
|
||||||
|
HostLocalIPAMDriver = "host-local"
|
||||||
|
// DHCPIPAMDriver get subnet and ip from dhcp server
|
||||||
|
DHCPIPAMDriver = "dhcp"
|
||||||
|
// NoneIPAMDriver do not provide ipam management
|
||||||
|
NoneIPAMDriver = "none"
|
||||||
|
|
||||||
|
// DefaultSubnet is the name that will be used for the default CNI network.
|
||||||
|
DefaultNetworkName = "podman"
|
||||||
|
// DefaultSubnet is the subnet that will be used for the default CNI network.
|
||||||
|
DefaultSubnet = "10.88.0.0/16"
|
||||||
|
|
||||||
|
// valid macvlan driver mode values
|
||||||
|
MacVLANModeBridge = "bridge"
|
||||||
|
MacVLANModePrivate = "private"
|
||||||
|
MacVLANModeVepa = "vepa"
|
||||||
|
MacVLANModePassthru = "passthru"
|
||||||
|
|
||||||
|
// valid ipvlan driver modes
|
||||||
|
IPVLANModeL2 = "l2"
|
||||||
|
IPVLANModeL3 = "l3"
|
||||||
|
IPVLANModeL3s = "l3s"
|
||||||
|
|
||||||
|
// valid network options
|
||||||
|
VLANOption = "vlan"
|
||||||
|
MTUOption = "mtu"
|
||||||
|
ModeOption = "mode"
|
||||||
|
IsolateOption = "isolate"
|
||||||
|
MetricOption = "metric"
|
||||||
|
NoDefaultRoute = "no_default_route"
|
||||||
|
BclimOption = "bclim"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetworkBackend string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CNI NetworkBackend = "cni"
|
||||||
|
Netavark NetworkBackend = "netavark"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidMacVLANModes is the list of valid mode options for the macvlan driver
|
||||||
|
var ValidMacVLANModes = []string{MacVLANModeBridge, MacVLANModePrivate, MacVLANModeVepa, MacVLANModePassthru}
|
||||||
|
|
||||||
|
// ValidIPVLANModes is the list of valid mode options for the ipvlan driver
|
||||||
|
var ValidIPVLANModes = []string{IPVLANModeL2, IPVLANModeL3, IPVLANModeL3s}
|
30
vendor/github.com/containers/common/libnetwork/types/define.go
generated
vendored
Normal file
30
vendor/github.com/containers/common/libnetwork/types/define.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoSuchNetwork indicates the requested network does not exist
|
||||||
|
ErrNoSuchNetwork = errors.New("network not found")
|
||||||
|
|
||||||
|
// ErrInvalidArg indicates that an invalid argument was passed
|
||||||
|
ErrInvalidArg = errors.New("invalid argument")
|
||||||
|
|
||||||
|
// ErrNetworkExists indicates that a network with the given name already
|
||||||
|
// exists.
|
||||||
|
ErrNetworkExists = errors.New("network already exists")
|
||||||
|
|
||||||
|
// NameRegex is a regular expression to validate names.
|
||||||
|
// This must NOT be changed.
|
||||||
|
NameRegex = regexp.Delayed("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$")
|
||||||
|
// RegexError is thrown in presence of an invalid name.
|
||||||
|
RegexError = fmt.Errorf("names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: %w", ErrInvalidArg) // nolint:revive // This lint is new and we do not want to break the API.
|
||||||
|
|
||||||
|
// NotHexRegex is a regular expression to check if a string is
|
||||||
|
// a hexadecimal string.
|
||||||
|
NotHexRegex = regexp.Delayed(`[^0-9a-fA-F]`)
|
||||||
|
)
|
339
vendor/github.com/containers/common/libnetwork/types/network.go
generated
vendored
Normal file
339
vendor/github.com/containers/common/libnetwork/types/network.go
generated
vendored
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerNetwork interface {
|
||||||
|
// NetworkCreate will take a partial filled Network and fill the
|
||||||
|
// missing fields. It creates the Network and returns the full Network.
|
||||||
|
NetworkCreate(Network, *NetworkCreateOptions) (Network, error)
|
||||||
|
// NetworkUpdate will take network name and ID and updates network DNS Servers.
|
||||||
|
NetworkUpdate(nameOrID string, options NetworkUpdateOptions) error
|
||||||
|
// NetworkRemove will remove the Network with the given name or ID.
|
||||||
|
NetworkRemove(nameOrID string) error
|
||||||
|
// NetworkList will return all known Networks. Optionally you can
|
||||||
|
// supply a list of filter functions. Only if a network matches all
|
||||||
|
// functions it is returned.
|
||||||
|
NetworkList(...FilterFunc) ([]Network, error)
|
||||||
|
// NetworkInspect will return the Network with the given name or ID.
|
||||||
|
NetworkInspect(nameOrID string) (Network, error)
|
||||||
|
|
||||||
|
// Setup will setup the container network namespace. It returns
|
||||||
|
// a map of StatusBlocks, the key is the network name.
|
||||||
|
Setup(namespacePath string, options SetupOptions) (map[string]StatusBlock, error)
|
||||||
|
// Teardown will teardown the container network namespace.
|
||||||
|
Teardown(namespacePath string, options TeardownOptions) error
|
||||||
|
|
||||||
|
// Drivers will return the list of supported network drivers
|
||||||
|
// for this interface.
|
||||||
|
Drivers() []string
|
||||||
|
|
||||||
|
// DefaultNetworkName will return the default network name
|
||||||
|
// for this interface.
|
||||||
|
DefaultNetworkName() string
|
||||||
|
|
||||||
|
// NetworkInfo return the network information about backend type,
|
||||||
|
// binary path, package version and so on.
|
||||||
|
NetworkInfo() NetworkInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network describes the Network attributes.
|
||||||
|
type Network struct {
|
||||||
|
// Name of the Network.
|
||||||
|
Name string `json:"name"`
|
||||||
|
// ID of the Network.
|
||||||
|
ID string `json:"id"`
|
||||||
|
// Driver for this Network, e.g. bridge, macvlan...
|
||||||
|
Driver string `json:"driver"`
|
||||||
|
// NetworkInterface is the network interface name on the host.
|
||||||
|
NetworkInterface string `json:"network_interface,omitempty"`
|
||||||
|
// Created contains the timestamp when this network was created.
|
||||||
|
Created time.Time `json:"created,omitempty"`
|
||||||
|
// Subnets to use for this network.
|
||||||
|
Subnets []Subnet `json:"subnets,omitempty"`
|
||||||
|
// Routes to use for this network.
|
||||||
|
Routes []Route `json:"routes,omitempty"`
|
||||||
|
// IPv6Enabled if set to true an ipv6 subnet should be created for this net.
|
||||||
|
IPv6Enabled bool `json:"ipv6_enabled"`
|
||||||
|
// Internal is whether the Network should not have external routes
|
||||||
|
// to public or other Networks.
|
||||||
|
Internal bool `json:"internal"`
|
||||||
|
// DNSEnabled is whether name resolution is active for container on
|
||||||
|
// this Network. Only supported with the bridge driver.
|
||||||
|
DNSEnabled bool `json:"dns_enabled"`
|
||||||
|
// List of custom DNS server for podman's DNS resolver at network level,
|
||||||
|
// all the containers attached to this network will consider resolvers
|
||||||
|
// configured at network level.
|
||||||
|
NetworkDNSServers []string `json:"network_dns_servers,omitempty"`
|
||||||
|
// Labels is a set of key-value labels that have been applied to the
|
||||||
|
// Network.
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
// Options is a set of key-value options that have been applied to
|
||||||
|
// the Network.
|
||||||
|
Options map[string]string `json:"options,omitempty"`
|
||||||
|
// IPAMOptions contains options used for the ip assignment.
|
||||||
|
IPAMOptions map[string]string `json:"ipam_options,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkOptions for a given container.
|
||||||
|
type NetworkUpdateOptions struct {
|
||||||
|
// List of custom DNS server for podman's DNS resolver.
|
||||||
|
// Priority order will be kept as defined by user in the configuration.
|
||||||
|
AddDNSServers []string `json:"add_dns_servers,omitempty"`
|
||||||
|
RemoveDNSServers []string `json:"remove_dns_servers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkInfo contains the network information.
|
||||||
|
type NetworkInfo struct {
|
||||||
|
Backend NetworkBackend `json:"backend"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Package string `json:"package,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
DNS DNSNetworkInfo `json:"dns,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkInfo contains the DNS information.
|
||||||
|
type DNSNetworkInfo struct {
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Package string `json:"package,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPNet is used as custom net.IPNet type to add Marshal/Unmarshal methods.
|
||||||
|
type IPNet struct {
|
||||||
|
net.IPNet
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCIDR parse a string to IPNet
|
||||||
|
func ParseCIDR(cidr string) (IPNet, error) {
|
||||||
|
ip, subnet, err := net.ParseCIDR(cidr)
|
||||||
|
if err != nil {
|
||||||
|
return IPNet{}, err
|
||||||
|
}
|
||||||
|
// convert to 4 bytes if ipv4
|
||||||
|
ipv4 := ip.To4()
|
||||||
|
if ipv4 != nil {
|
||||||
|
ip = ipv4
|
||||||
|
}
|
||||||
|
subnet.IP = ip
|
||||||
|
return IPNet{*subnet}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *IPNet) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(n.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *IPNet) UnmarshalText(text []byte) error {
|
||||||
|
subnet, err := ParseCIDR(string(text))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*n = subnet
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HardwareAddr is the same as net.HardwareAddr except
|
||||||
|
// that it adds the json marshal/unmarshal methods.
|
||||||
|
// This allows us to read the mac from a json string
|
||||||
|
// and a byte array.
|
||||||
|
// swagger:model MacAddress
|
||||||
|
type HardwareAddr net.HardwareAddr
|
||||||
|
|
||||||
|
func (h *HardwareAddr) String() string {
|
||||||
|
return (*net.HardwareAddr)(h).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HardwareAddr) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(h.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HardwareAddr) UnmarshalJSON(text []byte) error {
|
||||||
|
if len(text) == 0 {
|
||||||
|
*h = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the json string start with a quote we got a string
|
||||||
|
// unmarshal the string and parse the mac from this string
|
||||||
|
if string(text[0]) == `"` {
|
||||||
|
var macString string
|
||||||
|
err := json.Unmarshal(text, &macString)
|
||||||
|
if err == nil {
|
||||||
|
mac, err := net.ParseMAC(macString)
|
||||||
|
if err == nil {
|
||||||
|
*h = HardwareAddr(mac)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// not a string or got an error fallback to the normal parsing
|
||||||
|
mac := make(net.HardwareAddr, 0, 6)
|
||||||
|
// use the standard json unmarshal for backwards compat
|
||||||
|
err := json.Unmarshal(text, &mac)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*h = HardwareAddr(mac)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Subnet struct {
|
||||||
|
// Subnet for this Network in CIDR form.
|
||||||
|
// swagger:strfmt string
|
||||||
|
Subnet IPNet `json:"subnet"`
|
||||||
|
// Gateway IP for this Network.
|
||||||
|
// swagger:strfmt string
|
||||||
|
Gateway net.IP `json:"gateway,omitempty"`
|
||||||
|
// LeaseRange contains the range where IP are leased. Optional.
|
||||||
|
LeaseRange *LeaseRange `json:"lease_range,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
// Destination for this route in CIDR form.
|
||||||
|
// swagger:strfmt string
|
||||||
|
Destination IPNet `json:"destination"`
|
||||||
|
// Gateway IP for this route.
|
||||||
|
// swagger:strfmt string
|
||||||
|
Gateway net.IP `json:"gateway"`
|
||||||
|
// Metric for this route. Optional.
|
||||||
|
Metric *uint32 `json:"metric,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaseRange contains the range where IP are leased.
|
||||||
|
type LeaseRange struct {
|
||||||
|
// StartIP first IP in the subnet which should be used to assign ips.
|
||||||
|
// swagger:strfmt string
|
||||||
|
StartIP net.IP `json:"start_ip,omitempty"`
|
||||||
|
// EndIP last IP in the subnet which should be used to assign ips.
|
||||||
|
// swagger:strfmt string
|
||||||
|
EndIP net.IP `json:"end_ip,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusBlock contains the network information about a container
|
||||||
|
// connected to one Network.
|
||||||
|
type StatusBlock struct {
|
||||||
|
// Interfaces contains the created network interface in the container.
|
||||||
|
// The map key is the interface name.
|
||||||
|
Interfaces map[string]NetInterface `json:"interfaces,omitempty"`
|
||||||
|
// DNSServerIPs nameserver addresses which should be added to
|
||||||
|
// the containers resolv.conf file.
|
||||||
|
DNSServerIPs []net.IP `json:"dns_server_ips,omitempty"`
|
||||||
|
// DNSSearchDomains search domains which should be added to
|
||||||
|
// the containers resolv.conf file.
|
||||||
|
DNSSearchDomains []string `json:"dns_search_domains,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetInterface contains the settings for a given network interface.
|
||||||
|
type NetInterface struct {
|
||||||
|
// Subnets list of assigned subnets with their gateway.
|
||||||
|
Subnets []NetAddress `json:"subnets,omitempty"`
|
||||||
|
// MacAddress for this Interface.
|
||||||
|
MacAddress HardwareAddr `json:"mac_address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetAddress contains the ip address, subnet and gateway.
|
||||||
|
type NetAddress struct {
|
||||||
|
// IPNet of this NetAddress. Note that this is a subnet but it has to contain the
|
||||||
|
// actual ip of the network interface and not the network address.
|
||||||
|
IPNet IPNet `json:"ipnet"`
|
||||||
|
// Gateway for the network. This can be empty if there is no gateway, e.g. internal network.
|
||||||
|
Gateway net.IP `json:"gateway,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerNetworkOptions are options which should be set on a per network basis.
|
||||||
|
type PerNetworkOptions struct {
|
||||||
|
// StaticIPs for this container. Optional.
|
||||||
|
// swagger:type []string
|
||||||
|
StaticIPs []net.IP `json:"static_ips,omitempty"`
|
||||||
|
// Aliases contains a list of names which the dns server should resolve
|
||||||
|
// to this container. Should only be set when DNSEnabled is true on the Network.
|
||||||
|
// If aliases are set but there is no dns support for this network the
|
||||||
|
// network interface implementation should ignore this and NOT error.
|
||||||
|
// Optional.
|
||||||
|
Aliases []string `json:"aliases,omitempty"`
|
||||||
|
// StaticMac for this container. Optional.
|
||||||
|
// swagger:strfmt string
|
||||||
|
StaticMAC HardwareAddr `json:"static_mac,omitempty"`
|
||||||
|
// InterfaceName for this container. Required in the backend.
|
||||||
|
// Optional in the frontend. Will be filled with ethX (where X is a integer) when empty.
|
||||||
|
InterfaceName string `json:"interface_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkOptions for a given container.
|
||||||
|
type NetworkOptions struct {
|
||||||
|
// ContainerID is the container id, used for iptables comments and ipam allocation.
|
||||||
|
ContainerID string `json:"container_id"`
|
||||||
|
// ContainerName is the container name, used as dns name.
|
||||||
|
ContainerName string `json:"container_name"`
|
||||||
|
// PortMappings contains the port mappings for this container
|
||||||
|
PortMappings []PortMapping `json:"port_mappings,omitempty"`
|
||||||
|
// Networks contains all networks with the PerNetworkOptions.
|
||||||
|
// The map should contain at least one element.
|
||||||
|
Networks map[string]PerNetworkOptions `json:"networks"`
|
||||||
|
// List of custom DNS server for podman's DNS resolver.
|
||||||
|
// Priority order will be kept as defined by user in the configuration.
|
||||||
|
DNSServers []string `json:"dns_servers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PortMapping is one or more ports that will be mapped into the container.
|
||||||
|
type PortMapping struct {
|
||||||
|
// HostIP is the IP that we will bind to on the host.
|
||||||
|
// If unset, assumed to be 0.0.0.0 (all interfaces).
|
||||||
|
HostIP string `json:"host_ip"`
|
||||||
|
// ContainerPort is the port number that will be exposed from the
|
||||||
|
// container.
|
||||||
|
// Mandatory.
|
||||||
|
ContainerPort uint16 `json:"container_port"`
|
||||||
|
// HostPort is the port number that will be forwarded from the host into
|
||||||
|
// the container.
|
||||||
|
// If omitted, a random port on the host (guaranteed to be over 1024)
|
||||||
|
// will be assigned.
|
||||||
|
HostPort uint16 `json:"host_port"`
|
||||||
|
// Range is the number of ports that will be forwarded, starting at
|
||||||
|
// HostPort and ContainerPort and counting up.
|
||||||
|
// This is 1-indexed, so 1 is assumed to be a single port (only the
|
||||||
|
// Hostport:Containerport mapping will be added), 2 is two ports (both
|
||||||
|
// Hostport:Containerport and Hostport+1:Containerport+1), etc.
|
||||||
|
// If unset, assumed to be 1 (a single port).
|
||||||
|
// Both hostport + range and containerport + range must be less than
|
||||||
|
// 65536.
|
||||||
|
Range uint16 `json:"range"`
|
||||||
|
// Protocol is the protocol forward.
|
||||||
|
// Must be either "tcp", "udp", and "sctp", or some combination of these
|
||||||
|
// separated by commas.
|
||||||
|
// If unset, assumed to be TCP.
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCICNIPortMapping maps to the standard CNI portmapping Capability.
|
||||||
|
// Deprecated: Do not use this struct for new fields. This only exists
|
||||||
|
// for backwards compatibility.
|
||||||
|
type OCICNIPortMapping struct {
|
||||||
|
// HostPort is the port number on the host.
|
||||||
|
HostPort int32 `json:"hostPort"`
|
||||||
|
// ContainerPort is the port number inside the sandbox.
|
||||||
|
ContainerPort int32 `json:"containerPort"`
|
||||||
|
// Protocol is the protocol of the port mapping.
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
// HostIP is the host ip to use.
|
||||||
|
HostIP string `json:"hostIP"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetupOptions struct {
|
||||||
|
NetworkOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
type TeardownOptions struct {
|
||||||
|
NetworkOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterFunc can be passed to NetworkList to filter the networks.
|
||||||
|
type FilterFunc func(Network) bool
|
||||||
|
|
||||||
|
type NetworkCreateOptions struct {
|
||||||
|
// IgnoreIfExists if true, do not fail if the network already exists
|
||||||
|
IgnoreIfExists bool
|
||||||
|
}
|
8
vendor/github.com/containers/common/pkg/auth/auth.go
generated
vendored
8
vendor/github.com/containers/common/pkg/auth/auth.go
generated
vendored
@@ -10,13 +10,13 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/util"
|
||||||
"github.com/containers/image/v5/docker"
|
"github.com/containers/image/v5/docker"
|
||||||
"github.com/containers/image/v5/docker/reference"
|
"github.com/containers/image/v5/docker/reference"
|
||||||
"github.com/containers/image/v5/pkg/docker/config"
|
"github.com/containers/image/v5/pkg/docker/config"
|
||||||
"github.com/containers/image/v5/pkg/sysregistriesv2"
|
"github.com/containers/image/v5/pkg/sysregistriesv2"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
terminal "golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNewCredentialsInvalid means that the new user-provided credentials are
|
// ErrNewCredentialsInvalid means that the new user-provided credentials are
|
||||||
@@ -259,7 +259,7 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("reading username: %w", err)
|
return "", "", fmt.Errorf("reading username: %w", err)
|
||||||
}
|
}
|
||||||
// If the user just hit enter, use the displayed user from the
|
// If the user just hit enter, use the displayed user from
|
||||||
// the authentication file. This allows to do a lazy
|
// the authentication file. This allows to do a lazy
|
||||||
// `$ buildah login -p $NEW_PASSWORD` without specifying the
|
// `$ buildah login -p $NEW_PASSWORD` without specifying the
|
||||||
// user.
|
// user.
|
||||||
@@ -269,7 +269,7 @@ func getUserAndPass(opts *LoginOptions, password, userFromAuthFile string) (user
|
|||||||
}
|
}
|
||||||
if password == "" {
|
if password == "" {
|
||||||
fmt.Fprint(opts.Stdout, "Password: ")
|
fmt.Fprint(opts.Stdout, "Password: ")
|
||||||
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
pass, err := util.ReadPassword(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("reading password: %w", err)
|
return "", "", fmt.Errorf("reading password: %w", err)
|
||||||
}
|
}
|
||||||
@@ -336,7 +336,7 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
|
|||||||
|
|
||||||
authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, registry)
|
authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, registry)
|
||||||
if authConfig.Username != "" && authConfig.Password != "" && authInvalid == nil {
|
if authConfig.Username != "" && authConfig.Password != "" && authInvalid == nil {
|
||||||
fmt.Printf("Not logged into %s with current tool. Existing credentials were established via docker login. Please use docker logout instead.\n", key)
|
fmt.Printf("Not logged into %s with current tool. Existing credentials were established via docker login. Please use docker logout instead.\n", key) //nolint:forbidigo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("not logged into %s", key)
|
return fmt.Errorf("not logged into %s", key)
|
||||||
|
2
vendor/github.com/containers/common/pkg/flag/flag.go
generated
vendored
2
vendor/github.com/containers/common/pkg/flag/flag.go
generated
vendored
@@ -165,7 +165,7 @@ func (ob *optionalIntValue) String() string {
|
|||||||
if !ob.present {
|
if !ob.present {
|
||||||
return "" // If the value is not present, just return an empty string, any other value wouldn't make sense.
|
return "" // If the value is not present, just return an empty string, any other value wouldn't make sense.
|
||||||
}
|
}
|
||||||
return strconv.Itoa(int(ob.value))
|
return strconv.Itoa(ob.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type returns the int's type.
|
// Type returns the int's type.
|
||||||
|
3
vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go
generated
vendored
3
vendor/github.com/containers/common/pkg/report/camelcase/camelcase.go
generated
vendored
@@ -51,8 +51,7 @@ func Split(src string) (entries []string) {
|
|||||||
}
|
}
|
||||||
entries = []string{}
|
entries = []string{}
|
||||||
var runes [][]rune
|
var runes [][]rune
|
||||||
lastClass := 0
|
var class, lastClass int
|
||||||
class := 0
|
|
||||||
// split into fields based on class of unicode character
|
// split into fields based on class of unicode character
|
||||||
for _, r := range src {
|
for _, r := range src {
|
||||||
switch {
|
switch {
|
||||||
|
1
vendor/github.com/containers/common/pkg/retry/retry.go
generated
vendored
1
vendor/github.com/containers/common/pkg/retry/retry.go
generated
vendored
@@ -74,7 +74,6 @@ func IsErrorRetryable(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
|
|
||||||
case errcode.Error:
|
case errcode.Error:
|
||||||
switch e.Code {
|
switch e.Code {
|
||||||
case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeDenied,
|
case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeDenied,
|
||||||
|
57
vendor/github.com/containers/common/pkg/util/copy.go
generated
vendored
Normal file
57
vendor/github.com/containers/common/pkg/util/copy.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrDetach indicates that an attach session was manually detached by
|
||||||
|
// the user.
|
||||||
|
var ErrDetach = errors.New("detached from container")
|
||||||
|
|
||||||
|
// CopyDetachable is similar to io.Copy but support a detach key sequence to break out.
|
||||||
|
func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) {
|
||||||
|
buf := make([]byte, 32*1024)
|
||||||
|
for {
|
||||||
|
nr, er := src.Read(buf)
|
||||||
|
if nr > 0 {
|
||||||
|
preservBuf := []byte{}
|
||||||
|
for i, key := range keys {
|
||||||
|
preservBuf = append(preservBuf, buf[0:nr]...)
|
||||||
|
if nr != 1 || buf[0] != key {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == len(keys)-1 {
|
||||||
|
return 0, ErrDetach
|
||||||
|
}
|
||||||
|
nr, er = src.Read(buf)
|
||||||
|
}
|
||||||
|
var nw int
|
||||||
|
var ew error
|
||||||
|
if len(preservBuf) > 0 {
|
||||||
|
nw, ew = dst.Write(preservBuf)
|
||||||
|
nr = len(preservBuf)
|
||||||
|
} else {
|
||||||
|
nw, ew = dst.Write(buf[0:nr])
|
||||||
|
}
|
||||||
|
if nw > 0 {
|
||||||
|
written += int64(nw)
|
||||||
|
}
|
||||||
|
if ew != nil {
|
||||||
|
err = ew
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if nr != nw {
|
||||||
|
err = io.ErrShortWrite
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if er != nil {
|
||||||
|
if er != io.EOF {
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return written, err
|
||||||
|
}
|
206
vendor/github.com/containers/common/pkg/util/util.go
generated
vendored
Normal file
206
vendor/github.com/containers/common/pkg/util/util.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/common/libnetwork/types"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnknownPackage = "Unknown"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInterrupt = errors.New("interrupted")
|
||||||
|
|
||||||
|
// Note: This function is copied from containers/podman libpod/util.go
|
||||||
|
// Please see https://github.com/containers/common/pull/1460
|
||||||
|
func queryPackageVersion(cmdArg ...string) string {
|
||||||
|
output := UnknownPackage
|
||||||
|
if 1 < len(cmdArg) {
|
||||||
|
cmd := exec.Command(cmdArg[0], cmdArg[1:]...)
|
||||||
|
if outp, err := cmd.Output(); err == nil {
|
||||||
|
output = string(outp)
|
||||||
|
deb := false
|
||||||
|
if cmdArg[0] == "/usr/bin/dlocate" {
|
||||||
|
// can return multiple matches
|
||||||
|
l := strings.Split(output, "\n")
|
||||||
|
output = l[0]
|
||||||
|
deb = true
|
||||||
|
} else if cmdArg[0] == "/usr/bin/dpkg" {
|
||||||
|
deb = true
|
||||||
|
}
|
||||||
|
if deb {
|
||||||
|
r := strings.Split(output, ": ")
|
||||||
|
queryFormat := `${Package}_${Version}_${Architecture}`
|
||||||
|
cmd = exec.Command("/usr/bin/dpkg-query", "-f", queryFormat, "-W", r[0])
|
||||||
|
if outp, err := cmd.Output(); err == nil {
|
||||||
|
output = string(outp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cmdArg[0] == "/sbin/apk" {
|
||||||
|
prefix := cmdArg[len(cmdArg)-1] + " is owned by "
|
||||||
|
output = strings.Replace(output, prefix, "", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Trim(output, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This function is copied from containers/podman libpod/util.go
|
||||||
|
// Please see https://github.com/containers/common/pull/1460
|
||||||
|
func PackageVersion(program string) string { // program is full path
|
||||||
|
_, err := os.Stat(program)
|
||||||
|
if err != nil {
|
||||||
|
return UnknownPackage
|
||||||
|
}
|
||||||
|
packagers := [][]string{
|
||||||
|
{"/usr/bin/rpm", "-q", "-f"},
|
||||||
|
{"/usr/bin/dlocate", "-F"}, // Debian, Ubuntu (quick)
|
||||||
|
{"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu (slow)
|
||||||
|
{"/usr/bin/pacman", "-Qo"}, // Arch
|
||||||
|
{"/usr/bin/qfile", "-qv"}, // Gentoo (quick)
|
||||||
|
{"/usr/bin/equery", "b"}, // Gentoo (slow)
|
||||||
|
{"/sbin/apk", "info", "-W"}, // Alpine
|
||||||
|
{"/usr/local/sbin/pkg", "which", "-q"}, // FreeBSD
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cmd := range packagers {
|
||||||
|
cmd = append(cmd, program)
|
||||||
|
if out := queryPackageVersion(cmd...); out != UnknownPackage {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UnknownPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This function is copied from containers/podman libpod/util.go
|
||||||
|
// Please see https://github.com/containers/common/pull/1460
|
||||||
|
func ProgramVersion(program string) (string, error) {
|
||||||
|
return programVersion(program, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProgramVersionDnsname(program string) (string, error) {
|
||||||
|
return programVersion(program, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func programVersion(program string, dnsname bool) (string, error) {
|
||||||
|
cmd := exec.Command(program, "--version")
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("`%v --version` failed: %v %v (%v)", program, stderr.String(), stdout.String(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := strings.TrimSuffix(stdout.String(), "\n")
|
||||||
|
// dnsname --version returns the information to stderr
|
||||||
|
if dnsname {
|
||||||
|
output = strings.TrimSuffix(stderr.String(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringInSlice determines if a string is in a string slice, returns bool
|
||||||
|
func StringInSlice(s string, sl []string) bool {
|
||||||
|
for _, i := range sl {
|
||||||
|
if i == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringMatchRegexSlice determines if a given string matches one of the given regexes, returns bool
|
||||||
|
func StringMatchRegexSlice(s string, re []string) bool {
|
||||||
|
for _, r := range re {
|
||||||
|
m, err := regexp.MatchString(r, s)
|
||||||
|
if err == nil && m {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterID is a function used to compare an id against a set of ids, if the
|
||||||
|
// input is hex we check if the prefix matches. Otherwise we assume it is a
|
||||||
|
// regex and try to match that.
|
||||||
|
// see https://github.com/containers/podman/issues/18471 for why we do this
|
||||||
|
func FilterID(id string, filters []string) bool {
|
||||||
|
for _, want := range filters {
|
||||||
|
isRegex := types.NotHexRegex.MatchString(want)
|
||||||
|
if isRegex {
|
||||||
|
match, err := regexp.MatchString(want, id)
|
||||||
|
if err == nil && match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(id, strings.ToLower(want)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForFile waits until a file has been created or the given timeout has occurred
|
||||||
|
func WaitForFile(path string, chWait chan error, timeout time.Duration) (bool, error) {
|
||||||
|
var inotifyEvents chan fsnotify.Event
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err == nil {
|
||||||
|
if err := watcher.Add(filepath.Dir(path)); err == nil {
|
||||||
|
inotifyEvents = watcher.Events
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := watcher.Close(); err != nil {
|
||||||
|
logrus.Errorf("Failed to close fsnotify watcher: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeoutChan <-chan time.Time
|
||||||
|
|
||||||
|
if timeout != 0 {
|
||||||
|
timeoutChan = time.After(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e := <-chWait:
|
||||||
|
return true, e
|
||||||
|
case <-inotifyEvents:
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
case <-time.After(25 * time.Millisecond):
|
||||||
|
// Check periodically for the file existence. It is needed
|
||||||
|
// if the inotify watcher could not have been created. It is
|
||||||
|
// also useful when using inotify as if for any reasons we missed
|
||||||
|
// a notification, we won't hang the process.
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
case <-timeoutChan:
|
||||||
|
return false, fmt.Errorf("timed out waiting for file %s", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
135
vendor/github.com/containers/common/pkg/util/util_supported.go
generated
vendored
Normal file
135
vendor/github.com/containers/common/pkg/util/util_supported.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
//go:build linux || darwin || freebsd
|
||||||
|
// +build linux darwin freebsd
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/storage/pkg/homedir"
|
||||||
|
"github.com/containers/storage/pkg/unshare"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
terminal "golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rootlessRuntimeDirOnce sync.Once
|
||||||
|
rootlessRuntimeDir string
|
||||||
|
)
|
||||||
|
|
||||||
|
// isWriteableOnlyByOwner checks that the specified permission mask allows write
|
||||||
|
// access only to the owner.
|
||||||
|
func isWriteableOnlyByOwner(perm os.FileMode) bool {
|
||||||
|
return (perm & 0o722) == 0o700
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntimeDir returns the runtime directory
|
||||||
|
func GetRuntimeDir() (string, error) {
|
||||||
|
var rootlessRuntimeDirError error
|
||||||
|
|
||||||
|
rootlessRuntimeDirOnce.Do(func() {
|
||||||
|
runtimeDir, err := homedir.GetRuntimeDir()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debug(err)
|
||||||
|
}
|
||||||
|
if runtimeDir != "" {
|
||||||
|
st, err := os.Stat(runtimeDir)
|
||||||
|
if err != nil {
|
||||||
|
rootlessRuntimeDirError = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if int(st.Sys().(*syscall.Stat_t).Uid) != os.Geteuid() {
|
||||||
|
rootlessRuntimeDirError = fmt.Errorf("XDG_RUNTIME_DIR directory %q is not owned by the current user", runtimeDir)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uid := fmt.Sprintf("%d", unshare.GetRootlessUID())
|
||||||
|
if runtimeDir == "" {
|
||||||
|
tmpDir := filepath.Join("/run", "user", uid)
|
||||||
|
if err := os.MkdirAll(tmpDir, 0o700); err != nil {
|
||||||
|
logrus.Debugf("unable to make temp dir: %v", err)
|
||||||
|
}
|
||||||
|
st, err := os.Stat(tmpDir)
|
||||||
|
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && isWriteableOnlyByOwner(st.Mode().Perm()) {
|
||||||
|
runtimeDir = tmpDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtimeDir == "" {
|
||||||
|
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("podman-run-%s", uid))
|
||||||
|
if err := os.MkdirAll(tmpDir, 0o700); err != nil {
|
||||||
|
logrus.Debugf("unable to make temp dir %v", err)
|
||||||
|
}
|
||||||
|
st, err := os.Stat(tmpDir)
|
||||||
|
if err == nil && int(st.Sys().(*syscall.Stat_t).Uid) == os.Geteuid() && isWriteableOnlyByOwner(st.Mode().Perm()) {
|
||||||
|
runtimeDir = tmpDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtimeDir == "" {
|
||||||
|
home := os.Getenv("HOME")
|
||||||
|
if home == "" {
|
||||||
|
rootlessRuntimeDirError = errors.New("neither XDG_RUNTIME_DIR nor HOME was set non-empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolvedHome, err := filepath.EvalSymlinks(home)
|
||||||
|
if err != nil {
|
||||||
|
rootlessRuntimeDirError = fmt.Errorf("cannot resolve home: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
runtimeDir = filepath.Join(resolvedHome, "rundir")
|
||||||
|
}
|
||||||
|
rootlessRuntimeDir = runtimeDir
|
||||||
|
})
|
||||||
|
|
||||||
|
if rootlessRuntimeDirError != nil {
|
||||||
|
return "", rootlessRuntimeDirError
|
||||||
|
}
|
||||||
|
return rootlessRuntimeDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a password from the terminal without echo.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
// Store and restore the terminal status on interruptions to
|
||||||
|
// avoid that the terminal remains in the password state
|
||||||
|
// This is necessary as for https://github.com/golang/go/issues/31180
|
||||||
|
|
||||||
|
oldState, err := terminal.GetState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return make([]byte, 0), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Buffer struct {
|
||||||
|
Buffer []byte
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
errorChannel := make(chan Buffer, 1)
|
||||||
|
|
||||||
|
// SIGINT and SIGTERM restore the terminal, otherwise the no-echo mode would remain intact
|
||||||
|
interruptChannel := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interruptChannel, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
defer func() {
|
||||||
|
signal.Stop(interruptChannel)
|
||||||
|
close(interruptChannel)
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for range interruptChannel {
|
||||||
|
if oldState != nil {
|
||||||
|
_ = terminal.Restore(fd, oldState)
|
||||||
|
}
|
||||||
|
errorChannel <- Buffer{Buffer: make([]byte, 0), Error: ErrInterrupt}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
buf, err := terminal.ReadPassword(fd)
|
||||||
|
errorChannel <- Buffer{Buffer: buf, Error: err}
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := <-errorChannel
|
||||||
|
return buf.Buffer, buf.Error
|
||||||
|
}
|
28
vendor/github.com/containers/common/pkg/util/util_windows.go
generated
vendored
Normal file
28
vendor/github.com/containers/common/pkg/util/util_windows.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
terminal "golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getRuntimeDir returns the runtime directory
|
||||||
|
func GetRuntimeDir() (string, error) {
|
||||||
|
return "", errors.New("this function is not implemented for windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPassword reads a password from the terminal.
|
||||||
|
func ReadPassword(fd int) ([]byte, error) {
|
||||||
|
oldState, err := terminal.GetState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return make([]byte, 0), err
|
||||||
|
}
|
||||||
|
buf, err := terminal.ReadPassword(fd)
|
||||||
|
if oldState != nil {
|
||||||
|
_ = terminal.Restore(fd, oldState)
|
||||||
|
}
|
||||||
|
return buf, err
|
||||||
|
}
|
12
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
Normal file
12
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
Normal file
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
go.sum linguist-generated
|
6
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
Normal file
6
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# go test -c output
|
||||||
|
*.test
|
||||||
|
*.test.exe
|
||||||
|
|
||||||
|
# Output of go build ./cmd/fsnotify
|
||||||
|
/fsnotify
|
2
vendor/github.com/fsnotify/fsnotify/.mailmap
generated
vendored
Normal file
2
vendor/github.com/fsnotify/fsnotify/.mailmap
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Chris Howey <howeyc@gmail.com> <chris@howey.me>
|
||||||
|
Nathan Youngman <git@nathany.com> <4566+nathany@users.noreply.github.com>
|
470
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
Normal file
470
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
Nothing yet.
|
||||||
|
|
||||||
|
## [1.6.0] - 2022-10-13
|
||||||
|
|
||||||
|
This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1,
|
||||||
|
but not documented). It also increases the minimum Linux version to 2.6.32.
|
||||||
|
|
||||||
|
### Additions
|
||||||
|
|
||||||
|
- all: add `Event.Has()` and `Op.Has()` ([#477])
|
||||||
|
|
||||||
|
This makes checking events a lot easier; for example:
|
||||||
|
|
||||||
|
if event.Op&Write == Write && !(event.Op&Remove == Remove) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Becomes:
|
||||||
|
|
||||||
|
if event.Has(Write) && !event.Has(Remove) {
|
||||||
|
}
|
||||||
|
|
||||||
|
- all: add cmd/fsnotify ([#463])
|
||||||
|
|
||||||
|
A command-line utility for testing and some examples.
|
||||||
|
|
||||||
|
### Changes and fixes
|
||||||
|
|
||||||
|
- inotify: don't ignore events for files that don't exist ([#260], [#470])
|
||||||
|
|
||||||
|
Previously the inotify watcher would call `os.Lstat()` to check if a file
|
||||||
|
still exists before emitting events.
|
||||||
|
|
||||||
|
This was inconsistent with other platforms and resulted in inconsistent event
|
||||||
|
reporting (e.g. when a file is quickly removed and re-created), and generally
|
||||||
|
a source of confusion. It was added in 2013 to fix a memory leak that no
|
||||||
|
longer exists.
|
||||||
|
|
||||||
|
- all: return `ErrNonExistentWatch` when `Remove()` is called on a path that's
|
||||||
|
not watched ([#460])
|
||||||
|
|
||||||
|
- inotify: replace epoll() with non-blocking inotify ([#434])
|
||||||
|
|
||||||
|
Non-blocking inotify was not generally available at the time this library was
|
||||||
|
written in 2014, but now it is. As a result, the minimum Linux version is
|
||||||
|
bumped from 2.6.27 to 2.6.32. This hugely simplifies the code and is faster.
|
||||||
|
|
||||||
|
- kqueue: don't check for events every 100ms ([#480])
|
||||||
|
|
||||||
|
The watcher would wake up every 100ms, even when there was nothing to do. Now
|
||||||
|
it waits until there is something to do.
|
||||||
|
|
||||||
|
- macos: retry opening files on EINTR ([#475])
|
||||||
|
|
||||||
|
- kqueue: skip unreadable files ([#479])
|
||||||
|
|
||||||
|
kqueue requires a file descriptor for every file in a directory; this would
|
||||||
|
fail if a file was unreadable by the current user. Now these files are simply
|
||||||
|
skipped.
|
||||||
|
|
||||||
|
- windows: fix renaming a watched directory if the parent is also watched ([#370])
|
||||||
|
|
||||||
|
- windows: increase buffer size from 4K to 64K ([#485])
|
||||||
|
|
||||||
|
- windows: close file handle on Remove() ([#288])
|
||||||
|
|
||||||
|
- kqueue: put pathname in the error if watching a file fails ([#471])
|
||||||
|
|
||||||
|
- inotify, windows: calling Close() more than once could race ([#465])
|
||||||
|
|
||||||
|
- kqueue: improve Close() performance ([#233])
|
||||||
|
|
||||||
|
- all: various documentation additions and clarifications.
|
||||||
|
|
||||||
|
[#233]: https://github.com/fsnotify/fsnotify/pull/233
|
||||||
|
[#260]: https://github.com/fsnotify/fsnotify/pull/260
|
||||||
|
[#288]: https://github.com/fsnotify/fsnotify/pull/288
|
||||||
|
[#370]: https://github.com/fsnotify/fsnotify/pull/370
|
||||||
|
[#434]: https://github.com/fsnotify/fsnotify/pull/434
|
||||||
|
[#460]: https://github.com/fsnotify/fsnotify/pull/460
|
||||||
|
[#463]: https://github.com/fsnotify/fsnotify/pull/463
|
||||||
|
[#465]: https://github.com/fsnotify/fsnotify/pull/465
|
||||||
|
[#470]: https://github.com/fsnotify/fsnotify/pull/470
|
||||||
|
[#471]: https://github.com/fsnotify/fsnotify/pull/471
|
||||||
|
[#475]: https://github.com/fsnotify/fsnotify/pull/475
|
||||||
|
[#477]: https://github.com/fsnotify/fsnotify/pull/477
|
||||||
|
[#479]: https://github.com/fsnotify/fsnotify/pull/479
|
||||||
|
[#480]: https://github.com/fsnotify/fsnotify/pull/480
|
||||||
|
[#485]: https://github.com/fsnotify/fsnotify/pull/485
|
||||||
|
|
||||||
|
## [1.5.4] - 2022-04-25
|
||||||
|
|
||||||
|
* Windows: add missing defer to `Watcher.WatchList` [#447](https://github.com/fsnotify/fsnotify/pull/447)
|
||||||
|
* go.mod: use latest x/sys [#444](https://github.com/fsnotify/fsnotify/pull/444)
|
||||||
|
* Fix compilation for OpenBSD [#443](https://github.com/fsnotify/fsnotify/pull/443)
|
||||||
|
|
||||||
|
## [1.5.3] - 2022-04-22
|
||||||
|
|
||||||
|
* This version is retracted. An incorrect branch is published accidentally [#445](https://github.com/fsnotify/fsnotify/issues/445)
|
||||||
|
|
||||||
|
## [1.5.2] - 2022-04-21
|
||||||
|
|
||||||
|
* Add a feature to return the directories and files that are being monitored [#374](https://github.com/fsnotify/fsnotify/pull/374)
|
||||||
|
* Fix potential crash on windows if `raw.FileNameLength` exceeds `syscall.MAX_PATH` [#361](https://github.com/fsnotify/fsnotify/pull/361)
|
||||||
|
* Allow build on unsupported GOOS [#424](https://github.com/fsnotify/fsnotify/pull/424)
|
||||||
|
* Don't set `poller.fd` twice in `newFdPoller` [#406](https://github.com/fsnotify/fsnotify/pull/406)
|
||||||
|
* fix go vet warnings: call to `(*T).Fatalf` from a non-test goroutine [#416](https://github.com/fsnotify/fsnotify/pull/416)
|
||||||
|
|
||||||
|
## [1.5.1] - 2021-08-24
|
||||||
|
|
||||||
|
* Revert Add AddRaw to not follow symlinks [#394](https://github.com/fsnotify/fsnotify/pull/394)
|
||||||
|
|
||||||
|
## [1.5.0] - 2021-08-20
|
||||||
|
|
||||||
|
* Go: Increase minimum required version to Go 1.12 [#381](https://github.com/fsnotify/fsnotify/pull/381)
|
||||||
|
* Feature: Add AddRaw method which does not follow symlinks when adding a watch [#289](https://github.com/fsnotify/fsnotify/pull/298)
|
||||||
|
* Windows: Follow symlinks by default like on all other systems [#289](https://github.com/fsnotify/fsnotify/pull/289)
|
||||||
|
* CI: Use GitHub Actions for CI and cover go 1.12-1.17
|
||||||
|
[#378](https://github.com/fsnotify/fsnotify/pull/378)
|
||||||
|
[#381](https://github.com/fsnotify/fsnotify/pull/381)
|
||||||
|
[#385](https://github.com/fsnotify/fsnotify/pull/385)
|
||||||
|
* Go 1.14+: Fix unsafe pointer conversion [#325](https://github.com/fsnotify/fsnotify/pull/325)
|
||||||
|
|
||||||
|
## [1.4.9] - 2020-03-11
|
||||||
|
|
||||||
|
* Move example usage to the readme #329. This may resolve #328.
|
||||||
|
|
||||||
|
## [1.4.8] - 2020-03-10
|
||||||
|
|
||||||
|
* CI: test more go versions (@nathany 1d13583d846ea9d66dcabbfefbfb9d8e6fb05216)
|
||||||
|
* Tests: Queued inotify events could have been read by the test before max_queued_events was hit (@matthias-stone #265)
|
||||||
|
* Tests: t.Fatalf -> t.Errorf in go routines (@gdey #266)
|
||||||
|
* CI: Less verbosity (@nathany #267)
|
||||||
|
* Tests: Darwin: Exchangedata is deprecated on 10.13 (@nathany #267)
|
||||||
|
* Tests: Check if channels are closed in the example (@alexeykazakov #244)
|
||||||
|
* CI: Only run golint on latest version of go and fix issues (@cpuguy83 #284)
|
||||||
|
* CI: Add windows to travis matrix (@cpuguy83 #284)
|
||||||
|
* Docs: Remover appveyor badge (@nathany 11844c0959f6fff69ba325d097fce35bd85a8e93)
|
||||||
|
* Linux: create epoll and pipe fds with close-on-exec (@JohannesEbke #219)
|
||||||
|
* Linux: open files with close-on-exec (@linxiulei #273)
|
||||||
|
* Docs: Plan to support fanotify (@nathany ab058b44498e8b7566a799372a39d150d9ea0119 )
|
||||||
|
* Project: Add go.mod (@nathany #309)
|
||||||
|
* Project: Revise editor config (@nathany #309)
|
||||||
|
* Project: Update copyright for 2019 (@nathany #309)
|
||||||
|
* CI: Drop go1.8 from CI matrix (@nathany #309)
|
||||||
|
* Docs: Updating the FAQ section for supportability with NFS & FUSE filesystems (@Pratik32 4bf2d1fec78374803a39307bfb8d340688f4f28e )
|
||||||
|
|
||||||
|
## [1.4.7] - 2018-01-09
|
||||||
|
|
||||||
|
* BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine)
|
||||||
|
* Tests: Fix missing verb on format string (thanks @rchiossi)
|
||||||
|
* Linux: Fix deadlock in Remove (thanks @aarondl)
|
||||||
|
* Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne)
|
||||||
|
* Docs: Moved FAQ into the README (thanks @vahe)
|
||||||
|
* Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich)
|
||||||
|
* Docs: replace references to OS X with macOS
|
||||||
|
|
||||||
|
## [1.4.2] - 2016-10-10
|
||||||
|
|
||||||
|
* Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack)
|
||||||
|
|
||||||
|
## [1.4.1] - 2016-10-04
|
||||||
|
|
||||||
|
* Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack)
|
||||||
|
|
||||||
|
## [1.4.0] - 2016-10-01
|
||||||
|
|
||||||
|
* add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie)
|
||||||
|
|
||||||
|
## [1.3.1] - 2016-06-28
|
||||||
|
|
||||||
|
* Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc)
|
||||||
|
|
||||||
|
## [1.3.0] - 2016-04-19
|
||||||
|
|
||||||
|
* Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135)
|
||||||
|
|
||||||
|
## [1.2.10] - 2016-03-02
|
||||||
|
|
||||||
|
* Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj)
|
||||||
|
|
||||||
|
## [1.2.9] - 2016-01-13
|
||||||
|
|
||||||
|
kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep)
|
||||||
|
|
||||||
|
## [1.2.8] - 2015-12-17
|
||||||
|
|
||||||
|
* kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test)
|
||||||
|
* inotify: fix race in test
|
||||||
|
* enable race detection for continuous integration (Linux, Mac, Windows)
|
||||||
|
|
||||||
|
## [1.2.5] - 2015-10-17
|
||||||
|
|
||||||
|
* inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki)
|
||||||
|
* inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken)
|
||||||
|
* kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie)
|
||||||
|
* kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion)
|
||||||
|
|
||||||
|
## [1.2.1] - 2015-10-14
|
||||||
|
|
||||||
|
* kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx)
|
||||||
|
|
||||||
|
## [1.2.0] - 2015-02-08
|
||||||
|
|
||||||
|
* inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD)
|
||||||
|
* inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD)
|
||||||
|
* kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59)
|
||||||
|
|
||||||
|
## [1.1.1] - 2015-02-05
|
||||||
|
|
||||||
|
* inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD)
|
||||||
|
|
||||||
|
## [1.1.0] - 2014-12-12
|
||||||
|
|
||||||
|
* kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43)
|
||||||
|
* add low-level functions
|
||||||
|
* only need to store flags on directories
|
||||||
|
* less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13)
|
||||||
|
* done can be an unbuffered channel
|
||||||
|
* remove calls to os.NewSyscallError
|
||||||
|
* More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher)
|
||||||
|
* kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48)
|
||||||
|
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
||||||
|
|
||||||
|
## [1.0.4] - 2014-09-07
|
||||||
|
|
||||||
|
* kqueue: add dragonfly to the build tags.
|
||||||
|
* Rename source code files, rearrange code so exported APIs are at the top.
|
||||||
|
* Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang)
|
||||||
|
|
||||||
|
## [1.0.3] - 2014-08-19
|
||||||
|
|
||||||
|
* [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36)
|
||||||
|
|
||||||
|
## [1.0.2] - 2014-08-17
|
||||||
|
|
||||||
|
* [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||||
|
* [Fix] Make ./path and path equivalent. (thanks @zhsso)
|
||||||
|
|
||||||
|
## [1.0.0] - 2014-08-15
|
||||||
|
|
||||||
|
* [API] Remove AddWatch on Windows, use Add.
|
||||||
|
* Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30)
|
||||||
|
* Minor updates based on feedback from golint.
|
||||||
|
|
||||||
|
## dev / 2014-07-09
|
||||||
|
|
||||||
|
* Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify).
|
||||||
|
* Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno)
|
||||||
|
|
||||||
|
## dev / 2014-07-04
|
||||||
|
|
||||||
|
* kqueue: fix incorrect mutex used in Close()
|
||||||
|
* Update example to demonstrate usage of Op.
|
||||||
|
|
||||||
|
## dev / 2014-06-28
|
||||||
|
|
||||||
|
* [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4)
|
||||||
|
* Fix for String() method on Event (thanks Alex Brainman)
|
||||||
|
* Don't build on Plan 9 or Solaris (thanks @4ad)
|
||||||
|
|
||||||
|
## dev / 2014-06-21
|
||||||
|
|
||||||
|
* Events channel of type Event rather than *Event.
|
||||||
|
* [internal] use syscall constants directly for inotify and kqueue.
|
||||||
|
* [internal] kqueue: rename events to kevents and fileEvent to event.
|
||||||
|
|
||||||
|
## dev / 2014-06-19
|
||||||
|
|
||||||
|
* Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally).
|
||||||
|
* [internal] remove cookie from Event struct (unused).
|
||||||
|
* [internal] Event struct has the same definition across every OS.
|
||||||
|
* [internal] remove internal watch and removeWatch methods.
|
||||||
|
|
||||||
|
## dev / 2014-06-12
|
||||||
|
|
||||||
|
* [API] Renamed Watch() to Add() and RemoveWatch() to Remove().
|
||||||
|
* [API] Pluralized channel names: Events and Errors.
|
||||||
|
* [API] Renamed FileEvent struct to Event.
|
||||||
|
* [API] Op constants replace methods like IsCreate().
|
||||||
|
|
||||||
|
## dev / 2014-06-12
|
||||||
|
|
||||||
|
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
||||||
|
|
||||||
|
## dev / 2014-05-23
|
||||||
|
|
||||||
|
* [API] Remove current implementation of WatchFlags.
|
||||||
|
* current implementation doesn't take advantage of OS for efficiency
|
||||||
|
* provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes
|
||||||
|
* no tests for the current implementation
|
||||||
|
* not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195)
|
||||||
|
|
||||||
|
## [0.9.3] - 2014-12-31
|
||||||
|
|
||||||
|
* kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51)
|
||||||
|
|
||||||
|
## [0.9.2] - 2014-08-17
|
||||||
|
|
||||||
|
* [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso)
|
||||||
|
|
||||||
|
## [0.9.1] - 2014-06-12
|
||||||
|
|
||||||
|
* Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98)
|
||||||
|
|
||||||
|
## [0.9.0] - 2014-01-17
|
||||||
|
|
||||||
|
* IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany)
|
||||||
|
* [Fix] kqueue: fix deadlock [#77][] (thanks @cespare)
|
||||||
|
* [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library.
|
||||||
|
|
||||||
|
## [0.8.12] - 2013-11-13
|
||||||
|
|
||||||
|
* [API] Remove FD_SET and friends from Linux adapter
|
||||||
|
|
||||||
|
## [0.8.11] - 2013-11-02
|
||||||
|
|
||||||
|
* [Doc] Add Changelog [#72][] (thanks @nathany)
|
||||||
|
* [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond)
|
||||||
|
|
||||||
|
## [0.8.10] - 2013-10-19
|
||||||
|
|
||||||
|
* [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott)
|
||||||
|
* [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer)
|
||||||
|
* [Doc] specify OS-specific limits in README (thanks @debrando)
|
||||||
|
|
||||||
|
## [0.8.9] - 2013-09-08
|
||||||
|
|
||||||
|
* [Doc] Contributing (thanks @nathany)
|
||||||
|
* [Doc] update package path in example code [#63][] (thanks @paulhammond)
|
||||||
|
* [Doc] GoCI badge in README (Linux only) [#60][]
|
||||||
|
* [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany)
|
||||||
|
|
||||||
|
## [0.8.8] - 2013-06-17
|
||||||
|
|
||||||
|
* [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie)
|
||||||
|
|
||||||
|
## [0.8.7] - 2013-06-03
|
||||||
|
|
||||||
|
* [API] Make syscall flags internal
|
||||||
|
* [Fix] inotify: ignore event changes
|
||||||
|
* [Fix] race in symlink test [#45][] (reported by @srid)
|
||||||
|
* [Fix] tests on Windows
|
||||||
|
* lower case error messages
|
||||||
|
|
||||||
|
## [0.8.6] - 2013-05-23
|
||||||
|
|
||||||
|
* kqueue: Use EVT_ONLY flag on Darwin
|
||||||
|
* [Doc] Update README with full example
|
||||||
|
|
||||||
|
## [0.8.5] - 2013-05-09
|
||||||
|
|
||||||
|
* [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg)
|
||||||
|
|
||||||
|
## [0.8.4] - 2013-04-07
|
||||||
|
|
||||||
|
* [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz)
|
||||||
|
|
||||||
|
## [0.8.3] - 2013-03-13
|
||||||
|
|
||||||
|
* [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin)
|
||||||
|
* [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin)
|
||||||
|
|
||||||
|
## [0.8.2] - 2013-02-07
|
||||||
|
|
||||||
|
* [Doc] add Authors
|
||||||
|
* [Fix] fix data races for map access [#29][] (thanks @fsouza)
|
||||||
|
|
||||||
|
## [0.8.1] - 2013-01-09
|
||||||
|
|
||||||
|
* [Fix] Windows path separators
|
||||||
|
* [Doc] BSD License
|
||||||
|
|
||||||
|
## [0.8.0] - 2012-11-09
|
||||||
|
|
||||||
|
* kqueue: directory watching improvements (thanks @vmirage)
|
||||||
|
* inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto)
|
||||||
|
* [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr)
|
||||||
|
|
||||||
|
## [0.7.4] - 2012-10-09
|
||||||
|
|
||||||
|
* [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji)
|
||||||
|
* [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig)
|
||||||
|
* [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig)
|
||||||
|
* [Fix] kqueue: modify after recreation of file
|
||||||
|
|
||||||
|
## [0.7.3] - 2012-09-27
|
||||||
|
|
||||||
|
* [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage)
|
||||||
|
* [Fix] kqueue: no longer get duplicate CREATE events
|
||||||
|
|
||||||
|
## [0.7.2] - 2012-09-01
|
||||||
|
|
||||||
|
* kqueue: events for created directories
|
||||||
|
|
||||||
|
## [0.7.1] - 2012-07-14
|
||||||
|
|
||||||
|
* [Fix] for renaming files
|
||||||
|
|
||||||
|
## [0.7.0] - 2012-07-02
|
||||||
|
|
||||||
|
* [Feature] FSNotify flags
|
||||||
|
* [Fix] inotify: Added file name back to event path
|
||||||
|
|
||||||
|
## [0.6.0] - 2012-06-06
|
||||||
|
|
||||||
|
* kqueue: watch files after directory created (thanks @tmc)
|
||||||
|
|
||||||
|
## [0.5.1] - 2012-05-22
|
||||||
|
|
||||||
|
* [Fix] inotify: remove all watches before Close()
|
||||||
|
|
||||||
|
## [0.5.0] - 2012-05-03
|
||||||
|
|
||||||
|
* [API] kqueue: return errors during watch instead of sending over channel
|
||||||
|
* kqueue: match symlink behavior on Linux
|
||||||
|
* inotify: add `DELETE_SELF` (requested by @taralx)
|
||||||
|
* [Fix] kqueue: handle EINTR (reported by @robfig)
|
||||||
|
* [Doc] Godoc example [#1][] (thanks @davecheney)
|
||||||
|
|
||||||
|
## [0.4.0] - 2012-03-30
|
||||||
|
|
||||||
|
* Go 1 released: build with go tool
|
||||||
|
* [Feature] Windows support using winfsnotify
|
||||||
|
* Windows does not have attribute change notifications
|
||||||
|
* Roll attribute notifications into IsModify
|
||||||
|
|
||||||
|
## [0.3.0] - 2012-02-19
|
||||||
|
|
||||||
|
* kqueue: add files when watch directory
|
||||||
|
|
||||||
|
## [0.2.0] - 2011-12-30
|
||||||
|
|
||||||
|
* update to latest Go weekly code
|
||||||
|
|
||||||
|
## [0.1.0] - 2011-10-19
|
||||||
|
|
||||||
|
* kqueue: add watch on file creation to match inotify
|
||||||
|
* kqueue: create file event
|
||||||
|
* inotify: ignore `IN_IGNORED` events
|
||||||
|
* event String()
|
||||||
|
* linux: common FileEvent functions
|
||||||
|
* initial commit
|
||||||
|
|
||||||
|
[#79]: https://github.com/howeyc/fsnotify/pull/79
|
||||||
|
[#77]: https://github.com/howeyc/fsnotify/pull/77
|
||||||
|
[#72]: https://github.com/howeyc/fsnotify/issues/72
|
||||||
|
[#71]: https://github.com/howeyc/fsnotify/issues/71
|
||||||
|
[#70]: https://github.com/howeyc/fsnotify/issues/70
|
||||||
|
[#63]: https://github.com/howeyc/fsnotify/issues/63
|
||||||
|
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
||||||
|
[#60]: https://github.com/howeyc/fsnotify/issues/60
|
||||||
|
[#59]: https://github.com/howeyc/fsnotify/issues/59
|
||||||
|
[#49]: https://github.com/howeyc/fsnotify/issues/49
|
||||||
|
[#45]: https://github.com/howeyc/fsnotify/issues/45
|
||||||
|
[#40]: https://github.com/howeyc/fsnotify/issues/40
|
||||||
|
[#36]: https://github.com/howeyc/fsnotify/issues/36
|
||||||
|
[#33]: https://github.com/howeyc/fsnotify/issues/33
|
||||||
|
[#29]: https://github.com/howeyc/fsnotify/issues/29
|
||||||
|
[#25]: https://github.com/howeyc/fsnotify/issues/25
|
||||||
|
[#24]: https://github.com/howeyc/fsnotify/issues/24
|
||||||
|
[#21]: https://github.com/howeyc/fsnotify/issues/21
|
26
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
Normal file
26
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Thank you for your interest in contributing to fsnotify! We try to review and
|
||||||
|
merge PRs in a reasonable timeframe, but please be aware that:
|
||||||
|
|
||||||
|
- To avoid "wasted" work, please discus changes on the issue tracker first. You
|
||||||
|
can just send PRs, but they may end up being rejected for one reason or the
|
||||||
|
other.
|
||||||
|
|
||||||
|
- fsnotify is a cross-platform library, and changes must work reasonably well on
|
||||||
|
all supported platforms.
|
||||||
|
|
||||||
|
- Changes will need to be compatible; old code should still compile, and the
|
||||||
|
runtime behaviour can't change in ways that are likely to lead to problems for
|
||||||
|
users.
|
||||||
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
Just `go test ./...` runs all the tests; the CI runs this on all supported
|
||||||
|
platforms. Testing different platforms locally can be done with something like
|
||||||
|
[goon] or [Vagrant], but this isn't super-easy to set up at the moment.
|
||||||
|
|
||||||
|
Use the `-short` flag to make the "stress test" run faster.
|
||||||
|
|
||||||
|
|
||||||
|
[goon]: https://github.com/arp242/goon
|
||||||
|
[Vagrant]: https://www.vagrantup.com/
|
||||||
|
[integration_test.go]: /integration_test.go
|
25
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
Normal file
25
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
Copyright © 2012 The Go Authors. All rights reserved.
|
||||||
|
Copyright © fsnotify Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its contributors may be used
|
||||||
|
to endorse or promote products derived from this software without specific
|
||||||
|
prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
161
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
Normal file
161
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
fsnotify is a Go library to provide cross-platform filesystem notifications on
|
||||||
|
Windows, Linux, macOS, and BSD systems.
|
||||||
|
|
||||||
|
Go 1.16 or newer is required; the full documentation is at
|
||||||
|
https://pkg.go.dev/github.com/fsnotify/fsnotify
|
||||||
|
|
||||||
|
**It's best to read the documentation at pkg.go.dev, as it's pinned to the last
|
||||||
|
released version, whereas this README is for the last development version which
|
||||||
|
may include additions/changes.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Platform support:
|
||||||
|
|
||||||
|
| Adapter | OS | Status |
|
||||||
|
| --------------------- | ---------------| -------------------------------------------------------------|
|
||||||
|
| inotify | Linux 2.6.32+ | Supported |
|
||||||
|
| kqueue | BSD, macOS | Supported |
|
||||||
|
| ReadDirectoryChangesW | Windows | Supported |
|
||||||
|
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
|
||||||
|
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) |
|
||||||
|
| fanotify | Linux 5.9+ | [Maybe](https://github.com/fsnotify/fsnotify/issues/114) |
|
||||||
|
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
|
||||||
|
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
|
||||||
|
|
||||||
|
Linux and macOS should include Android and iOS, but these are currently untested.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
A basic example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Create new watcher.
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
// Start listening for events.
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-watcher.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("event:", event)
|
||||||
|
if event.Has(fsnotify.Write) {
|
||||||
|
log.Println("modified file:", event.Name)
|
||||||
|
}
|
||||||
|
case err, ok := <-watcher.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Add a path.
|
||||||
|
err = watcher.Add("/tmp")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block main goroutine forever.
|
||||||
|
<-make(chan struct{})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Some more examples can be found in [cmd/fsnotify](cmd/fsnotify), which can be
|
||||||
|
run with:
|
||||||
|
|
||||||
|
% go run ./cmd/fsnotify
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
### Will a file still be watched when it's moved to another directory?
|
||||||
|
No, not unless you are watching the location it was moved to.
|
||||||
|
|
||||||
|
### Are subdirectories watched too?
|
||||||
|
No, you must add watches for any directory you want to watch (a recursive
|
||||||
|
watcher is on the roadmap: [#18]).
|
||||||
|
|
||||||
|
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
||||||
|
|
||||||
|
### Do I have to watch the Error and Event channels in a goroutine?
|
||||||
|
As of now, yes (you can read both channels in the same goroutine using `select`,
|
||||||
|
you don't need a separate goroutine for both channels; see the example).
|
||||||
|
|
||||||
|
### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys?
|
||||||
|
fsnotify requires support from underlying OS to work. The current NFS and SMB
|
||||||
|
protocols does not provide network level support for file notifications, and
|
||||||
|
neither do the /proc and /sys virtual filesystems.
|
||||||
|
|
||||||
|
This could be fixed with a polling watcher ([#9]), but it's not yet implemented.
|
||||||
|
|
||||||
|
[#9]: https://github.com/fsnotify/fsnotify/issues/9
|
||||||
|
|
||||||
|
Platform-specific notes
|
||||||
|
-----------------------
|
||||||
|
### Linux
|
||||||
|
When a file is removed a REMOVE event won't be emitted until all file
|
||||||
|
descriptors are closed; it will emit a CHMOD instead:
|
||||||
|
|
||||||
|
fp := os.Open("file")
|
||||||
|
os.Remove("file") // CHMOD
|
||||||
|
fp.Close() // REMOVE
|
||||||
|
|
||||||
|
This is the event that inotify sends, so not much can be changed about this.
|
||||||
|
|
||||||
|
The `fs.inotify.max_user_watches` sysctl variable specifies the upper limit for
|
||||||
|
the number of watches per user, and `fs.inotify.max_user_instances` specifies
|
||||||
|
the maximum number of inotify instances per user. Every Watcher you create is an
|
||||||
|
"instance", and every path you add is a "watch".
|
||||||
|
|
||||||
|
These are also exposed in `/proc` as `/proc/sys/fs/inotify/max_user_watches` and
|
||||||
|
`/proc/sys/fs/inotify/max_user_instances`
|
||||||
|
|
||||||
|
To increase them you can use `sysctl` or write the value to proc file:
|
||||||
|
|
||||||
|
# The default values on Linux 5.18
|
||||||
|
sysctl fs.inotify.max_user_watches=124983
|
||||||
|
sysctl fs.inotify.max_user_instances=128
|
||||||
|
|
||||||
|
To make the changes persist on reboot edit `/etc/sysctl.conf` or
|
||||||
|
`/usr/lib/sysctl.d/50-default.conf` (details differ per Linux distro; check your
|
||||||
|
distro's documentation):
|
||||||
|
|
||||||
|
fs.inotify.max_user_watches=124983
|
||||||
|
fs.inotify.max_user_instances=128
|
||||||
|
|
||||||
|
Reaching the limit will result in a "no space left on device" or "too many open
|
||||||
|
files" error.
|
||||||
|
|
||||||
|
### kqueue (macOS, all BSD systems)
|
||||||
|
kqueue requires opening a file descriptor for every file that's being watched;
|
||||||
|
so if you're watching a directory with five files then that's six file
|
||||||
|
descriptors. You will run in to your system's "max open files" limit faster on
|
||||||
|
these platforms.
|
||||||
|
|
||||||
|
The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to
|
||||||
|
control the maximum number of open files.
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
Spotlight indexing on macOS can result in multiple events (see [#15]). A temporary
|
||||||
|
workaround is to add your folder(s) to the *Spotlight Privacy settings* until we
|
||||||
|
have a native FSEvents implementation (see [#11]).
|
||||||
|
|
||||||
|
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
[#15]: https://github.com/fsnotify/fsnotify/issues/15
|
162
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
Normal file
162
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
//go:build solaris
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher watches a set of paths, delivering events on a channel.
|
||||||
|
//
|
||||||
|
// A watcher should not be copied (e.g. pass it by pointer, rather than by
|
||||||
|
// value).
|
||||||
|
//
|
||||||
|
// # Linux notes
|
||||||
|
//
|
||||||
|
// When a file is removed a Remove event won't be emitted until all file
|
||||||
|
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||||
|
//
|
||||||
|
// fp := os.Open("file")
|
||||||
|
// os.Remove("file") // Triggers Chmod
|
||||||
|
// fp.Close() // Triggers Remove
|
||||||
|
//
|
||||||
|
// This is the event that inotify sends, so not much can be changed about this.
|
||||||
|
//
|
||||||
|
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||||
|
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||||
|
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||||
|
// create is an "instance", and every path you add is a "watch".
|
||||||
|
//
|
||||||
|
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||||
|
// /proc/sys/fs/inotify/max_user_instances
|
||||||
|
//
|
||||||
|
// To increase them you can use sysctl or write the value to the /proc file:
|
||||||
|
//
|
||||||
|
// # Default values on Linux 5.18
|
||||||
|
// sysctl fs.inotify.max_user_watches=124983
|
||||||
|
// sysctl fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||||
|
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||||
|
// your distro's documentation):
|
||||||
|
//
|
||||||
|
// fs.inotify.max_user_watches=124983
|
||||||
|
// fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||||
|
// files" error.
|
||||||
|
//
|
||||||
|
// # kqueue notes (macOS, BSD)
|
||||||
|
//
|
||||||
|
// kqueue requires opening a file descriptor for every file that's being watched;
|
||||||
|
// so if you're watching a directory with five files then that's six file
|
||||||
|
// descriptors. You will run in to your system's "max open files" limit faster on
|
||||||
|
// these platforms.
|
||||||
|
//
|
||||||
|
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
|
||||||
|
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||||
|
// systems.
|
||||||
|
//
|
||||||
|
// # macOS notes
|
||||||
|
//
|
||||||
|
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||||
|
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||||
|
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||||
|
//
|
||||||
|
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||||
|
type Watcher struct {
|
||||||
|
// Events sends the filesystem change events.
|
||||||
|
//
|
||||||
|
// fsnotify can send the following events; a "path" here can refer to a
|
||||||
|
// file, directory, symbolic link, or special file like a FIFO.
|
||||||
|
//
|
||||||
|
// fsnotify.Create A new path was created; this may be followed by one
|
||||||
|
// or more Write events if data also gets written to a
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// fsnotify.Remove A path was removed.
|
||||||
|
//
|
||||||
|
// fsnotify.Rename A path was renamed. A rename is always sent with the
|
||||||
|
// old path as Event.Name, and a Create event will be
|
||||||
|
// sent with the new name. Renames are only sent for
|
||||||
|
// paths that are currently watched; e.g. moving an
|
||||||
|
// unmonitored file into a monitored directory will
|
||||||
|
// show up as just a Create. Similarly, renaming a file
|
||||||
|
// to outside a monitored directory will show up as
|
||||||
|
// only a Rename.
|
||||||
|
//
|
||||||
|
// fsnotify.Write A file or named pipe was written to. A Truncate will
|
||||||
|
// also trigger a Write. A single "write action"
|
||||||
|
// initiated by the user may show up as one or multiple
|
||||||
|
// writes, depending on when the system syncs things to
|
||||||
|
// disk. For example when compiling a large Go program
|
||||||
|
// you may get hundreds of Write events, so you
|
||||||
|
// probably want to wait until you've stopped receiving
|
||||||
|
// them (see the dedup example in cmd/fsnotify).
|
||||||
|
//
|
||||||
|
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||||
|
// when a file is removed (or more accurately, when a
|
||||||
|
// link to an inode is removed). On kqueue it's sent
|
||||||
|
// and on kqueue when a file is truncated. On Windows
|
||||||
|
// it's never sent.
|
||||||
|
Events chan Event
|
||||||
|
|
||||||
|
// Errors sends any errors.
|
||||||
|
Errors chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWatcher creates a new Watcher.
|
||||||
|
func NewWatcher() (*Watcher, error) {
|
||||||
|
return nil, errors.New("FEN based watcher not yet supported for fsnotify\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes all watches and closes the events channel.
|
||||||
|
func (w *Watcher) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add starts monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// A path can only be watched once; attempting to watch it more than once will
|
||||||
|
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||||
|
// added. A watch will be automatically removed if the path is deleted.
|
||||||
|
//
|
||||||
|
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||||
|
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||||
|
// re-created, or if it's moved to a different filesystem.
|
||||||
|
//
|
||||||
|
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||||
|
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||||
|
//
|
||||||
|
// # Watching directories
|
||||||
|
//
|
||||||
|
// All files in a directory are monitored, including new files that are created
|
||||||
|
// after the watcher is started. Subdirectories are not watched (i.e. it's
|
||||||
|
// non-recursive).
|
||||||
|
//
|
||||||
|
// # Watching files
|
||||||
|
//
|
||||||
|
// Watching individual files (rather than directories) is generally not
|
||||||
|
// recommended as many tools update files atomically. Instead of "just" writing
|
||||||
|
// to the file a temporary file will be written to first, and if successful the
|
||||||
|
// temporary file is moved to to destination removing the original, or some
|
||||||
|
// variant thereof. The watcher on the original file is now lost, as it no
|
||||||
|
// longer exists.
|
||||||
|
//
|
||||||
|
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||||
|
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||||
|
func (w *Watcher) Add(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stops monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// Directories are always removed non-recursively. For example, if you added
|
||||||
|
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||||
|
//
|
||||||
|
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||||
|
func (w *Watcher) Remove(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
459
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
Normal file
459
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher watches a set of paths, delivering events on a channel.
|
||||||
|
//
|
||||||
|
// A watcher should not be copied (e.g. pass it by pointer, rather than by
|
||||||
|
// value).
|
||||||
|
//
|
||||||
|
// # Linux notes
|
||||||
|
//
|
||||||
|
// When a file is removed a Remove event won't be emitted until all file
|
||||||
|
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||||
|
//
|
||||||
|
// fp := os.Open("file")
|
||||||
|
// os.Remove("file") // Triggers Chmod
|
||||||
|
// fp.Close() // Triggers Remove
|
||||||
|
//
|
||||||
|
// This is the event that inotify sends, so not much can be changed about this.
|
||||||
|
//
|
||||||
|
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||||
|
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||||
|
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||||
|
// create is an "instance", and every path you add is a "watch".
|
||||||
|
//
|
||||||
|
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||||
|
// /proc/sys/fs/inotify/max_user_instances
|
||||||
|
//
|
||||||
|
// To increase them you can use sysctl or write the value to the /proc file:
|
||||||
|
//
|
||||||
|
// # Default values on Linux 5.18
|
||||||
|
// sysctl fs.inotify.max_user_watches=124983
|
||||||
|
// sysctl fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||||
|
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||||
|
// your distro's documentation):
|
||||||
|
//
|
||||||
|
// fs.inotify.max_user_watches=124983
|
||||||
|
// fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||||
|
// files" error.
|
||||||
|
//
|
||||||
|
// # kqueue notes (macOS, BSD)
|
||||||
|
//
|
||||||
|
// kqueue requires opening a file descriptor for every file that's being watched;
|
||||||
|
// so if you're watching a directory with five files then that's six file
|
||||||
|
// descriptors. You will run in to your system's "max open files" limit faster on
|
||||||
|
// these platforms.
|
||||||
|
//
|
||||||
|
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
|
||||||
|
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||||
|
// systems.
|
||||||
|
//
|
||||||
|
// # macOS notes
|
||||||
|
//
|
||||||
|
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||||
|
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||||
|
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||||
|
//
|
||||||
|
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||||
|
type Watcher struct {
|
||||||
|
// Events sends the filesystem change events.
|
||||||
|
//
|
||||||
|
// fsnotify can send the following events; a "path" here can refer to a
|
||||||
|
// file, directory, symbolic link, or special file like a FIFO.
|
||||||
|
//
|
||||||
|
// fsnotify.Create A new path was created; this may be followed by one
|
||||||
|
// or more Write events if data also gets written to a
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// fsnotify.Remove A path was removed.
|
||||||
|
//
|
||||||
|
// fsnotify.Rename A path was renamed. A rename is always sent with the
|
||||||
|
// old path as Event.Name, and a Create event will be
|
||||||
|
// sent with the new name. Renames are only sent for
|
||||||
|
// paths that are currently watched; e.g. moving an
|
||||||
|
// unmonitored file into a monitored directory will
|
||||||
|
// show up as just a Create. Similarly, renaming a file
|
||||||
|
// to outside a monitored directory will show up as
|
||||||
|
// only a Rename.
|
||||||
|
//
|
||||||
|
// fsnotify.Write A file or named pipe was written to. A Truncate will
|
||||||
|
// also trigger a Write. A single "write action"
|
||||||
|
// initiated by the user may show up as one or multiple
|
||||||
|
// writes, depending on when the system syncs things to
|
||||||
|
// disk. For example when compiling a large Go program
|
||||||
|
// you may get hundreds of Write events, so you
|
||||||
|
// probably want to wait until you've stopped receiving
|
||||||
|
// them (see the dedup example in cmd/fsnotify).
|
||||||
|
//
|
||||||
|
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||||
|
// when a file is removed (or more accurately, when a
|
||||||
|
// link to an inode is removed). On kqueue it's sent
|
||||||
|
// and on kqueue when a file is truncated. On Windows
|
||||||
|
// it's never sent.
|
||||||
|
Events chan Event
|
||||||
|
|
||||||
|
// Errors sends any errors.
|
||||||
|
Errors chan error
|
||||||
|
|
||||||
|
// Store fd here as os.File.Read() will no longer return on close after
|
||||||
|
// calling Fd(). See: https://github.com/golang/go/issues/26439
|
||||||
|
fd int
|
||||||
|
mu sync.Mutex // Map access
|
||||||
|
inotifyFile *os.File
|
||||||
|
watches map[string]*watch // Map of inotify watches (key: path)
|
||||||
|
paths map[int]string // Map of watched paths (key: watch descriptor)
|
||||||
|
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
|
||||||
|
doneResp chan struct{} // Channel to respond to Close
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWatcher creates a new Watcher.
|
||||||
|
func NewWatcher() (*Watcher, error) {
|
||||||
|
// Create inotify fd
|
||||||
|
// Need to set the FD to nonblocking mode in order for SetDeadline methods to work
|
||||||
|
// Otherwise, blocking i/o operations won't terminate on close
|
||||||
|
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
|
||||||
|
if fd == -1 {
|
||||||
|
return nil, errno
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &Watcher{
|
||||||
|
fd: fd,
|
||||||
|
inotifyFile: os.NewFile(uintptr(fd), ""),
|
||||||
|
watches: make(map[string]*watch),
|
||||||
|
paths: make(map[int]string),
|
||||||
|
Events: make(chan Event),
|
||||||
|
Errors: make(chan error),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
doneResp: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
go w.readEvents()
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the event was sent, or false if watcher is closed.
|
||||||
|
func (w *Watcher) sendEvent(e Event) bool {
|
||||||
|
select {
|
||||||
|
case w.Events <- e:
|
||||||
|
return true
|
||||||
|
case <-w.done:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the error was sent, or false if watcher is closed.
|
||||||
|
func (w *Watcher) sendError(err error) bool {
|
||||||
|
select {
|
||||||
|
case w.Errors <- err:
|
||||||
|
return true
|
||||||
|
case <-w.done:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) isClosed() bool {
|
||||||
|
select {
|
||||||
|
case <-w.done:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes all watches and closes the events channel.
|
||||||
|
func (w *Watcher) Close() error {
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.isClosed() {
|
||||||
|
w.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send 'close' signal to goroutine, and set the Watcher to closed.
|
||||||
|
close(w.done)
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
// Causes any blocking reads to return with an error, provided the file
|
||||||
|
// still supports deadline operations.
|
||||||
|
err := w.inotifyFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for goroutine to close
|
||||||
|
<-w.doneResp
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add starts monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// A path can only be watched once; attempting to watch it more than once will
|
||||||
|
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||||
|
// added. A watch will be automatically removed if the path is deleted.
|
||||||
|
//
|
||||||
|
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||||
|
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||||
|
// re-created, or if it's moved to a different filesystem.
|
||||||
|
//
|
||||||
|
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||||
|
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||||
|
//
|
||||||
|
// # Watching directories
|
||||||
|
//
|
||||||
|
// All files in a directory are monitored, including new files that are created
|
||||||
|
// after the watcher is started. Subdirectories are not watched (i.e. it's
|
||||||
|
// non-recursive).
|
||||||
|
//
|
||||||
|
// # Watching files
|
||||||
|
//
|
||||||
|
// Watching individual files (rather than directories) is generally not
|
||||||
|
// recommended as many tools update files atomically. Instead of "just" writing
|
||||||
|
// to the file a temporary file will be written to first, and if successful the
|
||||||
|
// temporary file is moved to to destination removing the original, or some
|
||||||
|
// variant thereof. The watcher on the original file is now lost, as it no
|
||||||
|
// longer exists.
|
||||||
|
//
|
||||||
|
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||||
|
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||||
|
func (w *Watcher) Add(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
if w.isClosed() {
|
||||||
|
return errors.New("inotify instance already closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
|
||||||
|
unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
|
||||||
|
unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
watchEntry := w.watches[name]
|
||||||
|
if watchEntry != nil {
|
||||||
|
flags |= watchEntry.flags | unix.IN_MASK_ADD
|
||||||
|
}
|
||||||
|
wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
|
||||||
|
if wd == -1 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
|
||||||
|
if watchEntry == nil {
|
||||||
|
w.watches[name] = &watch{wd: uint32(wd), flags: flags}
|
||||||
|
w.paths[wd] = name
|
||||||
|
} else {
|
||||||
|
watchEntry.wd = uint32(wd)
|
||||||
|
watchEntry.flags = flags
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stops monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// Directories are always removed non-recursively. For example, if you added
|
||||||
|
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||||
|
//
|
||||||
|
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||||
|
func (w *Watcher) Remove(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
|
||||||
|
// Fetch the watch.
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
watch, ok := w.watches[name]
|
||||||
|
|
||||||
|
// Remove it from inotify.
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We successfully removed the watch if InotifyRmWatch doesn't return an
|
||||||
|
// error, we need to clean up our internal state to ensure it matches
|
||||||
|
// inotify's kernel state.
|
||||||
|
delete(w.paths, int(watch.wd))
|
||||||
|
delete(w.watches, name)
|
||||||
|
|
||||||
|
// inotify_rm_watch will return EINVAL if the file has been deleted;
|
||||||
|
// the inotify will already have been removed.
|
||||||
|
// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
|
||||||
|
// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
|
||||||
|
// so that EINVAL means that the wd is being rm_watch()ed or its file removed
|
||||||
|
// by another thread and we have not received IN_IGNORE event.
|
||||||
|
success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
|
||||||
|
if success == -1 {
|
||||||
|
// TODO: Perhaps it's not helpful to return an error here in every case;
|
||||||
|
// The only two possible errors are:
|
||||||
|
//
|
||||||
|
// - EBADF, which happens when w.fd is not a valid file descriptor
|
||||||
|
// of any kind.
|
||||||
|
// - EINVAL, which is when fd is not an inotify descriptor or wd
|
||||||
|
// is not a valid watch descriptor. Watch descriptors are
|
||||||
|
// invalidated when they are removed explicitly or implicitly;
|
||||||
|
// explicitly by inotify_rm_watch, implicitly when the file they
|
||||||
|
// are watching is deleted.
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||||
|
func (w *Watcher) WatchList() []string {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
entries := make([]string, 0, len(w.watches))
|
||||||
|
for pathname := range w.watches {
|
||||||
|
entries = append(entries, pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
type watch struct {
|
||||||
|
wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
|
||||||
|
flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readEvents reads from the inotify file descriptor, converts the
|
||||||
|
// received events into Event objects and sends them via the Events channel
|
||||||
|
func (w *Watcher) readEvents() {
|
||||||
|
defer func() {
|
||||||
|
close(w.doneResp)
|
||||||
|
close(w.Errors)
|
||||||
|
close(w.Events)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var (
|
||||||
|
buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
|
||||||
|
errno error // Syscall errno
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
// See if we have been closed.
|
||||||
|
if w.isClosed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := w.inotifyFile.Read(buf[:])
|
||||||
|
switch {
|
||||||
|
case errors.Unwrap(err) == os.ErrClosed:
|
||||||
|
return
|
||||||
|
case err != nil:
|
||||||
|
if !w.sendError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < unix.SizeofInotifyEvent {
|
||||||
|
var err error
|
||||||
|
if n == 0 {
|
||||||
|
// If EOF is received. This should really never happen.
|
||||||
|
err = io.EOF
|
||||||
|
} else if n < 0 {
|
||||||
|
// If an error occurred while reading.
|
||||||
|
err = errno
|
||||||
|
} else {
|
||||||
|
// Read was too short.
|
||||||
|
err = errors.New("notify: short read in readEvents()")
|
||||||
|
}
|
||||||
|
if !w.sendError(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset uint32
|
||||||
|
// We don't know how many events we just read into the buffer
|
||||||
|
// While the offset points to at least one whole event...
|
||||||
|
for offset <= uint32(n-unix.SizeofInotifyEvent) {
|
||||||
|
var (
|
||||||
|
// Point "raw" to the event in the buffer
|
||||||
|
raw = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
||||||
|
mask = uint32(raw.Mask)
|
||||||
|
nameLen = uint32(raw.Len)
|
||||||
|
)
|
||||||
|
|
||||||
|
if mask&unix.IN_Q_OVERFLOW != 0 {
|
||||||
|
if !w.sendError(ErrEventOverflow) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the event happened to the watched directory or the watched file, the kernel
|
||||||
|
// doesn't append the filename to the event, but we would like to always fill the
|
||||||
|
// the "Name" field with a valid filename. We retrieve the path of the watch from
|
||||||
|
// the "paths" map.
|
||||||
|
w.mu.Lock()
|
||||||
|
name, ok := w.paths[int(raw.Wd)]
|
||||||
|
// IN_DELETE_SELF occurs when the file/directory being watched is removed.
|
||||||
|
// This is a sign to clean up the maps, otherwise we are no longer in sync
|
||||||
|
// with the inotify kernel state which has already deleted the watch
|
||||||
|
// automatically.
|
||||||
|
if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
|
||||||
|
delete(w.paths, int(raw.Wd))
|
||||||
|
delete(w.watches, name)
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
if nameLen > 0 {
|
||||||
|
// Point "bytes" at the first byte of the filename
|
||||||
|
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
|
||||||
|
// The filename is padded with NULL bytes. TrimRight() gets rid of those.
|
||||||
|
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
|
||||||
|
}
|
||||||
|
|
||||||
|
event := w.newEvent(name, mask)
|
||||||
|
|
||||||
|
// Send the events that are not ignored on the events channel
|
||||||
|
if mask&unix.IN_IGNORED == 0 {
|
||||||
|
if !w.sendEvent(event) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next event in the buffer
|
||||||
|
offset += unix.SizeofInotifyEvent + nameLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEvent returns an platform-independent Event based on an inotify mask.
|
||||||
|
func (w *Watcher) newEvent(name string, mask uint32) Event {
|
||||||
|
e := Event{Name: name}
|
||||||
|
if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
|
||||||
|
e.Op |= Create
|
||||||
|
}
|
||||||
|
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
|
||||||
|
e.Op |= Remove
|
||||||
|
}
|
||||||
|
if mask&unix.IN_MODIFY == unix.IN_MODIFY {
|
||||||
|
e.Op |= Write
|
||||||
|
}
|
||||||
|
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
|
||||||
|
e.Op |= Rename
|
||||||
|
}
|
||||||
|
if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
|
||||||
|
e.Op |= Chmod
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
707
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
Normal file
707
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
Normal file
@@ -0,0 +1,707 @@
|
|||||||
|
//go:build freebsd || openbsd || netbsd || dragonfly || darwin
|
||||||
|
// +build freebsd openbsd netbsd dragonfly darwin
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher watches a set of paths, delivering events on a channel.
|
||||||
|
//
|
||||||
|
// A watcher should not be copied (e.g. pass it by pointer, rather than by
|
||||||
|
// value).
|
||||||
|
//
|
||||||
|
// # Linux notes
|
||||||
|
//
|
||||||
|
// When a file is removed a Remove event won't be emitted until all file
|
||||||
|
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||||
|
//
|
||||||
|
// fp := os.Open("file")
|
||||||
|
// os.Remove("file") // Triggers Chmod
|
||||||
|
// fp.Close() // Triggers Remove
|
||||||
|
//
|
||||||
|
// This is the event that inotify sends, so not much can be changed about this.
|
||||||
|
//
|
||||||
|
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||||
|
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||||
|
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||||
|
// create is an "instance", and every path you add is a "watch".
|
||||||
|
//
|
||||||
|
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||||
|
// /proc/sys/fs/inotify/max_user_instances
|
||||||
|
//
|
||||||
|
// To increase them you can use sysctl or write the value to the /proc file:
|
||||||
|
//
|
||||||
|
// # Default values on Linux 5.18
|
||||||
|
// sysctl fs.inotify.max_user_watches=124983
|
||||||
|
// sysctl fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||||
|
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||||
|
// your distro's documentation):
|
||||||
|
//
|
||||||
|
// fs.inotify.max_user_watches=124983
|
||||||
|
// fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||||
|
// files" error.
|
||||||
|
//
|
||||||
|
// # kqueue notes (macOS, BSD)
|
||||||
|
//
|
||||||
|
// kqueue requires opening a file descriptor for every file that's being watched;
|
||||||
|
// so if you're watching a directory with five files then that's six file
|
||||||
|
// descriptors. You will run in to your system's "max open files" limit faster on
|
||||||
|
// these platforms.
|
||||||
|
//
|
||||||
|
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
|
||||||
|
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||||
|
// systems.
|
||||||
|
//
|
||||||
|
// # macOS notes
|
||||||
|
//
|
||||||
|
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||||
|
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||||
|
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||||
|
//
|
||||||
|
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||||
|
type Watcher struct {
|
||||||
|
// Events sends the filesystem change events.
|
||||||
|
//
|
||||||
|
// fsnotify can send the following events; a "path" here can refer to a
|
||||||
|
// file, directory, symbolic link, or special file like a FIFO.
|
||||||
|
//
|
||||||
|
// fsnotify.Create A new path was created; this may be followed by one
|
||||||
|
// or more Write events if data also gets written to a
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// fsnotify.Remove A path was removed.
|
||||||
|
//
|
||||||
|
// fsnotify.Rename A path was renamed. A rename is always sent with the
|
||||||
|
// old path as Event.Name, and a Create event will be
|
||||||
|
// sent with the new name. Renames are only sent for
|
||||||
|
// paths that are currently watched; e.g. moving an
|
||||||
|
// unmonitored file into a monitored directory will
|
||||||
|
// show up as just a Create. Similarly, renaming a file
|
||||||
|
// to outside a monitored directory will show up as
|
||||||
|
// only a Rename.
|
||||||
|
//
|
||||||
|
// fsnotify.Write A file or named pipe was written to. A Truncate will
|
||||||
|
// also trigger a Write. A single "write action"
|
||||||
|
// initiated by the user may show up as one or multiple
|
||||||
|
// writes, depending on when the system syncs things to
|
||||||
|
// disk. For example when compiling a large Go program
|
||||||
|
// you may get hundreds of Write events, so you
|
||||||
|
// probably want to wait until you've stopped receiving
|
||||||
|
// them (see the dedup example in cmd/fsnotify).
|
||||||
|
//
|
||||||
|
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||||
|
// when a file is removed (or more accurately, when a
|
||||||
|
// link to an inode is removed). On kqueue it's sent
|
||||||
|
// and on kqueue when a file is truncated. On Windows
|
||||||
|
// it's never sent.
|
||||||
|
Events chan Event
|
||||||
|
|
||||||
|
// Errors sends any errors.
|
||||||
|
Errors chan error
|
||||||
|
|
||||||
|
done chan struct{}
|
||||||
|
kq int // File descriptor (as returned by the kqueue() syscall).
|
||||||
|
closepipe [2]int // Pipe used for closing.
|
||||||
|
mu sync.Mutex // Protects access to watcher data
|
||||||
|
watches map[string]int // Watched file descriptors (key: path).
|
||||||
|
watchesByDir map[string]map[int]struct{} // Watched file descriptors indexed by the parent directory (key: dirname(path)).
|
||||||
|
userWatches map[string]struct{} // Watches added with Watcher.Add()
|
||||||
|
dirFlags map[string]uint32 // Watched directories to fflags used in kqueue.
|
||||||
|
paths map[int]pathInfo // File descriptors to path names for processing kqueue events.
|
||||||
|
fileExists map[string]struct{} // Keep track of if we know this file exists (to stop duplicate create events).
|
||||||
|
isClosed bool // Set to true when Close() is first called
|
||||||
|
}
|
||||||
|
|
||||||
|
type pathInfo struct {
|
||||||
|
name string
|
||||||
|
isDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWatcher creates a new Watcher.
|
||||||
|
func NewWatcher() (*Watcher, error) {
|
||||||
|
kq, closepipe, err := newKqueue()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &Watcher{
|
||||||
|
kq: kq,
|
||||||
|
closepipe: closepipe,
|
||||||
|
watches: make(map[string]int),
|
||||||
|
watchesByDir: make(map[string]map[int]struct{}),
|
||||||
|
dirFlags: make(map[string]uint32),
|
||||||
|
paths: make(map[int]pathInfo),
|
||||||
|
fileExists: make(map[string]struct{}),
|
||||||
|
userWatches: make(map[string]struct{}),
|
||||||
|
Events: make(chan Event),
|
||||||
|
Errors: make(chan error),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
go w.readEvents()
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newKqueue creates a new kernel event queue and returns a descriptor.
|
||||||
|
//
|
||||||
|
// This registers a new event on closepipe, which will trigger an event when
|
||||||
|
// it's closed. This way we can use kevent() without timeout/polling; without
|
||||||
|
// the closepipe, it would block forever and we wouldn't be able to stop it at
|
||||||
|
// all.
|
||||||
|
func newKqueue() (kq int, closepipe [2]int, err error) {
|
||||||
|
kq, err = unix.Kqueue()
|
||||||
|
if kq == -1 {
|
||||||
|
return kq, closepipe, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the close pipe.
|
||||||
|
err = unix.Pipe(closepipe[:])
|
||||||
|
if err != nil {
|
||||||
|
unix.Close(kq)
|
||||||
|
return kq, closepipe, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register changes to listen on the closepipe.
|
||||||
|
changes := make([]unix.Kevent_t, 1)
|
||||||
|
// SetKevent converts int to the platform-specific types.
|
||||||
|
unix.SetKevent(&changes[0], closepipe[0], unix.EVFILT_READ,
|
||||||
|
unix.EV_ADD|unix.EV_ENABLE|unix.EV_ONESHOT)
|
||||||
|
|
||||||
|
ok, err := unix.Kevent(kq, changes, nil, nil)
|
||||||
|
if ok == -1 {
|
||||||
|
unix.Close(kq)
|
||||||
|
unix.Close(closepipe[0])
|
||||||
|
unix.Close(closepipe[1])
|
||||||
|
return kq, closepipe, err
|
||||||
|
}
|
||||||
|
return kq, closepipe, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the event was sent, or false if watcher is closed.
|
||||||
|
func (w *Watcher) sendEvent(e Event) bool {
|
||||||
|
select {
|
||||||
|
case w.Events <- e:
|
||||||
|
return true
|
||||||
|
case <-w.done:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the error was sent, or false if watcher is closed.
|
||||||
|
func (w *Watcher) sendError(err error) bool {
|
||||||
|
select {
|
||||||
|
case w.Errors <- err:
|
||||||
|
return true
|
||||||
|
case <-w.done:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes all watches and closes the events channel.
|
||||||
|
func (w *Watcher) Close() error {
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.isClosed {
|
||||||
|
w.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.isClosed = true
|
||||||
|
|
||||||
|
// copy paths to remove while locked
|
||||||
|
pathsToRemove := make([]string, 0, len(w.watches))
|
||||||
|
for name := range w.watches {
|
||||||
|
pathsToRemove = append(pathsToRemove, name)
|
||||||
|
}
|
||||||
|
w.mu.Unlock() // Unlock before calling Remove, which also locks
|
||||||
|
for _, name := range pathsToRemove {
|
||||||
|
w.Remove(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send "quit" message to the reader goroutine.
|
||||||
|
unix.Close(w.closepipe[1])
|
||||||
|
close(w.done)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add starts monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// A path can only be watched once; attempting to watch it more than once will
|
||||||
|
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||||
|
// added. A watch will be automatically removed if the path is deleted.
|
||||||
|
//
|
||||||
|
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||||
|
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||||
|
// re-created, or if it's moved to a different filesystem.
|
||||||
|
//
|
||||||
|
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||||
|
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||||
|
//
|
||||||
|
// # Watching directories
|
||||||
|
//
|
||||||
|
// All files in a directory are monitored, including new files that are created
|
||||||
|
// after the watcher is started. Subdirectories are not watched (i.e. it's
|
||||||
|
// non-recursive).
|
||||||
|
//
|
||||||
|
// # Watching files
|
||||||
|
//
|
||||||
|
// Watching individual files (rather than directories) is generally not
|
||||||
|
// recommended as many tools update files atomically. Instead of "just" writing
|
||||||
|
// to the file a temporary file will be written to first, and if successful the
|
||||||
|
// temporary file is moved to to destination removing the original, or some
|
||||||
|
// variant thereof. The watcher on the original file is now lost, as it no
|
||||||
|
// longer exists.
|
||||||
|
//
|
||||||
|
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||||
|
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||||
|
func (w *Watcher) Add(name string) error {
|
||||||
|
w.mu.Lock()
|
||||||
|
w.userWatches[name] = struct{}{}
|
||||||
|
w.mu.Unlock()
|
||||||
|
_, err := w.addWatch(name, noteAllEvents)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stops monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// Directories are always removed non-recursively. For example, if you added
|
||||||
|
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||||
|
//
|
||||||
|
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||||
|
func (w *Watcher) Remove(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
w.mu.Lock()
|
||||||
|
watchfd, ok := w.watches[name]
|
||||||
|
w.mu.Unlock()
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := w.register([]int{watchfd}, unix.EV_DELETE, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
unix.Close(watchfd)
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
isDir := w.paths[watchfd].isDir
|
||||||
|
delete(w.watches, name)
|
||||||
|
delete(w.userWatches, name)
|
||||||
|
|
||||||
|
parentName := filepath.Dir(name)
|
||||||
|
delete(w.watchesByDir[parentName], watchfd)
|
||||||
|
|
||||||
|
if len(w.watchesByDir[parentName]) == 0 {
|
||||||
|
delete(w.watchesByDir, parentName)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(w.paths, watchfd)
|
||||||
|
delete(w.dirFlags, name)
|
||||||
|
delete(w.fileExists, name)
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
// Find all watched paths that are in this directory that are not external.
|
||||||
|
if isDir {
|
||||||
|
var pathsToRemove []string
|
||||||
|
w.mu.Lock()
|
||||||
|
for fd := range w.watchesByDir[name] {
|
||||||
|
path := w.paths[fd]
|
||||||
|
if _, ok := w.userWatches[path.name]; !ok {
|
||||||
|
pathsToRemove = append(pathsToRemove, path.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
for _, name := range pathsToRemove {
|
||||||
|
// Since these are internal, not much sense in propagating error
|
||||||
|
// to the user, as that will just confuse them with an error about
|
||||||
|
// a path they did not explicitly watch themselves.
|
||||||
|
w.Remove(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||||
|
func (w *Watcher) WatchList() []string {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
entries := make([]string, 0, len(w.userWatches))
|
||||||
|
for pathname := range w.userWatches {
|
||||||
|
entries = append(entries, pathname)
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
|
||||||
|
const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME
|
||||||
|
|
||||||
|
// addWatch adds name to the watched file set.
|
||||||
|
// The flags are interpreted as described in kevent(2).
|
||||||
|
// Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks.
|
||||||
|
func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
|
||||||
|
var isDir bool
|
||||||
|
// Make ./name and name equivalent
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.isClosed {
|
||||||
|
w.mu.Unlock()
|
||||||
|
return "", errors.New("kevent instance already closed")
|
||||||
|
}
|
||||||
|
watchfd, alreadyWatching := w.watches[name]
|
||||||
|
// We already have a watch, but we can still override flags.
|
||||||
|
if alreadyWatching {
|
||||||
|
isDir = w.paths[watchfd].isDir
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
if !alreadyWatching {
|
||||||
|
fi, err := os.Lstat(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't watch sockets or named pipes
|
||||||
|
if (fi.Mode()&os.ModeSocket == os.ModeSocket) || (fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow Symlinks
|
||||||
|
//
|
||||||
|
// Linux can add unresolvable symlinks to the watch list without issue,
|
||||||
|
// and Windows can't do symlinks period. To maintain consistency, we
|
||||||
|
// will act like everything is fine if the link can't be resolved.
|
||||||
|
// There will simply be no file events for broken symlinks. Hence the
|
||||||
|
// returns of nil on errors.
|
||||||
|
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
name, err = filepath.EvalSymlinks(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
_, alreadyWatching = w.watches[name]
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
if alreadyWatching {
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err = os.Lstat(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry on EINTR; open() can return EINTR in practice on macOS.
|
||||||
|
// See #354, and go issues 11180 and 39237.
|
||||||
|
for {
|
||||||
|
watchfd, err = unix.Open(name, openMode, 0)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if errors.Is(err, unix.EINTR) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
isDir = fi.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := w.register([]int{watchfd}, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE, flags)
|
||||||
|
if err != nil {
|
||||||
|
unix.Close(watchfd)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !alreadyWatching {
|
||||||
|
w.mu.Lock()
|
||||||
|
parentName := filepath.Dir(name)
|
||||||
|
w.watches[name] = watchfd
|
||||||
|
|
||||||
|
watchesByDir, ok := w.watchesByDir[parentName]
|
||||||
|
if !ok {
|
||||||
|
watchesByDir = make(map[int]struct{}, 1)
|
||||||
|
w.watchesByDir[parentName] = watchesByDir
|
||||||
|
}
|
||||||
|
watchesByDir[watchfd] = struct{}{}
|
||||||
|
|
||||||
|
w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
|
||||||
|
w.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDir {
|
||||||
|
// Watch the directory if it has not been watched before,
|
||||||
|
// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
|
||||||
|
w.mu.Lock()
|
||||||
|
|
||||||
|
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
|
||||||
|
(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE)
|
||||||
|
// Store flags so this watch can be updated later
|
||||||
|
w.dirFlags[name] = flags
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
if watchDir {
|
||||||
|
if err := w.watchDirectoryFiles(name); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readEvents reads from kqueue and converts the received kevents into
|
||||||
|
// Event values that it sends down the Events channel.
|
||||||
|
func (w *Watcher) readEvents() {
|
||||||
|
defer func() {
|
||||||
|
err := unix.Close(w.kq)
|
||||||
|
if err != nil {
|
||||||
|
w.Errors <- err
|
||||||
|
}
|
||||||
|
unix.Close(w.closepipe[0])
|
||||||
|
close(w.Events)
|
||||||
|
close(w.Errors)
|
||||||
|
}()
|
||||||
|
|
||||||
|
eventBuffer := make([]unix.Kevent_t, 10)
|
||||||
|
for closed := false; !closed; {
|
||||||
|
kevents, err := w.read(eventBuffer)
|
||||||
|
// EINTR is okay, the syscall was interrupted before timeout expired.
|
||||||
|
if err != nil && err != unix.EINTR {
|
||||||
|
if !w.sendError(fmt.Errorf("fsnotify.readEvents: %w", err)) {
|
||||||
|
closed = true
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush the events we received to the Events channel
|
||||||
|
for _, kevent := range kevents {
|
||||||
|
var (
|
||||||
|
watchfd = int(kevent.Ident)
|
||||||
|
mask = uint32(kevent.Fflags)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Shut down the loop when the pipe is closed, but only after all
|
||||||
|
// other events have been processed.
|
||||||
|
if watchfd == w.closepipe[0] {
|
||||||
|
closed = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
path := w.paths[watchfd]
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
event := w.newEvent(path.name, mask)
|
||||||
|
|
||||||
|
if path.isDir && !event.Has(Remove) {
|
||||||
|
// Double check to make sure the directory exists. This can
|
||||||
|
// happen when we do a rm -fr on a recursively watched folders
|
||||||
|
// and we receive a modification event first but the folder has
|
||||||
|
// been deleted and later receive the delete event.
|
||||||
|
if _, err := os.Lstat(event.Name); os.IsNotExist(err) {
|
||||||
|
event.Op |= Remove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.Has(Rename) || event.Has(Remove) {
|
||||||
|
w.Remove(event.Name)
|
||||||
|
w.mu.Lock()
|
||||||
|
delete(w.fileExists, event.Name)
|
||||||
|
w.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.isDir && event.Has(Write) && !event.Has(Remove) {
|
||||||
|
w.sendDirectoryChangeEvents(event.Name)
|
||||||
|
} else {
|
||||||
|
if !w.sendEvent(event) {
|
||||||
|
closed = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.Has(Remove) {
|
||||||
|
// Look for a file that may have overwritten this.
|
||||||
|
// For example, mv f1 f2 will delete f2, then create f2.
|
||||||
|
if path.isDir {
|
||||||
|
fileDir := filepath.Clean(event.Name)
|
||||||
|
w.mu.Lock()
|
||||||
|
_, found := w.watches[fileDir]
|
||||||
|
w.mu.Unlock()
|
||||||
|
if found {
|
||||||
|
// make sure the directory exists before we watch for changes. When we
|
||||||
|
// do a recursive watch and perform rm -fr, the parent directory might
|
||||||
|
// have gone missing, ignore the missing directory and let the
|
||||||
|
// upcoming delete event remove the watch from the parent directory.
|
||||||
|
if _, err := os.Lstat(fileDir); err == nil {
|
||||||
|
w.sendDirectoryChangeEvents(fileDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filePath := filepath.Clean(event.Name)
|
||||||
|
if fileInfo, err := os.Lstat(filePath); err == nil {
|
||||||
|
w.sendFileCreatedEventIfNew(filePath, fileInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEvent returns an platform-independent Event based on kqueue Fflags.
|
||||||
|
func (w *Watcher) newEvent(name string, mask uint32) Event {
|
||||||
|
e := Event{Name: name}
|
||||||
|
if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
|
||||||
|
e.Op |= Remove
|
||||||
|
}
|
||||||
|
if mask&unix.NOTE_WRITE == unix.NOTE_WRITE {
|
||||||
|
e.Op |= Write
|
||||||
|
}
|
||||||
|
if mask&unix.NOTE_RENAME == unix.NOTE_RENAME {
|
||||||
|
e.Op |= Rename
|
||||||
|
}
|
||||||
|
if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB {
|
||||||
|
e.Op |= Chmod
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// watchDirectoryFiles to mimic inotify when adding a watch on a directory
|
||||||
|
func (w *Watcher) watchDirectoryFiles(dirPath string) error {
|
||||||
|
// Get all files
|
||||||
|
files, err := ioutil.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fileInfo := range files {
|
||||||
|
path := filepath.Join(dirPath, fileInfo.Name())
|
||||||
|
|
||||||
|
cleanPath, err := w.internalWatch(path, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
// No permission to read the file; that's not a problem: just skip.
|
||||||
|
// But do add it to w.fileExists to prevent it from being picked up
|
||||||
|
// as a "new" file later (it still shows up in the directory
|
||||||
|
// listing).
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM):
|
||||||
|
cleanPath = filepath.Clean(path)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
w.fileExists[cleanPath] = struct{}{}
|
||||||
|
w.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search the directory for new files and send an event for them.
|
||||||
|
//
|
||||||
|
// This functionality is to have the BSD watcher match the inotify, which sends
|
||||||
|
// a create event for files created in a watched directory.
|
||||||
|
func (w *Watcher) sendDirectoryChangeEvents(dir string) {
|
||||||
|
// Get all files
|
||||||
|
files, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for new files
|
||||||
|
for _, fi := range files {
|
||||||
|
err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
|
||||||
|
func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) {
|
||||||
|
w.mu.Lock()
|
||||||
|
_, doesExist := w.fileExists[filePath]
|
||||||
|
w.mu.Unlock()
|
||||||
|
if !doesExist {
|
||||||
|
if !w.sendEvent(Event{Name: filePath, Op: Create}) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// like watchDirectoryFiles (but without doing another ReadDir)
|
||||||
|
filePath, err = w.internalWatch(filePath, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
w.fileExists[filePath] = struct{}{}
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) {
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
// mimic Linux providing delete events for subdirectories
|
||||||
|
// but preserve the flags used if currently watching subdirectory
|
||||||
|
w.mu.Lock()
|
||||||
|
flags := w.dirFlags[name]
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
flags |= unix.NOTE_DELETE | unix.NOTE_RENAME
|
||||||
|
return w.addWatch(name, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch file to mimic Linux inotify
|
||||||
|
return w.addWatch(name, noteAllEvents)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register events with the queue.
|
||||||
|
func (w *Watcher) register(fds []int, flags int, fflags uint32) error {
|
||||||
|
changes := make([]unix.Kevent_t, len(fds))
|
||||||
|
for i, fd := range fds {
|
||||||
|
// SetKevent converts int to the platform-specific types.
|
||||||
|
unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags)
|
||||||
|
changes[i].Fflags = fflags
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the events.
|
||||||
|
success, err := unix.Kevent(w.kq, changes, nil, nil)
|
||||||
|
if success == -1 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// read retrieves pending events, or waits until an event occurs.
|
||||||
|
func (w *Watcher) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) {
|
||||||
|
n, err := unix.Kevent(w.kq, nil, events, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return events[0:n], nil
|
||||||
|
}
|
66
vendor/github.com/fsnotify/fsnotify/backend_other.go
generated
vendored
Normal file
66
vendor/github.com/fsnotify/fsnotify/backend_other.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows
|
||||||
|
// +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher watches a set of files, delivering events to a channel.
|
||||||
|
type Watcher struct{}
|
||||||
|
|
||||||
|
// NewWatcher creates a new Watcher.
|
||||||
|
func NewWatcher() (*Watcher, error) {
|
||||||
|
return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes all watches and closes the events channel.
|
||||||
|
func (w *Watcher) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add starts monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// A path can only be watched once; attempting to watch it more than once will
|
||||||
|
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||||
|
// added. A watch will be automatically removed if the path is deleted.
|
||||||
|
//
|
||||||
|
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||||
|
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||||
|
// re-created, or if it's moved to a different filesystem.
|
||||||
|
//
|
||||||
|
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||||
|
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||||
|
//
|
||||||
|
// # Watching directories
|
||||||
|
//
|
||||||
|
// All files in a directory are monitored, including new files that are created
|
||||||
|
// after the watcher is started. Subdirectories are not watched (i.e. it's
|
||||||
|
// non-recursive).
|
||||||
|
//
|
||||||
|
// # Watching files
|
||||||
|
//
|
||||||
|
// Watching individual files (rather than directories) is generally not
|
||||||
|
// recommended as many tools update files atomically. Instead of "just" writing
|
||||||
|
// to the file a temporary file will be written to first, and if successful the
|
||||||
|
// temporary file is moved to to destination removing the original, or some
|
||||||
|
// variant thereof. The watcher on the original file is now lost, as it no
|
||||||
|
// longer exists.
|
||||||
|
//
|
||||||
|
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||||
|
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||||
|
func (w *Watcher) Add(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stops monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// Directories are always removed non-recursively. For example, if you added
|
||||||
|
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||||
|
//
|
||||||
|
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||||
|
func (w *Watcher) Remove(name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
746
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
Normal file
746
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
Normal file
@@ -0,0 +1,746 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher watches a set of paths, delivering events on a channel.
|
||||||
|
//
|
||||||
|
// A watcher should not be copied (e.g. pass it by pointer, rather than by
|
||||||
|
// value).
|
||||||
|
//
|
||||||
|
// # Linux notes
|
||||||
|
//
|
||||||
|
// When a file is removed a Remove event won't be emitted until all file
|
||||||
|
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||||
|
//
|
||||||
|
// fp := os.Open("file")
|
||||||
|
// os.Remove("file") // Triggers Chmod
|
||||||
|
// fp.Close() // Triggers Remove
|
||||||
|
//
|
||||||
|
// This is the event that inotify sends, so not much can be changed about this.
|
||||||
|
//
|
||||||
|
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||||
|
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||||
|
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||||
|
// create is an "instance", and every path you add is a "watch".
|
||||||
|
//
|
||||||
|
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||||
|
// /proc/sys/fs/inotify/max_user_instances
|
||||||
|
//
|
||||||
|
// To increase them you can use sysctl or write the value to the /proc file:
|
||||||
|
//
|
||||||
|
// # Default values on Linux 5.18
|
||||||
|
// sysctl fs.inotify.max_user_watches=124983
|
||||||
|
// sysctl fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||||
|
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||||
|
// your distro's documentation):
|
||||||
|
//
|
||||||
|
// fs.inotify.max_user_watches=124983
|
||||||
|
// fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||||
|
// files" error.
|
||||||
|
//
|
||||||
|
// # kqueue notes (macOS, BSD)
|
||||||
|
//
|
||||||
|
// kqueue requires opening a file descriptor for every file that's being watched;
|
||||||
|
// so if you're watching a directory with five files then that's six file
|
||||||
|
// descriptors. You will run in to your system's "max open files" limit faster on
|
||||||
|
// these platforms.
|
||||||
|
//
|
||||||
|
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
|
||||||
|
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||||
|
// systems.
|
||||||
|
//
|
||||||
|
// # macOS notes
|
||||||
|
//
|
||||||
|
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||||
|
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||||
|
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||||
|
//
|
||||||
|
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||||
|
type Watcher struct {
|
||||||
|
// Events sends the filesystem change events.
|
||||||
|
//
|
||||||
|
// fsnotify can send the following events; a "path" here can refer to a
|
||||||
|
// file, directory, symbolic link, or special file like a FIFO.
|
||||||
|
//
|
||||||
|
// fsnotify.Create A new path was created; this may be followed by one
|
||||||
|
// or more Write events if data also gets written to a
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// fsnotify.Remove A path was removed.
|
||||||
|
//
|
||||||
|
// fsnotify.Rename A path was renamed. A rename is always sent with the
|
||||||
|
// old path as Event.Name, and a Create event will be
|
||||||
|
// sent with the new name. Renames are only sent for
|
||||||
|
// paths that are currently watched; e.g. moving an
|
||||||
|
// unmonitored file into a monitored directory will
|
||||||
|
// show up as just a Create. Similarly, renaming a file
|
||||||
|
// to outside a monitored directory will show up as
|
||||||
|
// only a Rename.
|
||||||
|
//
|
||||||
|
// fsnotify.Write A file or named pipe was written to. A Truncate will
|
||||||
|
// also trigger a Write. A single "write action"
|
||||||
|
// initiated by the user may show up as one or multiple
|
||||||
|
// writes, depending on when the system syncs things to
|
||||||
|
// disk. For example when compiling a large Go program
|
||||||
|
// you may get hundreds of Write events, so you
|
||||||
|
// probably want to wait until you've stopped receiving
|
||||||
|
// them (see the dedup example in cmd/fsnotify).
|
||||||
|
//
|
||||||
|
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||||
|
// when a file is removed (or more accurately, when a
|
||||||
|
// link to an inode is removed). On kqueue it's sent
|
||||||
|
// and on kqueue when a file is truncated. On Windows
|
||||||
|
// it's never sent.
|
||||||
|
Events chan Event
|
||||||
|
|
||||||
|
// Errors sends any errors.
|
||||||
|
Errors chan error
|
||||||
|
|
||||||
|
port windows.Handle // Handle to completion port
|
||||||
|
input chan *input // Inputs to the reader are sent on this channel
|
||||||
|
quit chan chan<- error
|
||||||
|
|
||||||
|
mu sync.Mutex // Protects access to watches, isClosed
|
||||||
|
watches watchMap // Map of watches (key: i-number)
|
||||||
|
isClosed bool // Set to true when Close() is first called
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWatcher creates a new Watcher.
|
||||||
|
func NewWatcher() (*Watcher, error) {
|
||||||
|
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, os.NewSyscallError("CreateIoCompletionPort", err)
|
||||||
|
}
|
||||||
|
w := &Watcher{
|
||||||
|
port: port,
|
||||||
|
watches: make(watchMap),
|
||||||
|
input: make(chan *input, 1),
|
||||||
|
Events: make(chan Event, 50),
|
||||||
|
Errors: make(chan error),
|
||||||
|
quit: make(chan chan<- error, 1),
|
||||||
|
}
|
||||||
|
go w.readEvents()
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) sendEvent(name string, mask uint64) bool {
|
||||||
|
if mask == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
event := w.newEvent(name, uint32(mask))
|
||||||
|
select {
|
||||||
|
case ch := <-w.quit:
|
||||||
|
w.quit <- ch
|
||||||
|
case w.Events <- event:
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the error was sent, or false if watcher is closed.
|
||||||
|
func (w *Watcher) sendError(err error) bool {
|
||||||
|
select {
|
||||||
|
case w.Errors <- err:
|
||||||
|
return true
|
||||||
|
case <-w.quit:
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes all watches and closes the events channel.
|
||||||
|
func (w *Watcher) Close() error {
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.isClosed {
|
||||||
|
w.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w.isClosed = true
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
// Send "quit" message to the reader goroutine
|
||||||
|
ch := make(chan error)
|
||||||
|
w.quit <- ch
|
||||||
|
if err := w.wakeupReader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return <-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add starts monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// A path can only be watched once; attempting to watch it more than once will
|
||||||
|
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||||
|
// added. A watch will be automatically removed if the path is deleted.
|
||||||
|
//
|
||||||
|
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||||
|
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||||
|
// re-created, or if it's moved to a different filesystem.
|
||||||
|
//
|
||||||
|
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||||
|
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||||
|
//
|
||||||
|
// # Watching directories
|
||||||
|
//
|
||||||
|
// All files in a directory are monitored, including new files that are created
|
||||||
|
// after the watcher is started. Subdirectories are not watched (i.e. it's
|
||||||
|
// non-recursive).
|
||||||
|
//
|
||||||
|
// # Watching files
|
||||||
|
//
|
||||||
|
// Watching individual files (rather than directories) is generally not
|
||||||
|
// recommended as many tools update files atomically. Instead of "just" writing
|
||||||
|
// to the file a temporary file will be written to first, and if successful the
|
||||||
|
// temporary file is moved to to destination removing the original, or some
|
||||||
|
// variant thereof. The watcher on the original file is now lost, as it no
|
||||||
|
// longer exists.
|
||||||
|
//
|
||||||
|
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||||
|
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||||
|
func (w *Watcher) Add(name string) error {
|
||||||
|
w.mu.Lock()
|
||||||
|
if w.isClosed {
|
||||||
|
w.mu.Unlock()
|
||||||
|
return errors.New("watcher already closed")
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
in := &input{
|
||||||
|
op: opAddWatch,
|
||||||
|
path: filepath.Clean(name),
|
||||||
|
flags: sysFSALLEVENTS,
|
||||||
|
reply: make(chan error),
|
||||||
|
}
|
||||||
|
w.input <- in
|
||||||
|
if err := w.wakeupReader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return <-in.reply
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stops monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// Directories are always removed non-recursively. For example, if you added
|
||||||
|
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||||
|
//
|
||||||
|
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||||
|
func (w *Watcher) Remove(name string) error {
|
||||||
|
in := &input{
|
||||||
|
op: opRemoveWatch,
|
||||||
|
path: filepath.Clean(name),
|
||||||
|
reply: make(chan error),
|
||||||
|
}
|
||||||
|
w.input <- in
|
||||||
|
if err := w.wakeupReader(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return <-in.reply
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||||
|
func (w *Watcher) WatchList() []string {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
entries := make([]string, 0, len(w.watches))
|
||||||
|
for _, entry := range w.watches {
|
||||||
|
for _, watchEntry := range entry {
|
||||||
|
entries = append(entries, watchEntry.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
// These options are from the old golang.org/x/exp/winfsnotify, where you could
|
||||||
|
// add various options to the watch. This has long since been removed.
|
||||||
|
//
|
||||||
|
// The "sys" in the name is misleading as they're not part of any "system".
|
||||||
|
//
|
||||||
|
// This should all be removed at some point, and just use windows.FILE_NOTIFY_*
|
||||||
|
const (
|
||||||
|
sysFSALLEVENTS = 0xfff
|
||||||
|
sysFSATTRIB = 0x4
|
||||||
|
sysFSCREATE = 0x100
|
||||||
|
sysFSDELETE = 0x200
|
||||||
|
sysFSDELETESELF = 0x400
|
||||||
|
sysFSMODIFY = 0x2
|
||||||
|
sysFSMOVE = 0xc0
|
||||||
|
sysFSMOVEDFROM = 0x40
|
||||||
|
sysFSMOVEDTO = 0x80
|
||||||
|
sysFSMOVESELF = 0x800
|
||||||
|
sysFSIGNORED = 0x8000
|
||||||
|
)
|
||||||
|
|
||||||
|
func (w *Watcher) newEvent(name string, mask uint32) Event {
|
||||||
|
e := Event{Name: name}
|
||||||
|
if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
|
||||||
|
e.Op |= Create
|
||||||
|
}
|
||||||
|
if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF {
|
||||||
|
e.Op |= Remove
|
||||||
|
}
|
||||||
|
if mask&sysFSMODIFY == sysFSMODIFY {
|
||||||
|
e.Op |= Write
|
||||||
|
}
|
||||||
|
if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM {
|
||||||
|
e.Op |= Rename
|
||||||
|
}
|
||||||
|
if mask&sysFSATTRIB == sysFSATTRIB {
|
||||||
|
e.Op |= Chmod
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
opAddWatch = iota
|
||||||
|
opRemoveWatch
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
provisional uint64 = 1 << (32 + iota)
|
||||||
|
)
|
||||||
|
|
||||||
|
type input struct {
|
||||||
|
op int
|
||||||
|
path string
|
||||||
|
flags uint32
|
||||||
|
reply chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
type inode struct {
|
||||||
|
handle windows.Handle
|
||||||
|
volume uint32
|
||||||
|
index uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type watch struct {
|
||||||
|
ov windows.Overlapped
|
||||||
|
ino *inode // i-number
|
||||||
|
path string // Directory path
|
||||||
|
mask uint64 // Directory itself is being watched with these notify flags
|
||||||
|
names map[string]uint64 // Map of names being watched and their notify flags
|
||||||
|
rename string // Remembers the old name while renaming a file
|
||||||
|
buf [65536]byte // 64K buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
indexMap map[uint64]*watch
|
||||||
|
watchMap map[uint32]indexMap
|
||||||
|
)
|
||||||
|
|
||||||
|
func (w *Watcher) wakeupReader() error {
|
||||||
|
err := windows.PostQueuedCompletionStatus(w.port, 0, 0, nil)
|
||||||
|
if err != nil {
|
||||||
|
return os.NewSyscallError("PostQueuedCompletionStatus", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) getDir(pathname string) (dir string, err error) {
|
||||||
|
attr, err := windows.GetFileAttributes(windows.StringToUTF16Ptr(pathname))
|
||||||
|
if err != nil {
|
||||||
|
return "", os.NewSyscallError("GetFileAttributes", err)
|
||||||
|
}
|
||||||
|
if attr&windows.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||||
|
dir = pathname
|
||||||
|
} else {
|
||||||
|
dir, _ = filepath.Split(pathname)
|
||||||
|
dir = filepath.Clean(dir)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) getIno(path string) (ino *inode, err error) {
|
||||||
|
h, err := windows.CreateFile(windows.StringToUTF16Ptr(path),
|
||||||
|
windows.FILE_LIST_DIRECTORY,
|
||||||
|
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
|
||||||
|
nil, windows.OPEN_EXISTING,
|
||||||
|
windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OVERLAPPED, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, os.NewSyscallError("CreateFile", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fi windows.ByHandleFileInformation
|
||||||
|
err = windows.GetFileInformationByHandle(h, &fi)
|
||||||
|
if err != nil {
|
||||||
|
windows.CloseHandle(h)
|
||||||
|
return nil, os.NewSyscallError("GetFileInformationByHandle", err)
|
||||||
|
}
|
||||||
|
ino = &inode{
|
||||||
|
handle: h,
|
||||||
|
volume: fi.VolumeSerialNumber,
|
||||||
|
index: uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),
|
||||||
|
}
|
||||||
|
return ino, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must run within the I/O thread.
|
||||||
|
func (m watchMap) get(ino *inode) *watch {
|
||||||
|
if i := m[ino.volume]; i != nil {
|
||||||
|
return i[ino.index]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must run within the I/O thread.
|
||||||
|
func (m watchMap) set(ino *inode, watch *watch) {
|
||||||
|
i := m[ino.volume]
|
||||||
|
if i == nil {
|
||||||
|
i = make(indexMap)
|
||||||
|
m[ino.volume] = i
|
||||||
|
}
|
||||||
|
i[ino.index] = watch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must run within the I/O thread.
|
||||||
|
func (w *Watcher) addWatch(pathname string, flags uint64) error {
|
||||||
|
dir, err := w.getDir(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ino, err := w.getIno(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.mu.Lock()
|
||||||
|
watchEntry := w.watches.get(ino)
|
||||||
|
w.mu.Unlock()
|
||||||
|
if watchEntry == nil {
|
||||||
|
_, err := windows.CreateIoCompletionPort(ino.handle, w.port, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
windows.CloseHandle(ino.handle)
|
||||||
|
return os.NewSyscallError("CreateIoCompletionPort", err)
|
||||||
|
}
|
||||||
|
watchEntry = &watch{
|
||||||
|
ino: ino,
|
||||||
|
path: dir,
|
||||||
|
names: make(map[string]uint64),
|
||||||
|
}
|
||||||
|
w.mu.Lock()
|
||||||
|
w.watches.set(ino, watchEntry)
|
||||||
|
w.mu.Unlock()
|
||||||
|
flags |= provisional
|
||||||
|
} else {
|
||||||
|
windows.CloseHandle(ino.handle)
|
||||||
|
}
|
||||||
|
if pathname == dir {
|
||||||
|
watchEntry.mask |= flags
|
||||||
|
} else {
|
||||||
|
watchEntry.names[filepath.Base(pathname)] |= flags
|
||||||
|
}
|
||||||
|
|
||||||
|
err = w.startRead(watchEntry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pathname == dir {
|
||||||
|
watchEntry.mask &= ^provisional
|
||||||
|
} else {
|
||||||
|
watchEntry.names[filepath.Base(pathname)] &= ^provisional
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must run within the I/O thread.
|
||||||
|
func (w *Watcher) remWatch(pathname string) error {
|
||||||
|
dir, err := w.getDir(pathname)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ino, err := w.getIno(dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.mu.Lock()
|
||||||
|
watch := w.watches.get(ino)
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
err = windows.CloseHandle(ino.handle)
|
||||||
|
if err != nil {
|
||||||
|
w.sendError(os.NewSyscallError("CloseHandle", err))
|
||||||
|
}
|
||||||
|
if watch == nil {
|
||||||
|
return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname)
|
||||||
|
}
|
||||||
|
if pathname == dir {
|
||||||
|
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
|
||||||
|
watch.mask = 0
|
||||||
|
} else {
|
||||||
|
name := filepath.Base(pathname)
|
||||||
|
w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED)
|
||||||
|
delete(watch.names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.startRead(watch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must run within the I/O thread.
|
||||||
|
func (w *Watcher) deleteWatch(watch *watch) {
|
||||||
|
for name, mask := range watch.names {
|
||||||
|
if mask&provisional == 0 {
|
||||||
|
w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
|
||||||
|
}
|
||||||
|
delete(watch.names, name)
|
||||||
|
}
|
||||||
|
if watch.mask != 0 {
|
||||||
|
if watch.mask&provisional == 0 {
|
||||||
|
w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
|
||||||
|
}
|
||||||
|
watch.mask = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must run within the I/O thread.
|
||||||
|
func (w *Watcher) startRead(watch *watch) error {
|
||||||
|
err := windows.CancelIo(watch.ino.handle)
|
||||||
|
if err != nil {
|
||||||
|
w.sendError(os.NewSyscallError("CancelIo", err))
|
||||||
|
w.deleteWatch(watch)
|
||||||
|
}
|
||||||
|
mask := w.toWindowsFlags(watch.mask)
|
||||||
|
for _, m := range watch.names {
|
||||||
|
mask |= w.toWindowsFlags(m)
|
||||||
|
}
|
||||||
|
if mask == 0 {
|
||||||
|
err := windows.CloseHandle(watch.ino.handle)
|
||||||
|
if err != nil {
|
||||||
|
w.sendError(os.NewSyscallError("CloseHandle", err))
|
||||||
|
}
|
||||||
|
w.mu.Lock()
|
||||||
|
delete(w.watches[watch.ino.volume], watch.ino.index)
|
||||||
|
w.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],
|
||||||
|
uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)
|
||||||
|
if rdErr != nil {
|
||||||
|
err := os.NewSyscallError("ReadDirectoryChanges", rdErr)
|
||||||
|
if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
|
||||||
|
// Watched directory was probably removed
|
||||||
|
w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
w.deleteWatch(watch)
|
||||||
|
w.startRead(watch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readEvents reads from the I/O completion port, converts the
|
||||||
|
// received events into Event objects and sends them via the Events channel.
|
||||||
|
// Entry point to the I/O thread.
|
||||||
|
func (w *Watcher) readEvents() {
|
||||||
|
var (
|
||||||
|
n uint32
|
||||||
|
key uintptr
|
||||||
|
ov *windows.Overlapped
|
||||||
|
)
|
||||||
|
runtime.LockOSThread()
|
||||||
|
|
||||||
|
for {
|
||||||
|
qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE)
|
||||||
|
// This error is handled after the watch == nil check below. NOTE: this
|
||||||
|
// seems odd, note sure if it's correct.
|
||||||
|
|
||||||
|
watch := (*watch)(unsafe.Pointer(ov))
|
||||||
|
if watch == nil {
|
||||||
|
select {
|
||||||
|
case ch := <-w.quit:
|
||||||
|
w.mu.Lock()
|
||||||
|
var indexes []indexMap
|
||||||
|
for _, index := range w.watches {
|
||||||
|
indexes = append(indexes, index)
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
for _, index := range indexes {
|
||||||
|
for _, watch := range index {
|
||||||
|
w.deleteWatch(watch)
|
||||||
|
w.startRead(watch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := windows.CloseHandle(w.port)
|
||||||
|
if err != nil {
|
||||||
|
err = os.NewSyscallError("CloseHandle", err)
|
||||||
|
}
|
||||||
|
close(w.Events)
|
||||||
|
close(w.Errors)
|
||||||
|
ch <- err
|
||||||
|
return
|
||||||
|
case in := <-w.input:
|
||||||
|
switch in.op {
|
||||||
|
case opAddWatch:
|
||||||
|
in.reply <- w.addWatch(in.path, uint64(in.flags))
|
||||||
|
case opRemoveWatch:
|
||||||
|
in.reply <- w.remWatch(in.path)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch qErr {
|
||||||
|
case windows.ERROR_MORE_DATA:
|
||||||
|
if watch == nil {
|
||||||
|
w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer"))
|
||||||
|
} else {
|
||||||
|
// The i/o succeeded but the buffer is full.
|
||||||
|
// In theory we should be building up a full packet.
|
||||||
|
// In practice we can get away with just carrying on.
|
||||||
|
n = uint32(unsafe.Sizeof(watch.buf))
|
||||||
|
}
|
||||||
|
case windows.ERROR_ACCESS_DENIED:
|
||||||
|
// Watched directory was probably removed
|
||||||
|
w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
|
||||||
|
w.deleteWatch(watch)
|
||||||
|
w.startRead(watch)
|
||||||
|
continue
|
||||||
|
case windows.ERROR_OPERATION_ABORTED:
|
||||||
|
// CancelIo was called on this handle
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr))
|
||||||
|
continue
|
||||||
|
case nil:
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset uint32
|
||||||
|
for {
|
||||||
|
if n == 0 {
|
||||||
|
w.sendError(errors.New("short read in readEvents()"))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point "raw" to the event in the buffer
|
||||||
|
raw := (*windows.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
|
||||||
|
|
||||||
|
// Create a buf that is the size of the path name
|
||||||
|
size := int(raw.FileNameLength / 2)
|
||||||
|
var buf []uint16
|
||||||
|
// TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973
|
||||||
|
sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf))
|
||||||
|
sh.Data = uintptr(unsafe.Pointer(&raw.FileName))
|
||||||
|
sh.Len = size
|
||||||
|
sh.Cap = size
|
||||||
|
name := windows.UTF16ToString(buf)
|
||||||
|
fullname := filepath.Join(watch.path, name)
|
||||||
|
|
||||||
|
var mask uint64
|
||||||
|
switch raw.Action {
|
||||||
|
case windows.FILE_ACTION_REMOVED:
|
||||||
|
mask = sysFSDELETESELF
|
||||||
|
case windows.FILE_ACTION_MODIFIED:
|
||||||
|
mask = sysFSMODIFY
|
||||||
|
case windows.FILE_ACTION_RENAMED_OLD_NAME:
|
||||||
|
watch.rename = name
|
||||||
|
case windows.FILE_ACTION_RENAMED_NEW_NAME:
|
||||||
|
// Update saved path of all sub-watches.
|
||||||
|
old := filepath.Join(watch.path, watch.rename)
|
||||||
|
w.mu.Lock()
|
||||||
|
for _, watchMap := range w.watches {
|
||||||
|
for _, ww := range watchMap {
|
||||||
|
if strings.HasPrefix(ww.path, old) {
|
||||||
|
ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.mu.Unlock()
|
||||||
|
|
||||||
|
if watch.names[watch.rename] != 0 {
|
||||||
|
watch.names[name] |= watch.names[watch.rename]
|
||||||
|
delete(watch.names, watch.rename)
|
||||||
|
mask = sysFSMOVESELF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendNameEvent := func() {
|
||||||
|
w.sendEvent(fullname, watch.names[name]&mask)
|
||||||
|
}
|
||||||
|
if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME {
|
||||||
|
sendNameEvent()
|
||||||
|
}
|
||||||
|
if raw.Action == windows.FILE_ACTION_REMOVED {
|
||||||
|
w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
|
||||||
|
delete(watch.names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.sendEvent(fullname, watch.mask&w.toFSnotifyFlags(raw.Action))
|
||||||
|
if raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME {
|
||||||
|
fullname = filepath.Join(watch.path, watch.rename)
|
||||||
|
sendNameEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next event in the buffer
|
||||||
|
if raw.NextEntryOffset == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
offset += raw.NextEntryOffset
|
||||||
|
|
||||||
|
// Error!
|
||||||
|
if offset >= n {
|
||||||
|
w.sendError(errors.New(
|
||||||
|
"Windows system assumed buffer larger than it is, events have likely been missed."))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := w.startRead(watch); err != nil {
|
||||||
|
w.sendError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
|
||||||
|
var m uint32
|
||||||
|
if mask&sysFSMODIFY != 0 {
|
||||||
|
m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
|
||||||
|
}
|
||||||
|
if mask&sysFSATTRIB != 0 {
|
||||||
|
m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES
|
||||||
|
}
|
||||||
|
if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 {
|
||||||
|
m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Watcher) toFSnotifyFlags(action uint32) uint64 {
|
||||||
|
switch action {
|
||||||
|
case windows.FILE_ACTION_ADDED:
|
||||||
|
return sysFSCREATE
|
||||||
|
case windows.FILE_ACTION_REMOVED:
|
||||||
|
return sysFSDELETE
|
||||||
|
case windows.FILE_ACTION_MODIFIED:
|
||||||
|
return sysFSMODIFY
|
||||||
|
case windows.FILE_ACTION_RENAMED_OLD_NAME:
|
||||||
|
return sysFSMOVEDFROM
|
||||||
|
case windows.FILE_ACTION_RENAMED_NEW_NAME:
|
||||||
|
return sysFSMOVEDTO
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
81
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
Normal file
81
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
//go:build !plan9
|
||||||
|
// +build !plan9
|
||||||
|
|
||||||
|
// Package fsnotify provides a cross-platform interface for file system
|
||||||
|
// notifications.
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event represents a file system notification.
|
||||||
|
type Event struct {
|
||||||
|
// Path to the file or directory.
|
||||||
|
//
|
||||||
|
// Paths are relative to the input; for example with Add("dir") the Name
|
||||||
|
// will be set to "dir/file" if you create that file, but if you use
|
||||||
|
// Add("/path/to/dir") it will be "/path/to/dir/file".
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// File operation that triggered the event.
|
||||||
|
//
|
||||||
|
// This is a bitmask and some systems may send multiple operations at once.
|
||||||
|
// Use the Event.Has() method instead of comparing with ==.
|
||||||
|
Op Op
|
||||||
|
}
|
||||||
|
|
||||||
|
// Op describes a set of file operations.
|
||||||
|
type Op uint32
|
||||||
|
|
||||||
|
// The operations fsnotify can trigger; see the documentation on [Watcher] for a
|
||||||
|
// full description, and check them with [Event.Has].
|
||||||
|
const (
|
||||||
|
Create Op = 1 << iota
|
||||||
|
Write
|
||||||
|
Remove
|
||||||
|
Rename
|
||||||
|
Chmod
|
||||||
|
)
|
||||||
|
|
||||||
|
// Common errors that can be reported by a watcher
|
||||||
|
var (
|
||||||
|
ErrNonExistentWatch = errors.New("can't remove non-existent watcher")
|
||||||
|
ErrEventOverflow = errors.New("fsnotify queue overflow")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (op Op) String() string {
|
||||||
|
var b strings.Builder
|
||||||
|
if op.Has(Create) {
|
||||||
|
b.WriteString("|CREATE")
|
||||||
|
}
|
||||||
|
if op.Has(Remove) {
|
||||||
|
b.WriteString("|REMOVE")
|
||||||
|
}
|
||||||
|
if op.Has(Write) {
|
||||||
|
b.WriteString("|WRITE")
|
||||||
|
}
|
||||||
|
if op.Has(Rename) {
|
||||||
|
b.WriteString("|RENAME")
|
||||||
|
}
|
||||||
|
if op.Has(Chmod) {
|
||||||
|
b.WriteString("|CHMOD")
|
||||||
|
}
|
||||||
|
if b.Len() == 0 {
|
||||||
|
return "[no events]"
|
||||||
|
}
|
||||||
|
return b.String()[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has reports if this operation has the given operation.
|
||||||
|
func (o Op) Has(h Op) bool { return o&h == h }
|
||||||
|
|
||||||
|
// Has reports if this event has the given operation.
|
||||||
|
func (e Event) Has(op Op) bool { return e.Op.Has(op) }
|
||||||
|
|
||||||
|
// String returns a string representation of the event with their path.
|
||||||
|
func (e Event) String() string {
|
||||||
|
return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
|
||||||
|
}
|
208
vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
generated
vendored
Normal file
208
vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
[ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1
|
||||||
|
setopt err_exit no_unset pipefail extended_glob
|
||||||
|
|
||||||
|
# Simple script to update the godoc comments on all watchers. Probably took me
|
||||||
|
# more time to write this than doing it manually, but ah well 🙃
|
||||||
|
|
||||||
|
watcher=$(<<EOF
|
||||||
|
// Watcher watches a set of paths, delivering events on a channel.
|
||||||
|
//
|
||||||
|
// A watcher should not be copied (e.g. pass it by pointer, rather than by
|
||||||
|
// value).
|
||||||
|
//
|
||||||
|
// # Linux notes
|
||||||
|
//
|
||||||
|
// When a file is removed a Remove event won't be emitted until all file
|
||||||
|
// descriptors are closed, and deletes will always emit a Chmod. For example:
|
||||||
|
//
|
||||||
|
// fp := os.Open("file")
|
||||||
|
// os.Remove("file") // Triggers Chmod
|
||||||
|
// fp.Close() // Triggers Remove
|
||||||
|
//
|
||||||
|
// This is the event that inotify sends, so not much can be changed about this.
|
||||||
|
//
|
||||||
|
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
|
||||||
|
// for the number of watches per user, and fs.inotify.max_user_instances
|
||||||
|
// specifies the maximum number of inotify instances per user. Every Watcher you
|
||||||
|
// create is an "instance", and every path you add is a "watch".
|
||||||
|
//
|
||||||
|
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
|
||||||
|
// /proc/sys/fs/inotify/max_user_instances
|
||||||
|
//
|
||||||
|
// To increase them you can use sysctl or write the value to the /proc file:
|
||||||
|
//
|
||||||
|
// # Default values on Linux 5.18
|
||||||
|
// sysctl fs.inotify.max_user_watches=124983
|
||||||
|
// sysctl fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// To make the changes persist on reboot edit /etc/sysctl.conf or
|
||||||
|
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
|
||||||
|
// your distro's documentation):
|
||||||
|
//
|
||||||
|
// fs.inotify.max_user_watches=124983
|
||||||
|
// fs.inotify.max_user_instances=128
|
||||||
|
//
|
||||||
|
// Reaching the limit will result in a "no space left on device" or "too many open
|
||||||
|
// files" error.
|
||||||
|
//
|
||||||
|
// # kqueue notes (macOS, BSD)
|
||||||
|
//
|
||||||
|
// kqueue requires opening a file descriptor for every file that's being watched;
|
||||||
|
// so if you're watching a directory with five files then that's six file
|
||||||
|
// descriptors. You will run in to your system's "max open files" limit faster on
|
||||||
|
// these platforms.
|
||||||
|
//
|
||||||
|
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
|
||||||
|
// control the maximum number of open files, as well as /etc/login.conf on BSD
|
||||||
|
// systems.
|
||||||
|
//
|
||||||
|
// # macOS notes
|
||||||
|
//
|
||||||
|
// Spotlight indexing on macOS can result in multiple events (see [#15]). A
|
||||||
|
// temporary workaround is to add your folder(s) to the "Spotlight Privacy
|
||||||
|
// Settings" until we have a native FSEvents implementation (see [#11]).
|
||||||
|
//
|
||||||
|
// [#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
// [#15]: https://github.com/fsnotify/fsnotify/issues/15
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
new=$(<<EOF
|
||||||
|
// NewWatcher creates a new Watcher.
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
add=$(<<EOF
|
||||||
|
// Add starts monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// A path can only be watched once; attempting to watch it more than once will
|
||||||
|
// return an error. Paths that do not yet exist on the filesystem cannot be
|
||||||
|
// added. A watch will be automatically removed if the path is deleted.
|
||||||
|
//
|
||||||
|
// A path will remain watched if it gets renamed to somewhere else on the same
|
||||||
|
// filesystem, but the monitor will get removed if the path gets deleted and
|
||||||
|
// re-created, or if it's moved to a different filesystem.
|
||||||
|
//
|
||||||
|
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
|
||||||
|
// filesystems (/proc, /sys, etc.) generally don't work.
|
||||||
|
//
|
||||||
|
// # Watching directories
|
||||||
|
//
|
||||||
|
// All files in a directory are monitored, including new files that are created
|
||||||
|
// after the watcher is started. Subdirectories are not watched (i.e. it's
|
||||||
|
// non-recursive).
|
||||||
|
//
|
||||||
|
// # Watching files
|
||||||
|
//
|
||||||
|
// Watching individual files (rather than directories) is generally not
|
||||||
|
// recommended as many tools update files atomically. Instead of "just" writing
|
||||||
|
// to the file a temporary file will be written to first, and if successful the
|
||||||
|
// temporary file is moved to to destination removing the original, or some
|
||||||
|
// variant thereof. The watcher on the original file is now lost, as it no
|
||||||
|
// longer exists.
|
||||||
|
//
|
||||||
|
// Instead, watch the parent directory and use Event.Name to filter out files
|
||||||
|
// you're not interested in. There is an example of this in [cmd/fsnotify/file.go].
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
remove=$(<<EOF
|
||||||
|
// Remove stops monitoring the path for changes.
|
||||||
|
//
|
||||||
|
// Directories are always removed non-recursively. For example, if you added
|
||||||
|
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
|
||||||
|
//
|
||||||
|
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
close=$(<<EOF
|
||||||
|
// Close removes all watches and closes the events channel.
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
watchlist=$(<<EOF
|
||||||
|
// WatchList returns all paths added with [Add] (and are not yet removed).
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
events=$(<<EOF
|
||||||
|
// Events sends the filesystem change events.
|
||||||
|
//
|
||||||
|
// fsnotify can send the following events; a "path" here can refer to a
|
||||||
|
// file, directory, symbolic link, or special file like a FIFO.
|
||||||
|
//
|
||||||
|
// fsnotify.Create A new path was created; this may be followed by one
|
||||||
|
// or more Write events if data also gets written to a
|
||||||
|
// file.
|
||||||
|
//
|
||||||
|
// fsnotify.Remove A path was removed.
|
||||||
|
//
|
||||||
|
// fsnotify.Rename A path was renamed. A rename is always sent with the
|
||||||
|
// old path as Event.Name, and a Create event will be
|
||||||
|
// sent with the new name. Renames are only sent for
|
||||||
|
// paths that are currently watched; e.g. moving an
|
||||||
|
// unmonitored file into a monitored directory will
|
||||||
|
// show up as just a Create. Similarly, renaming a file
|
||||||
|
// to outside a monitored directory will show up as
|
||||||
|
// only a Rename.
|
||||||
|
//
|
||||||
|
// fsnotify.Write A file or named pipe was written to. A Truncate will
|
||||||
|
// also trigger a Write. A single "write action"
|
||||||
|
// initiated by the user may show up as one or multiple
|
||||||
|
// writes, depending on when the system syncs things to
|
||||||
|
// disk. For example when compiling a large Go program
|
||||||
|
// you may get hundreds of Write events, so you
|
||||||
|
// probably want to wait until you've stopped receiving
|
||||||
|
// them (see the dedup example in cmd/fsnotify).
|
||||||
|
//
|
||||||
|
// fsnotify.Chmod Attributes were changed. On Linux this is also sent
|
||||||
|
// when a file is removed (or more accurately, when a
|
||||||
|
// link to an inode is removed). On kqueue it's sent
|
||||||
|
// and on kqueue when a file is truncated. On Windows
|
||||||
|
// it's never sent.
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
errors=$(<<EOF
|
||||||
|
// Errors sends any errors.
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
set-cmt() {
|
||||||
|
local pat=$1
|
||||||
|
local cmt=$2
|
||||||
|
|
||||||
|
IFS=$'\n' local files=($(grep -n $pat backend_*~*_test.go))
|
||||||
|
for f in $files; do
|
||||||
|
IFS=':' local fields=($=f)
|
||||||
|
local file=$fields[1]
|
||||||
|
local end=$(( $fields[2] - 1 ))
|
||||||
|
|
||||||
|
# Find start of comment.
|
||||||
|
local start=0
|
||||||
|
IFS=$'\n' local lines=($(head -n$end $file))
|
||||||
|
for (( i = 1; i <= $#lines; i++ )); do
|
||||||
|
local line=$lines[-$i]
|
||||||
|
if ! grep -q '^[[:space:]]*//' <<<$line; then
|
||||||
|
start=$(( end - (i - 2) ))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
head -n $(( start - 1 )) $file >/tmp/x
|
||||||
|
print -r -- $cmt >>/tmp/x
|
||||||
|
tail -n+$(( end + 1 )) $file >>/tmp/x
|
||||||
|
mv /tmp/x $file
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
set-cmt '^type Watcher struct ' $watcher
|
||||||
|
set-cmt '^func NewWatcher(' $new
|
||||||
|
set-cmt '^func (w \*Watcher) Add(' $add
|
||||||
|
set-cmt '^func (w \*Watcher) Remove(' $remove
|
||||||
|
set-cmt '^func (w \*Watcher) Close(' $close
|
||||||
|
set-cmt '^func (w \*Watcher) WatchList(' $watchlist
|
||||||
|
set-cmt '^[[:space:]]*Events *chan Event$' $events
|
||||||
|
set-cmt '^[[:space:]]*Errors *chan error$' $errors
|
8
vendor/github.com/fsnotify/fsnotify/system_bsd.go
generated
vendored
Normal file
8
vendor/github.com/fsnotify/fsnotify/system_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//go:build freebsd || openbsd || netbsd || dragonfly
|
||||||
|
// +build freebsd openbsd netbsd dragonfly
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC
|
9
vendor/github.com/fsnotify/fsnotify/system_darwin.go
generated
vendored
Normal file
9
vendor/github.com/fsnotify/fsnotify/system_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package fsnotify
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
// note: this constant is not defined on BSD
|
||||||
|
const openMode = unix.O_EVTONLY | unix.O_CLOEXEC
|
9
vendor/modules.txt
vendored
9
vendor/modules.txt
vendored
@@ -53,7 +53,7 @@ github.com/asaskevich/govalidator
|
|||||||
# github.com/containerd/cgroups/v3 v3.0.2
|
# github.com/containerd/cgroups/v3 v3.0.2
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
github.com/containerd/cgroups/v3/cgroup1/stats
|
github.com/containerd/cgroups/v3/cgroup1/stats
|
||||||
# github.com/containerd/containerd v1.7.2
|
# github.com/containerd/containerd v1.7.6
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/containerd/containerd/errdefs
|
github.com/containerd/containerd/errdefs
|
||||||
github.com/containerd/containerd/log
|
github.com/containerd/containerd/log
|
||||||
@@ -61,8 +61,9 @@ github.com/containerd/containerd/log
|
|||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/containerd/stargz-snapshotter/estargz
|
github.com/containerd/stargz-snapshotter/estargz
|
||||||
github.com/containerd/stargz-snapshotter/estargz/errorutil
|
github.com/containerd/stargz-snapshotter/estargz/errorutil
|
||||||
# github.com/containers/common v0.55.4
|
# github.com/containers/common v0.56.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
|
github.com/containers/common/libnetwork/types
|
||||||
github.com/containers/common/pkg/auth
|
github.com/containers/common/pkg/auth
|
||||||
github.com/containers/common/pkg/capabilities
|
github.com/containers/common/pkg/capabilities
|
||||||
github.com/containers/common/pkg/completion
|
github.com/containers/common/pkg/completion
|
||||||
@@ -70,6 +71,7 @@ github.com/containers/common/pkg/flag
|
|||||||
github.com/containers/common/pkg/report
|
github.com/containers/common/pkg/report
|
||||||
github.com/containers/common/pkg/report/camelcase
|
github.com/containers/common/pkg/report/camelcase
|
||||||
github.com/containers/common/pkg/retry
|
github.com/containers/common/pkg/retry
|
||||||
|
github.com/containers/common/pkg/util
|
||||||
# github.com/containers/image/v5 v5.28.0
|
# github.com/containers/image/v5 v5.28.0
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/containers/image/v5/copy
|
github.com/containers/image/v5/copy
|
||||||
@@ -262,6 +264,9 @@ github.com/docker/go-connections/tlsconfig
|
|||||||
github.com/docker/go-units
|
github.com/docker/go-units
|
||||||
# github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5
|
# github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5
|
||||||
## explicit; go 1.9
|
## explicit; go 1.9
|
||||||
|
# github.com/fsnotify/fsnotify v1.6.0
|
||||||
|
## explicit; go 1.16
|
||||||
|
github.com/fsnotify/fsnotify
|
||||||
# github.com/go-jose/go-jose/v3 v3.0.0
|
# github.com/go-jose/go-jose/v3 v3.0.0
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/go-jose/go-jose/v3
|
github.com/go-jose/go-jose/v3
|
||||||
|
Reference in New Issue
Block a user