mirror of
https://github.com/containers/skopeo.git
synced 2025-09-04 16:20:23 +00:00
vendor of containers/common
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
3
go.mod
3
go.mod
@@ -3,7 +3,7 @@ module github.com/containers/skopeo
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/containers/common v0.56.0
|
github.com/containers/common v0.56.1-0.20230920110729-eb4ad859f309
|
||||||
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
|
||||||
@@ -42,7 +42,6 @@ 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
|
||||||
|
9
go.sum
9
go.sum
@@ -30,8 +30,8 @@ github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDY
|
|||||||
github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4=
|
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.56.0 h1:hysHUsEai1EkMXanU26UV55wMXns/a6AYmaFqJ4fEMY=
|
github.com/containers/common v0.56.1-0.20230920110729-eb4ad859f309 h1:ZuP6m9Ps9bBR/3TlR4AqzobAvtYstKkoYmKIg3R7O84=
|
||||||
github.com/containers/common v0.56.0/go.mod h1:IjaDdfUtcs2CfCcJMZxuut4XlvkTkY9Nlqkso9xCOq4=
|
github.com/containers/common v0.56.1-0.20230920110729-eb4ad859f309/go.mod h1:ABFEglmyt48WWWQv80kGhitfbVfR1Br35wk3gBQdrIk=
|
||||||
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,8 +71,6 @@ 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=
|
||||||
@@ -268,7 +266,7 @@ 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.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
|
github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA=
|
||||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
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=
|
||||||
@@ -471,7 +469,6 @@ 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=
|
||||||
|
59
vendor/github.com/containers/common/libnetwork/types/const.go
generated
vendored
59
vendor/github.com/containers/common/libnetwork/types/const.go
generated
vendored
@@ -1,59 +0,0 @@
|
|||||||
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
30
vendor/github.com/containers/common/libnetwork/types/define.go
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
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
339
vendor/github.com/containers/common/libnetwork/types/network.go
generated
vendored
@@ -1,339 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
4
vendor/github.com/containers/common/pkg/auth/auth.go
generated
vendored
4
vendor/github.com/containers/common/pkg/auth/auth.go
generated
vendored
@@ -10,7 +10,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/common/pkg/util"
|
passwd "github.com/containers/common/pkg/password"
|
||||||
"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"
|
||||||
@@ -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 := util.ReadPassword(int(os.Stdin.Fd()))
|
pass, err := passwd.Read(int(os.Stdin.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("reading password: %w", err)
|
return "", "", fmt.Errorf("reading password: %w", err)
|
||||||
}
|
}
|
||||||
|
57
vendor/github.com/containers/common/pkg/password/password_supported.go
generated
vendored
Normal file
57
vendor/github.com/containers/common/pkg/password/password_supported.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//go:build linux || darwin || freebsd
|
||||||
|
// +build linux darwin freebsd
|
||||||
|
|
||||||
|
package password
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
terminal "golang.org/x/term"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrInterrupt = errors.New("interrupted")
|
||||||
|
|
||||||
|
// Read reads a password from the terminal without echo.
|
||||||
|
func Read(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
|
||||||
|
}
|
@@ -1,21 +1,14 @@
|
|||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package util
|
package password
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
terminal "golang.org/x/term"
|
terminal "golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getRuntimeDir returns the runtime directory
|
// Read reads a password from the terminal.
|
||||||
func GetRuntimeDir() (string, error) {
|
func Read(fd int) ([]byte, 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)
|
oldState, err := terminal.GetState(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return make([]byte, 0), err
|
return make([]byte, 0), err
|
57
vendor/github.com/containers/common/pkg/util/copy.go
generated
vendored
57
vendor/github.com/containers/common/pkg/util/copy.go
generated
vendored
@@ -1,57 +0,0 @@
|
|||||||
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
206
vendor/github.com/containers/common/pkg/util/util.go
generated
vendored
@@ -1,206 +0,0 @@
|
|||||||
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
135
vendor/github.com/containers/common/pkg/util/util_supported.go
generated
vendored
@@ -1,135 +0,0 @@
|
|||||||
//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
|
|
||||||
}
|
|
12
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
12
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
@@ -1,12 +0,0 @@
|
|||||||
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
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
@@ -1 +0,0 @@
|
|||||||
go.sum linguist-generated
|
|
6
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
6
vendor/github.com/fsnotify/fsnotify/.gitignore
generated
vendored
@@ -1,6 +0,0 @@
|
|||||||
# go test -c output
|
|
||||||
*.test
|
|
||||||
*.test.exe
|
|
||||||
|
|
||||||
# Output of go build ./cmd/fsnotify
|
|
||||||
/fsnotify
|
|
2
vendor/github.com/fsnotify/fsnotify/.mailmap
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/.mailmap
generated
vendored
@@ -1,2 +0,0 @@
|
|||||||
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
470
vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
generated
vendored
@@ -1,470 +0,0 @@
|
|||||||
# 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
26
vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
generated
vendored
@@ -1,26 +0,0 @@
|
|||||||
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
25
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
@@ -1,25 +0,0 @@
|
|||||||
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
161
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
@@ -1,161 +0,0 @@
|
|||||||
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
162
vendor/github.com/fsnotify/fsnotify/backend_fen.go
generated
vendored
@@ -1,162 +0,0 @@
|
|||||||
//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
459
vendor/github.com/fsnotify/fsnotify/backend_inotify.go
generated
vendored
@@ -1,459 +0,0 @@
|
|||||||
//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
707
vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
generated
vendored
@@ -1,707 +0,0 @@
|
|||||||
//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
66
vendor/github.com/fsnotify/fsnotify/backend_other.go
generated
vendored
@@ -1,66 +0,0 @@
|
|||||||
//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
746
vendor/github.com/fsnotify/fsnotify/backend_windows.go
generated
vendored
@@ -1,746 +0,0 @@
|
|||||||
//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
81
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
@@ -1,81 +0,0 @@
|
|||||||
//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
208
vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
generated
vendored
@@ -1,208 +0,0 @@
|
|||||||
#!/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
8
vendor/github.com/fsnotify/fsnotify/system_bsd.go
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
//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
9
vendor/github.com/fsnotify/fsnotify/system_darwin.go
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
//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
|
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
@@ -61,17 +61,16 @@ 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.56.0
|
# github.com/containers/common v0.56.1-0.20230920110729-eb4ad859f309
|
||||||
## 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
|
||||||
github.com/containers/common/pkg/flag
|
github.com/containers/common/pkg/flag
|
||||||
|
github.com/containers/common/pkg/password
|
||||||
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
|
||||||
@@ -264,9 +263,6 @@ 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