diff --git a/go.mod b/go.mod
index 03b9cb7efe5098f6dd9a4651211477114eed511b..4ec5b7bec20548971d36214a19fdc1bc1996c749 100644
--- a/go.mod
+++ b/go.mod
@@ -7,14 +7,14 @@ require (
 	github.com/DATA-DOG/go-sqlmock v1.5.0
 	github.com/docker/docker v24.0.6+incompatible
 	github.com/docker/go-connections v0.4.0
-	github.com/fsnotify/fsnotify v1.7.0
+	github.com/fsnotify/fsnotify v1.8.0
 	github.com/google/uuid v1.6.0
-	github.com/pkg/sftp v1.13.6
+	github.com/pkg/sftp v1.13.7
 	github.com/robfig/cron/v3 v3.0.1
 	github.com/shirou/gopsutil/v3 v3.24.5
 	github.com/stretchr/testify v1.9.0
 	go.uber.org/zap v1.27.0
-	golang.org/x/crypto v0.27.0
+	golang.org/x/crypto v0.28.0
 	gopkg.in/yaml.v3 v3.0.1
 	gorm.io/driver/mysql v1.5.7
 	gorm.io/driver/sqlite v1.5.5
@@ -46,14 +46,14 @@ require (
 	github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
 	github.com/shoenig/go-m1cpu v0.1.6 // indirect
 	github.com/tklauser/go-sysconf v0.3.14 // indirect
-	github.com/tklauser/numcpus v0.8.0 // indirect
+	github.com/tklauser/numcpus v0.9.0 // indirect
 	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/mod v0.17.0 // indirect
 	golang.org/x/net v0.25.0 // indirect
 	golang.org/x/sync v0.8.0 // indirect
-	golang.org/x/sys v0.25.0 // indirect
-	golang.org/x/text v0.18.0 // indirect
+	golang.org/x/sys v0.26.0 // indirect
+	golang.org/x/text v0.19.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
 )
diff --git a/go.sum b/go.sum
index ab1a0fca6207aff6ba0789100bca2bf1c170138c..06e50f87817cd4740a5099d15a4d793fcb73562e 100644
--- a/go.sum
+++ b/go.sum
@@ -21,6 +21,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
 github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
 github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -57,6 +59,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
 github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
+github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
+github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
@@ -79,6 +83,8 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
 github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
 github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
 github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
+github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
+github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -95,11 +101,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
 golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
 golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -109,12 +119,15 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
 golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -127,19 +140,32 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
 golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
 golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
 golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
 golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -147,6 +173,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
index ffc7b992b3c7873e8ecfced5c840808a7edd2d6f..f4e7dbf37b363fcac673e697d469c3bbbf7fc5f9 100644
--- a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
+++ b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
@@ -1,7 +1,7 @@
 freebsd_task:
   name: 'FreeBSD'
   freebsd_instance:
-    image_family: freebsd-13-2
+    image_family: freebsd-14-1
   install_script:
     - pkg update -f
     - pkg install -y go
@@ -9,5 +9,6 @@ freebsd_task:
       # run tests as user "cirrus" instead of root
     - pw useradd cirrus -m
     - chown -R cirrus:cirrus .
-    - FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
-    -                      sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race ./...
+    - FSNOTIFY_BUFFER=4096 sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race    ./...
+    -                      sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race    ./...
+    - FSNOTIFY_DEBUG=1     sudo --preserve-env=FSNOTIFY_BUFFER -u cirrus go test -parallel 1 -race -v ./...
diff --git a/vendor/github.com/fsnotify/fsnotify/.editorconfig b/vendor/github.com/fsnotify/fsnotify/.editorconfig
deleted file mode 100644
index fad895851e56b56b08dfd6ac860e0c3fdf145c2c..0000000000000000000000000000000000000000
--- a/vendor/github.com/fsnotify/fsnotify/.editorconfig
+++ /dev/null
@@ -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
diff --git a/vendor/github.com/fsnotify/fsnotify/.gitattributes b/vendor/github.com/fsnotify/fsnotify/.gitattributes
deleted file mode 100644
index 32f1001be0a5ea57221f79fbb96cd7d41bed2fa7..0000000000000000000000000000000000000000
--- a/vendor/github.com/fsnotify/fsnotify/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-go.sum linguist-generated
diff --git a/vendor/github.com/fsnotify/fsnotify/.gitignore b/vendor/github.com/fsnotify/fsnotify/.gitignore
index 391cc076b12650e25fba6de40fe6d024ad8fc03e..daea9dd6d6d204e5bbe05883001f9b7e0f2a244a 100644
--- a/vendor/github.com/fsnotify/fsnotify/.gitignore
+++ b/vendor/github.com/fsnotify/fsnotify/.gitignore
@@ -5,3 +5,6 @@
 # Output of go build ./cmd/fsnotify
 /fsnotify
 /fsnotify.exe
+
+/test/kqueue
+/test/a.out
diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
index e0e57575496c305928f4a3ac640d538dc4b8fa86..fa854785d0f548e339cb4b959f8290e68df1dd46 100644
--- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
+++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
@@ -1,8 +1,36 @@
 # Changelog
 
-Unreleased
-----------
-Nothing yet.
+1.8.0 2023-10-31
+----------------
+
+### Additions
+
+- all: add `FSNOTIFY_DEBUG` to print debug logs to stderr ([#619])
+
+### Changes and fixes
+
+- windows: fix behaviour of `WatchList()` to be consistent with other platforms ([#610])
+
+- kqueue: ignore events with Ident=0 ([#590])
+
+- kqueue: set O_CLOEXEC to prevent passing file descriptors to children ([#617])
+
+- kqueue: emit events as "/path/dir/file" instead of "path/link/file" when watching a symlink ([#625])
+
+- inotify: don't send event for IN_DELETE_SELF when also watching the parent ([#620])
+
+- inotify: fix panic when calling Remove() in a goroutine ([#650])
+
+- fen: allow watching subdirectories of watched directories ([#621])
+
+[#590]: https://github.com/fsnotify/fsnotify/pull/590
+[#610]: https://github.com/fsnotify/fsnotify/pull/610
+[#617]: https://github.com/fsnotify/fsnotify/pull/617
+[#619]: https://github.com/fsnotify/fsnotify/pull/619
+[#620]: https://github.com/fsnotify/fsnotify/pull/620
+[#621]: https://github.com/fsnotify/fsnotify/pull/621
+[#625]: https://github.com/fsnotify/fsnotify/pull/625
+[#650]: https://github.com/fsnotify/fsnotify/pull/650
 
 1.7.0 - 2023-10-22
 ------------------
diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
index ea379759d51a0b19d670adaeb858c3c851cfe134..e4ac2a2fffdc12c9385e5dfbeedf9061d8c27ec4 100644
--- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
+++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
@@ -1,7 +1,7 @@
 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
+- To avoid "wasted" work, please discuss changes on the issue tracker first. You
   can just send PRs, but they may end up being rejected for one reason or the
   other.
 
@@ -20,6 +20,124 @@ platforms. Testing different platforms locally can be done with something like
 
 Use the `-short` flag to make the "stress test" run faster.
 
+Writing new tests
+-----------------
+Scripts in the testdata directory allow creating test cases in a "shell-like"
+syntax. The basic format is:
+
+    script
+
+    Output:
+    desired output
+
+For example:
+
+    # Create a new empty file with some data.
+    watch /
+    echo data >/file
+
+    Output:
+        create  /file
+        write   /file
+
+Just create a new file to add a new test; select which tests to run with
+`-run TestScript/[path]`.
+
+script
+------
+The script is a "shell-like" script:
+
+    cmd arg arg
+
+Comments are supported with `#`:
+
+    # Comment
+    cmd arg arg  # Comment
+
+All operations are done in a temp directory; a path like "/foo" is rewritten to
+"/tmp/TestFoo/foo".
+
+Arguments can be quoted with `"` or `'`; there are no escapes and they're
+functionally identical right now, but this may change in the future, so best to
+assume shell-like rules.
+
+    touch "/file with spaces"
+
+End-of-line escapes with `\` are not supported.
+
+### Supported commands
+
+    watch path [ops]    # Watch the path, reporting events for it. Nothing is
+                        # watched by default. Optionally a list of ops can be
+                        # given, as with AddWith(path, WithOps(...)).
+    unwatch path        # Stop watching the path.
+    watchlist n         # Assert watchlist length.
+
+    stop                # Stop running the script; for debugging.
+    debug [yes/no]      # Enable/disable FSNOTIFY_DEBUG (tests are run in
+                          parallel by default, so -parallel=1 is probably a good
+                          idea).
+
+    touch path
+    mkdir [-p] dir
+    ln -s target link   # Only ln -s supported.
+    mkfifo path
+    mknod dev path
+    mv src dst
+    rm [-r] path
+    chmod mode path     # Octal only
+    sleep time-in-ms
+
+    cat path            # Read path (does nothing with the data; just reads it).
+    echo str >>path     # Append "str" to "path".
+    echo str >path      # Truncate "path" and write "str".
+
+    require reason      # Skip the test if "reason" is true; "skip" and
+    skip reason         # "require" behave identical; it supports both for
+                        # readability. Possible reasons are:
+                        #
+                        #   always    Always skip this test.
+                        #   symlink   Symlinks are supported (requires admin
+                        #             permissions on Windows).
+                        #   mkfifo    Platform doesn't support FIFO named sockets.
+                        #   mknod     Platform doesn't support device nodes.
+
+
+output
+------
+After `Output:` the desired output is given; this is indented by convention, but
+that's not required.
+
+The format of that is:
+
+    # Comment
+    event  path  # Comment
+
+    system:
+        event  path
+    system2:
+        event  path
+
+Every event is one line, and any whitespace between the event and path are
+ignored. The path can optionally be surrounded in ". Anything after a "#" is
+ignored.
+
+Platform-specific tests can be added after GOOS; for example:
+
+    watch /
+    touch /file
+
+    Output:
+        # Tested if nothing else matches
+        create    /file
+
+        # Windows-specific test.
+        windows:
+            write  /file
+
+You can specify multiple platforms with a comma (e.g. "windows, linux:").
+"kqueue" is a shortcut for all kqueue systems (BSD, macOS).
+
 
 [goon]: https://github.com/arp242/goon
 [Vagrant]: https://www.vagrantup.com/
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_fen.go b/vendor/github.com/fsnotify/fsnotify/backend_fen.go
index 28497f1dd8e6c13e2a7e9fbe6dfc002ec1f2d78e..c349c326c718fc541fd89bd2322d3218cba1a0a9 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_fen.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_fen.go
@@ -1,8 +1,8 @@
 //go:build solaris
-// +build solaris
 
-// Note: the documentation on the Watcher type and methods is generated from
-// mkdoc.zsh
+// FEN backend for illumos (supported) and Solaris (untested, but should work).
+//
+// See port_create(3c) etc. for docs. https://www.illumos.org/man/3C/port_create
 
 package fsnotify
 
@@ -12,150 +12,33 @@ import (
 	"os"
 	"path/filepath"
 	"sync"
+	"time"
 
+	"github.com/fsnotify/fsnotify/internal"
 	"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.
-//
-// # Windows notes
-//
-// Paths can be added as "C:\path\to\dir", but forward slashes
-// ("C:/path/to/dir") will also work.
-//
-// When a watched directory is removed it will always send an event for the
-// directory itself, but may not send events for all files in that directory.
-// Sometimes it will send events for all times, sometimes it will send no
-// events, and often only for some files.
-//
-// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
-// value that is guaranteed to work with SMB filesystems. If you have many
-// events in quick succession this may not be enough, and you will have to use
-// [WithBufferSize] to increase the value.
-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, and you may
-	//                      want to wait until you've stopped receiving them
-	//                      (see the dedup example in cmd/fsnotify).
-	//
-	//                      Some systems may send Write event for directories
-	//                      when the directory content changes.
-	//
-	//   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
-	//                      when a file is truncated. On Windows it's never
-	//                      sent.
+type fen struct {
 	Events chan Event
-
-	// Errors sends any errors.
-	//
-	// ErrEventOverflow is used to indicate there are too many events:
-	//
-	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
-	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
-	//  - kqueue, fen:  Not used.
 	Errors chan error
 
 	mu      sync.Mutex
 	port    *unix.EventPort
-	done    chan struct{}       // Channel for sending a "quit message" to the reader goroutine
-	dirs    map[string]struct{} // Explicitly watched directories
-	watches map[string]struct{} // Explicitly watched non-directories
+	done    chan struct{} // Channel for sending a "quit message" to the reader goroutine
+	dirs    map[string]Op // Explicitly watched directories
+	watches map[string]Op // Explicitly watched non-directories
 }
 
-// NewWatcher creates a new Watcher.
-func NewWatcher() (*Watcher, error) {
-	return NewBufferedWatcher(0)
+func newBackend(ev chan Event, errs chan error) (backend, error) {
+	return newBufferedBackend(0, ev, errs)
 }
 
-// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
-// channel.
-//
-// The main use case for this is situations with a very large number of events
-// where the kernel buffer size can't be increased (e.g. due to lack of
-// permissions). An unbuffered Watcher will perform better for almost all use
-// cases, and whenever possible you will be better off increasing the kernel
-// buffers instead of adding a large userspace buffer.
-func NewBufferedWatcher(sz uint) (*Watcher, error) {
-	w := &Watcher{
-		Events:  make(chan Event, sz),
-		Errors:  make(chan error),
-		dirs:    make(map[string]struct{}),
-		watches: make(map[string]struct{}),
+func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+	w := &fen{
+		Events:  ev,
+		Errors:  errs,
+		dirs:    make(map[string]Op),
+		watches: make(map[string]Op),
 		done:    make(chan struct{}),
 	}
 
@@ -171,27 +54,30 @@ func NewBufferedWatcher(sz uint) (*Watcher, error) {
 
 // sendEvent attempts to send an event to the user, returning true if the event
 // was put in the channel successfully and false if the watcher has been closed.
-func (w *Watcher) sendEvent(name string, op Op) (sent bool) {
+func (w *fen) sendEvent(name string, op Op) (sent bool) {
 	select {
-	case w.Events <- Event{Name: name, Op: op}:
-		return true
 	case <-w.done:
 		return false
+	case w.Events <- Event{Name: name, Op: op}:
+		return true
 	}
 }
 
 // sendError attempts to send an error to the user, returning true if the error
 // was put in the channel successfully and false if the watcher has been closed.
-func (w *Watcher) sendError(err error) (sent bool) {
-	select {
-	case w.Errors <- err:
+func (w *fen) sendError(err error) (sent bool) {
+	if err == nil {
 		return true
+	}
+	select {
 	case <-w.done:
 		return false
+	case w.Errors <- err:
+		return true
 	}
 }
 
-func (w *Watcher) isClosed() bool {
+func (w *fen) isClosed() bool {
 	select {
 	case <-w.done:
 		return true
@@ -200,8 +86,7 @@ func (w *Watcher) isClosed() bool {
 	}
 }
 
-// Close removes all watches and closes the Events channel.
-func (w *Watcher) Close() error {
+func (w *fen) Close() error {
 	// Take the lock used by associateFile to prevent lingering events from
 	// being processed after the close
 	w.mu.Lock()
@@ -213,60 +98,21 @@ func (w *Watcher) Close() error {
 	return w.port.Close()
 }
 
-// Add starts monitoring the path for changes.
-//
-// A path can only be watched once; watching it more than once is a no-op and will
-// not return an error. Paths that do not yet exist on the filesystem cannot be
-// watched.
-//
-// A watch will be automatically removed if the watched path is deleted or
-// renamed. The exception is the Windows backend, which doesn't remove the
-// watcher on renames.
-//
-// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
-// filesystems (/proc, /sys, etc.) generally don't work.
-//
-// Returns [ErrClosed] if [Watcher.Close] was called.
-//
-// See [Watcher.AddWith] for a version that allows adding options.
-//
-// # 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 programs (especially editors) update files atomically: it
-// will write to a temporary file which is then moved to to destination,
-// overwriting the original (or some variant thereof). The watcher on the
-// original file is now lost, as that no longer exists.
-//
-// The upshot of this is that a power failure or crash won't leave a
-// half-written file.
-//
-// 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 w.AddWith(name) }
+func (w *fen) Add(name string) error { return w.AddWith(name) }
 
-// AddWith is like [Watcher.Add], but allows adding options. When using Add()
-// the defaults described below are used.
-//
-// Possible options are:
-//
-//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
-//     other platforms. The default is 64K (65536 bytes).
-func (w *Watcher) AddWith(name string, opts ...addOpt) error {
+func (w *fen) AddWith(name string, opts ...addOpt) error {
 	if w.isClosed() {
 		return ErrClosed
 	}
-	if w.port.PathIsWatched(name) {
-		return nil
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  AddWith(%q)\n",
+			time.Now().Format("15:04:05.000000000"), name)
 	}
 
-	_ = getOptions(opts...)
+	with := getOptions(opts...)
+	if !w.xSupports(with.op) {
+		return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
+	}
 
 	// Currently we resolve symlinks that were explicitly requested to be
 	// watched. Otherwise we would use LStat here.
@@ -283,7 +129,7 @@ func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 		}
 
 		w.mu.Lock()
-		w.dirs[name] = struct{}{}
+		w.dirs[name] = with.op
 		w.mu.Unlock()
 		return nil
 	}
@@ -294,26 +140,22 @@ func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 	}
 
 	w.mu.Lock()
-	w.watches[name] = struct{}{}
+	w.watches[name] = with.op
 	w.mu.Unlock()
 	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].
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) Remove(name string) error {
+func (w *fen) Remove(name string) error {
 	if w.isClosed() {
 		return nil
 	}
 	if !w.port.PathIsWatched(name) {
 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
 	}
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  Remove(%q)\n",
+			time.Now().Format("15:04:05.000000000"), name)
+	}
 
 	// The user has expressed an intent. Immediately remove this name from
 	// whichever watch list it might be in. If it's not in there the delete
@@ -346,7 +188,7 @@ func (w *Watcher) Remove(name string) error {
 }
 
 // readEvents contains the main loop that runs in a goroutine watching for events.
-func (w *Watcher) readEvents() {
+func (w *fen) readEvents() {
 	// If this function returns, the watcher has been closed and we can close
 	// these channels
 	defer func() {
@@ -382,17 +224,19 @@ func (w *Watcher) readEvents() {
 				continue
 			}
 
+			if debug {
+				internal.Debug(pevent.Path, pevent.Events)
+			}
+
 			err = w.handleEvent(&pevent)
-			if err != nil {
-				if !w.sendError(err) {
-					return
-				}
+			if !w.sendError(err) {
+				return
 			}
 		}
 	}
 }
 
-func (w *Watcher) handleDirectory(path string, stat os.FileInfo, follow bool, handler func(string, os.FileInfo, bool) error) error {
+func (w *fen) handleDirectory(path string, stat os.FileInfo, follow bool, handler func(string, os.FileInfo, bool) error) error {
 	files, err := os.ReadDir(path)
 	if err != nil {
 		return err
@@ -418,7 +262,7 @@ func (w *Watcher) handleDirectory(path string, stat os.FileInfo, follow bool, ha
 // bitmap matches more than one event type (e.g. the file was both modified and
 // had the attributes changed between when the association was created and the
 // when event was returned)
-func (w *Watcher) handleEvent(event *unix.PortEvent) error {
+func (w *fen) handleEvent(event *unix.PortEvent) error {
 	var (
 		events     = event.Events
 		path       = event.Path
@@ -510,15 +354,9 @@ func (w *Watcher) handleEvent(event *unix.PortEvent) error {
 	}
 
 	if events&unix.FILE_MODIFIED != 0 {
-		if fmode.IsDir() {
-			if watchedDir {
-				if err := w.updateDirectory(path); err != nil {
-					return err
-				}
-			} else {
-				if !w.sendEvent(path, Write) {
-					return nil
-				}
+		if fmode.IsDir() && watchedDir {
+			if err := w.updateDirectory(path); err != nil {
+				return err
 			}
 		} else {
 			if !w.sendEvent(path, Write) {
@@ -543,7 +381,7 @@ func (w *Watcher) handleEvent(event *unix.PortEvent) error {
 	return nil
 }
 
-func (w *Watcher) updateDirectory(path string) error {
+func (w *fen) updateDirectory(path string) error {
 	// The directory was modified, so we must find unwatched entities and watch
 	// them. If something was removed from the directory, nothing will happen,
 	// as everything else should still be watched.
@@ -563,10 +401,8 @@ func (w *Watcher) updateDirectory(path string) error {
 			return err
 		}
 		err = w.associateFile(path, finfo, false)
-		if err != nil {
-			if !w.sendError(err) {
-				return nil
-			}
+		if !w.sendError(err) {
+			return nil
 		}
 		if !w.sendEvent(path, Create) {
 			return nil
@@ -575,7 +411,7 @@ func (w *Watcher) updateDirectory(path string) error {
 	return nil
 }
 
-func (w *Watcher) associateFile(path string, stat os.FileInfo, follow bool) error {
+func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
 	if w.isClosed() {
 		return ErrClosed
 	}
@@ -593,34 +429,34 @@ func (w *Watcher) associateFile(path string, stat os.FileInfo, follow bool) erro
 		// cleared up that discrepancy. The most likely cause is that the event
 		// has fired but we haven't processed it yet.
 		err := w.port.DissociatePath(path)
-		if err != nil && err != unix.ENOENT {
+		if err != nil && !errors.Is(err, unix.ENOENT) {
 			return err
 		}
 	}
-	// FILE_NOFOLLOW means we watch symlinks themselves rather than their
-	// targets.
-	events := unix.FILE_MODIFIED | unix.FILE_ATTRIB | unix.FILE_NOFOLLOW
-	if follow {
-		// We *DO* follow symlinks for explicitly watched entries.
-		events = unix.FILE_MODIFIED | unix.FILE_ATTRIB
+
+	var events int
+	if !follow {
+		// Watch symlinks themselves rather than their targets unless this entry
+		// is explicitly watched.
+		events |= unix.FILE_NOFOLLOW
+	}
+	if true { // TODO: implement withOps()
+		events |= unix.FILE_MODIFIED
 	}
-	return w.port.AssociatePath(path, stat,
-		events,
-		stat.Mode())
+	if true {
+		events |= unix.FILE_ATTRIB
+	}
+	return w.port.AssociatePath(path, stat, events, stat.Mode())
 }
 
-func (w *Watcher) dissociateFile(path string, stat os.FileInfo, unused bool) error {
+func (w *fen) dissociateFile(path string, stat os.FileInfo, unused bool) error {
 	if !w.port.PathIsWatched(path) {
 		return nil
 	}
 	return w.port.DissociatePath(path)
 }
 
-// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
-// yet removed).
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) WatchList() []string {
+func (w *fen) WatchList() []string {
 	if w.isClosed() {
 		return nil
 	}
@@ -638,3 +474,11 @@ func (w *Watcher) WatchList() []string {
 
 	return entries
 }
+
+func (w *fen) xSupports(op Op) bool {
+	if op.Has(xUnportableOpen) || op.Has(xUnportableRead) ||
+		op.Has(xUnportableCloseWrite) || op.Has(xUnportableCloseRead) {
+		return false
+	}
+	return true
+}
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
index 921c1c1e401219e1aa5c150a4a4585c814e8fdbc..36c311694cd5ae27af198736d48c0ac9556e6520 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
@@ -1,8 +1,4 @@
 //go:build linux && !appengine
-// +build linux,!appengine
-
-// Note: the documentation on the Watcher type and methods is generated from
-// mkdoc.zsh
 
 package fsnotify
 
@@ -10,127 +6,20 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"strings"
 	"sync"
+	"time"
 	"unsafe"
 
+	"github.com/fsnotify/fsnotify/internal"
 	"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.
-//
-// # Windows notes
-//
-// Paths can be added as "C:\path\to\dir", but forward slashes
-// ("C:/path/to/dir") will also work.
-//
-// When a watched directory is removed it will always send an event for the
-// directory itself, but may not send events for all files in that directory.
-// Sometimes it will send events for all times, sometimes it will send no
-// events, and often only for some files.
-//
-// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
-// value that is guaranteed to work with SMB filesystems. If you have many
-// events in quick succession this may not be enough, and you will have to use
-// [WithBufferSize] to increase the value.
-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, and you may
-	//                      want to wait until you've stopped receiving them
-	//                      (see the dedup example in cmd/fsnotify).
-	//
-	//                      Some systems may send Write event for directories
-	//                      when the directory content changes.
-	//
-	//   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
-	//                      when a file is truncated. On Windows it's never
-	//                      sent.
+type inotify struct {
 	Events chan Event
-
-	// Errors sends any errors.
-	//
-	// ErrEventOverflow is used to indicate there are too many events:
-	//
-	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
-	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
-	//  - kqueue, fen:  Not used.
 	Errors chan error
 
 	// Store fd here as os.File.Read() will no longer return on close after
@@ -139,8 +28,26 @@ type Watcher struct {
 	inotifyFile *os.File
 	watches     *watches
 	done        chan struct{} // Channel for sending a "quit message" to the reader goroutine
-	closeMu     sync.Mutex
+	doneMu      sync.Mutex
 	doneResp    chan struct{} // Channel to respond to Close
+
+	// Store rename cookies in an array, with the index wrapping to 0. Almost
+	// all of the time what we get is a MOVED_FROM to set the cookie and the
+	// next event inotify sends will be MOVED_TO to read it. However, this is
+	// not guaranteed – as described in inotify(7) – and we may get other events
+	// between the two MOVED_* events (including other MOVED_* ones).
+	//
+	// A second issue is that moving a file outside the watched directory will
+	// trigger a MOVED_FROM to set the cookie, but we never see the MOVED_TO to
+	// read and delete it. So just storing it in a map would slowly leak memory.
+	//
+	// Doing it like this gives us a simple fast LRU-cache that won't allocate.
+	// Ten items should be more than enough for our purpose, and a loop over
+	// such a short array is faster than a map access anyway (not that it hugely
+	// matters since we're talking about hundreds of ns at the most, but still).
+	cookies     [10]koekje
+	cookieIndex uint8
+	cookiesMu   sync.Mutex
 }
 
 type (
@@ -150,9 +57,14 @@ type (
 		path map[string]uint32 // pathname → wd
 	}
 	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)
-		path  string // Watch path.
+		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)
+		path    string // Watch path.
+		recurse bool   // Recursion with ./...?
+	}
+	koekje struct {
+		cookie uint32
+		path   string
 	}
 )
 
@@ -179,23 +91,45 @@ func (w *watches) add(ww *watch) {
 func (w *watches) remove(wd uint32) {
 	w.mu.Lock()
 	defer w.mu.Unlock()
-	delete(w.path, w.wd[wd].path)
+	watch := w.wd[wd] // Could have had Remove() called. See #616.
+	if watch == nil {
+		return
+	}
+	delete(w.path, watch.path)
 	delete(w.wd, wd)
 }
 
-func (w *watches) removePath(path string) (uint32, bool) {
+func (w *watches) removePath(path string) ([]uint32, error) {
 	w.mu.Lock()
 	defer w.mu.Unlock()
 
+	path, recurse := recursivePath(path)
 	wd, ok := w.path[path]
 	if !ok {
-		return 0, false
+		return nil, fmt.Errorf("%w: %s", ErrNonExistentWatch, path)
+	}
+
+	watch := w.wd[wd]
+	if recurse && !watch.recurse {
+		return nil, fmt.Errorf("can't use /... with non-recursive watch %q", path)
 	}
 
 	delete(w.path, path)
 	delete(w.wd, wd)
+	if !watch.recurse {
+		return []uint32{wd}, nil
+	}
 
-	return wd, true
+	wds := make([]uint32, 0, 8)
+	wds = append(wds, wd)
+	for p, rwd := range w.path {
+		if filepath.HasPrefix(p, path) {
+			delete(w.path, p)
+			delete(w.wd, rwd)
+			wds = append(wds, rwd)
+		}
+	}
+	return wds, nil
 }
 
 func (w *watches) byPath(path string) *watch {
@@ -236,20 +170,11 @@ func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error
 	return nil
 }
 
-// NewWatcher creates a new Watcher.
-func NewWatcher() (*Watcher, error) {
-	return NewBufferedWatcher(0)
+func newBackend(ev chan Event, errs chan error) (backend, error) {
+	return newBufferedBackend(0, ev, errs)
 }
 
-// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
-// channel.
-//
-// The main use case for this is situations with a very large number of events
-// where the kernel buffer size can't be increased (e.g. due to lack of
-// permissions). An unbuffered Watcher will perform better for almost all use
-// cases, and whenever possible you will be better off increasing the kernel
-// buffers instead of adding a large userspace buffer.
-func NewBufferedWatcher(sz uint) (*Watcher, error) {
+func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
 	// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
 	// I/O operations won't terminate on close.
 	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
@@ -257,12 +182,12 @@ func NewBufferedWatcher(sz uint) (*Watcher, error) {
 		return nil, errno
 	}
 
-	w := &Watcher{
+	w := &inotify{
+		Events:      ev,
+		Errors:      errs,
 		fd:          fd,
 		inotifyFile: os.NewFile(uintptr(fd), ""),
 		watches:     newWatches(),
-		Events:      make(chan Event, sz),
-		Errors:      make(chan error),
 		done:        make(chan struct{}),
 		doneResp:    make(chan struct{}),
 	}
@@ -272,26 +197,29 @@ func NewBufferedWatcher(sz uint) (*Watcher, error) {
 }
 
 // Returns true if the event was sent, or false if watcher is closed.
-func (w *Watcher) sendEvent(e Event) bool {
+func (w *inotify) sendEvent(e Event) bool {
 	select {
-	case w.Events <- e:
-		return true
 	case <-w.done:
 		return false
+	case w.Events <- e:
+		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:
+func (w *inotify) sendError(err error) bool {
+	if err == nil {
 		return true
+	}
+	select {
 	case <-w.done:
 		return false
+	case w.Errors <- err:
+		return true
 	}
 }
 
-func (w *Watcher) isClosed() bool {
+func (w *inotify) isClosed() bool {
 	select {
 	case <-w.done:
 		return true
@@ -300,15 +228,14 @@ func (w *Watcher) isClosed() bool {
 	}
 }
 
-// Close removes all watches and closes the Events channel.
-func (w *Watcher) Close() error {
-	w.closeMu.Lock()
+func (w *inotify) Close() error {
+	w.doneMu.Lock()
 	if w.isClosed() {
-		w.closeMu.Unlock()
+		w.doneMu.Unlock()
 		return nil
 	}
 	close(w.done)
-	w.closeMu.Unlock()
+	w.doneMu.Unlock()
 
 	// Causes any blocking reads to return with an error, provided the file
 	// still supports deadline operations.
@@ -323,78 +250,104 @@ func (w *Watcher) Close() error {
 	return nil
 }
 
-// Add starts monitoring the path for changes.
-//
-// A path can only be watched once; watching it more than once is a no-op and will
-// not return an error. Paths that do not yet exist on the filesystem cannot be
-// watched.
-//
-// A watch will be automatically removed if the watched path is deleted or
-// renamed. The exception is the Windows backend, which doesn't remove the
-// watcher on renames.
-//
-// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
-// filesystems (/proc, /sys, etc.) generally don't work.
-//
-// Returns [ErrClosed] if [Watcher.Close] was called.
-//
-// See [Watcher.AddWith] for a version that allows adding options.
-//
-// # 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 programs (especially editors) update files atomically: it
-// will write to a temporary file which is then moved to to destination,
-// overwriting the original (or some variant thereof). The watcher on the
-// original file is now lost, as that no longer exists.
-//
-// The upshot of this is that a power failure or crash won't leave a
-// half-written file.
-//
-// 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 w.AddWith(name) }
-
-// AddWith is like [Watcher.Add], but allows adding options. When using Add()
-// the defaults described below are used.
-//
-// Possible options are:
-//
-//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
-//     other platforms. The default is 64K (65536 bytes).
-func (w *Watcher) AddWith(name string, opts ...addOpt) error {
+func (w *inotify) Add(name string) error { return w.AddWith(name) }
+
+func (w *inotify) AddWith(path string, opts ...addOpt) error {
 	if w.isClosed() {
 		return ErrClosed
 	}
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  AddWith(%q)\n",
+			time.Now().Format("15:04:05.000000000"), path)
+	}
+
+	with := getOptions(opts...)
+	if !w.xSupports(with.op) {
+		return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
+	}
 
-	name = filepath.Clean(name)
-	_ = getOptions(opts...)
+	path, recurse := recursivePath(path)
+	if recurse {
+		return filepath.WalkDir(path, func(root string, d fs.DirEntry, err error) error {
+			if err != nil {
+				return err
+			}
+			if !d.IsDir() {
+				if root == path {
+					return fmt.Errorf("fsnotify: not a directory: %q", path)
+				}
+				return nil
+			}
 
-	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
+			// Send a Create event when adding new directory from a recursive
+			// watch; this is for "mkdir -p one/two/three". Usually all those
+			// directories will be created before we can set up watchers on the
+			// subdirectories, so only "one" would be sent as a Create event and
+			// not "one/two" and "one/two/three" (inotifywait -r has the same
+			// problem).
+			if with.sendCreate && root != path {
+				w.sendEvent(Event{Name: root, Op: Create})
+			}
+
+			return w.add(root, with, true)
+		})
+	}
 
-	return w.watches.updatePath(name, func(existing *watch) (*watch, error) {
+	return w.add(path, with, false)
+}
+
+func (w *inotify) add(path string, with withOpts, recurse bool) error {
+	var flags uint32
+	if with.noFollow {
+		flags |= unix.IN_DONT_FOLLOW
+	}
+	if with.op.Has(Create) {
+		flags |= unix.IN_CREATE
+	}
+	if with.op.Has(Write) {
+		flags |= unix.IN_MODIFY
+	}
+	if with.op.Has(Remove) {
+		flags |= unix.IN_DELETE | unix.IN_DELETE_SELF
+	}
+	if with.op.Has(Rename) {
+		flags |= unix.IN_MOVED_TO | unix.IN_MOVED_FROM | unix.IN_MOVE_SELF
+	}
+	if with.op.Has(Chmod) {
+		flags |= unix.IN_ATTRIB
+	}
+	if with.op.Has(xUnportableOpen) {
+		flags |= unix.IN_OPEN
+	}
+	if with.op.Has(xUnportableRead) {
+		flags |= unix.IN_ACCESS
+	}
+	if with.op.Has(xUnportableCloseWrite) {
+		flags |= unix.IN_CLOSE_WRITE
+	}
+	if with.op.Has(xUnportableCloseRead) {
+		flags |= unix.IN_CLOSE_NOWRITE
+	}
+	return w.register(path, flags, recurse)
+}
+
+func (w *inotify) register(path string, flags uint32, recurse bool) error {
+	return w.watches.updatePath(path, func(existing *watch) (*watch, error) {
 		if existing != nil {
 			flags |= existing.flags | unix.IN_MASK_ADD
 		}
 
-		wd, err := unix.InotifyAddWatch(w.fd, name, flags)
+		wd, err := unix.InotifyAddWatch(w.fd, path, flags)
 		if wd == -1 {
 			return nil, err
 		}
 
 		if existing == nil {
 			return &watch{
-				wd:    uint32(wd),
-				path:  name,
-				flags: flags,
+				wd:      uint32(wd),
+				path:    path,
+				flags:   flags,
+				recurse: recurse,
 			}, nil
 		}
 
@@ -404,49 +357,44 @@ func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 	})
 }
 
-// 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].
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) Remove(name string) error {
+func (w *inotify) Remove(name string) error {
 	if w.isClosed() {
 		return nil
 	}
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  Remove(%q)\n",
+			time.Now().Format("15:04:05.000000000"), name)
+	}
 	return w.remove(filepath.Clean(name))
 }
 
-func (w *Watcher) remove(name string) error {
-	wd, ok := w.watches.removePath(name)
-	if !ok {
-		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
-	}
-
-	success, errno := unix.InotifyRmWatch(w.fd, 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
+func (w *inotify) remove(name string) error {
+	wds, err := w.watches.removePath(name)
+	if err != nil {
+		return err
+	}
+
+	for _, wd := range wds {
+		_, err := unix.InotifyRmWatch(w.fd, wd)
+		if err != nil {
+			// 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 err
+		}
 	}
 	return nil
 }
 
-// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
-// yet removed).
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) WatchList() []string {
+func (w *inotify) WatchList() []string {
 	if w.isClosed() {
 		return nil
 	}
@@ -463,7 +411,7 @@ func (w *Watcher) WatchList() []string {
 
 // 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() {
+func (w *inotify) readEvents() {
 	defer func() {
 		close(w.doneResp)
 		close(w.Errors)
@@ -506,15 +454,17 @@ func (w *Watcher) readEvents() {
 			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...
+		var offset uint32
 		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)
+				// Move to the next event in the buffer
+				next = func() { offset += unix.SizeofInotifyEvent + nameLen }
 			)
 
 			if mask&unix.IN_Q_OVERFLOW != 0 {
@@ -523,21 +473,53 @@ func (w *Watcher) readEvents() {
 				}
 			}
 
-			// 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.
+			/// 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.
 			watch := w.watches.byWd(uint32(raw.Wd))
+			/// Can be nil if Remove() was called in another goroutine for this
+			/// path inbetween reading the events from the kernel and reading
+			/// the internal state. Not much we can do about it, so just skip.
+			/// See #616.
+			if watch == nil {
+				next()
+				continue
+			}
+
+			name := watch.path
+			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")
+			}
+
+			if debug {
+				internal.Debug(name, raw.Mask, raw.Cookie)
+			}
+
+			if mask&unix.IN_IGNORED != 0 { //&& event.Op != 0
+				next()
+				continue
+			}
 
 			// inotify will automatically remove the watch on deletes; just need
 			// to clean our state here.
-			if watch != nil && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
+			if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
 				w.watches.remove(watch.wd)
 			}
+
 			// We can't really update the state when a watched path is moved;
 			// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
 			// the watch.
-			if watch != nil && mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
+			if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
+				if watch.recurse {
+					next() // Do nothing
+					continue
+				}
+
 				err := w.remove(watch.path)
 				if err != nil && !errors.Is(err, ErrNonExistentWatch) {
 					if !w.sendError(err) {
@@ -546,34 +528,69 @@ func (w *Watcher) readEvents() {
 				}
 			}
 
-			var name string
-			if watch != nil {
-				name = watch.path
-			}
-			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")
+			/// Skip if we're watching both this path and the parent; the parent
+			/// will already send a delete so no need to do it twice.
+			if mask&unix.IN_DELETE_SELF != 0 {
+				if _, ok := w.watches.path[filepath.Dir(watch.path)]; ok {
+					next()
+					continue
+				}
 			}
 
-			event := w.newEvent(name, mask)
+			ev := w.newEvent(name, mask, raw.Cookie)
+			// Need to update watch path for recurse.
+			if watch.recurse {
+				isDir := mask&unix.IN_ISDIR == unix.IN_ISDIR
+				/// New directory created: set up watch on it.
+				if isDir && ev.Has(Create) {
+					err := w.register(ev.Name, watch.flags, true)
+					if !w.sendError(err) {
+						return
+					}
 
-			// Send the events that are not ignored on the events channel
-			if mask&unix.IN_IGNORED == 0 {
-				if !w.sendEvent(event) {
-					return
+					// This was a directory rename, so we need to update all
+					// the children.
+					//
+					// TODO: this is of course pretty slow; we should use a
+					// better data structure for storing all of this, e.g. store
+					// children in the watch. I have some code for this in my
+					// kqueue refactor we can use in the future. For now I'm
+					// okay with this as it's not publicly available.
+					// Correctness first, performance second.
+					if ev.renamedFrom != "" {
+						w.watches.mu.Lock()
+						for k, ww := range w.watches.wd {
+							if k == watch.wd || ww.path == ev.Name {
+								continue
+							}
+							if strings.HasPrefix(ww.path, ev.renamedFrom) {
+								ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1)
+								w.watches.wd[k] = ww
+							}
+						}
+						w.watches.mu.Unlock()
+					}
 				}
 			}
 
-			// Move to the next event in the buffer
-			offset += unix.SizeofInotifyEvent + nameLen
+			/// Send the events that are not ignored on the events channel
+			if !w.sendEvent(ev) {
+				return
+			}
+			next()
 		}
 	}
 }
 
-// newEvent returns an platform-independent Event based on an inotify mask.
-func (w *Watcher) newEvent(name string, mask uint32) Event {
+func (w *inotify) isRecursive(path string) bool {
+	ww := w.watches.byPath(path)
+	if ww == nil { // path could be a file, so also check the Dir.
+		ww = w.watches.byPath(filepath.Dir(path))
+	}
+	return ww != nil && ww.recurse
+}
+
+func (w *inotify) newEvent(name string, mask, cookie 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
@@ -584,11 +601,58 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
 	if mask&unix.IN_MODIFY == unix.IN_MODIFY {
 		e.Op |= Write
 	}
+	if mask&unix.IN_OPEN == unix.IN_OPEN {
+		e.Op |= xUnportableOpen
+	}
+	if mask&unix.IN_ACCESS == unix.IN_ACCESS {
+		e.Op |= xUnportableRead
+	}
+	if mask&unix.IN_CLOSE_WRITE == unix.IN_CLOSE_WRITE {
+		e.Op |= xUnportableCloseWrite
+	}
+	if mask&unix.IN_CLOSE_NOWRITE == unix.IN_CLOSE_NOWRITE {
+		e.Op |= xUnportableCloseRead
+	}
 	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
 	}
+
+	if cookie != 0 {
+		if mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
+			w.cookiesMu.Lock()
+			w.cookies[w.cookieIndex] = koekje{cookie: cookie, path: e.Name}
+			w.cookieIndex++
+			if w.cookieIndex > 9 {
+				w.cookieIndex = 0
+			}
+			w.cookiesMu.Unlock()
+		} else if mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
+			w.cookiesMu.Lock()
+			var prev string
+			for _, c := range w.cookies {
+				if c.cookie == cookie {
+					prev = c.path
+					break
+				}
+			}
+			w.cookiesMu.Unlock()
+			e.renamedFrom = prev
+		}
+	}
 	return e
 }
+
+func (w *inotify) xSupports(op Op) bool {
+	return true // Supports everything.
+}
+
+func (w *inotify) state() {
+	w.watches.mu.Lock()
+	defer w.watches.mu.Unlock()
+	for wd, ww := range w.watches.wd {
+		fmt.Fprintf(os.Stderr, "%4d: recurse=%t %q\n", wd, ww.recurse, ww.path)
+	}
+}
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
index 063a0915a07acbb36c5f2f127d9cdbcab89620fc..d8de5ab76fdd0003efad540a2260e2ef0c4982ac 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
@@ -1,8 +1,4 @@
 //go:build freebsd || openbsd || netbsd || dragonfly || darwin
-// +build freebsd openbsd netbsd dragonfly darwin
-
-// Note: the documentation on the Watcher type and methods is generated from
-// mkdoc.zsh
 
 package fsnotify
 
@@ -11,174 +7,195 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
+	"runtime"
 	"sync"
+	"time"
 
+	"github.com/fsnotify/fsnotify/internal"
 	"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.
-//
-// # Windows notes
-//
-// Paths can be added as "C:\path\to\dir", but forward slashes
-// ("C:/path/to/dir") will also work.
-//
-// When a watched directory is removed it will always send an event for the
-// directory itself, but may not send events for all files in that directory.
-// Sometimes it will send events for all times, sometimes it will send no
-// events, and often only for some files.
-//
-// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
-// value that is guaranteed to work with SMB filesystems. If you have many
-// events in quick succession this may not be enough, and you will have to use
-// [WithBufferSize] to increase the value.
-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, and you may
-	//                      want to wait until you've stopped receiving them
-	//                      (see the dedup example in cmd/fsnotify).
-	//
-	//                      Some systems may send Write event for directories
-	//                      when the directory content changes.
-	//
-	//   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
-	//                      when a file is truncated. On Windows it's never
-	//                      sent.
+type kqueue struct {
 	Events chan Event
-
-	// Errors sends any errors.
-	//
-	// ErrEventOverflow is used to indicate there are too many events:
-	//
-	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
-	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
-	//  - kqueue, fen:  Not used.
 	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
+	kq        int    // File descriptor (as returned by the kqueue() syscall).
+	closepipe [2]int // Pipe used for closing kq.
+	watches   *watches
+	done      chan struct{}
+	doneMu    sync.Mutex
 }
 
-type pathInfo struct {
-	name  string
-	isDir bool
+type (
+	watches struct {
+		mu     sync.RWMutex
+		wd     map[int]watch               // wd → watch
+		path   map[string]int              // pathname → wd
+		byDir  map[string]map[int]struct{} // dirname(path) → wd
+		seen   map[string]struct{}         // Keep track of if we know this file exists.
+		byUser map[string]struct{}         // Watches added with Watcher.Add()
+	}
+	watch struct {
+		wd       int
+		name     string
+		linkName string // In case of links; name is the target, and this is the link.
+		isDir    bool
+		dirFlags uint32
+	}
+)
+
+func newWatches() *watches {
+	return &watches{
+		wd:     make(map[int]watch),
+		path:   make(map[string]int),
+		byDir:  make(map[string]map[int]struct{}),
+		seen:   make(map[string]struct{}),
+		byUser: make(map[string]struct{}),
+	}
 }
 
-// NewWatcher creates a new Watcher.
-func NewWatcher() (*Watcher, error) {
-	return NewBufferedWatcher(0)
+func (w *watches) listPaths(userOnly bool) []string {
+	w.mu.RLock()
+	defer w.mu.RUnlock()
+
+	if userOnly {
+		l := make([]string, 0, len(w.byUser))
+		for p := range w.byUser {
+			l = append(l, p)
+		}
+		return l
+	}
+
+	l := make([]string, 0, len(w.path))
+	for p := range w.path {
+		l = append(l, p)
+	}
+	return l
 }
 
-// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
-// channel.
-//
-// The main use case for this is situations with a very large number of events
-// where the kernel buffer size can't be increased (e.g. due to lack of
-// permissions). An unbuffered Watcher will perform better for almost all use
-// cases, and whenever possible you will be better off increasing the kernel
-// buffers instead of adding a large userspace buffer.
-func NewBufferedWatcher(sz uint) (*Watcher, error) {
+func (w *watches) watchesInDir(path string) []string {
+	w.mu.RLock()
+	defer w.mu.RUnlock()
+
+	l := make([]string, 0, 4)
+	for fd := range w.byDir[path] {
+		info := w.wd[fd]
+		if _, ok := w.byUser[info.name]; !ok {
+			l = append(l, info.name)
+		}
+	}
+	return l
+}
+
+// Mark path as added by the user.
+func (w *watches) addUserWatch(path string) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	w.byUser[path] = struct{}{}
+}
+
+func (w *watches) addLink(path string, fd int) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	w.path[path] = fd
+	w.seen[path] = struct{}{}
+}
+
+func (w *watches) add(path, linkPath string, fd int, isDir bool) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	w.path[path] = fd
+	w.wd[fd] = watch{wd: fd, name: path, linkName: linkPath, isDir: isDir}
+
+	parent := filepath.Dir(path)
+	byDir, ok := w.byDir[parent]
+	if !ok {
+		byDir = make(map[int]struct{}, 1)
+		w.byDir[parent] = byDir
+	}
+	byDir[fd] = struct{}{}
+}
+
+func (w *watches) byWd(fd int) (watch, bool) {
+	w.mu.RLock()
+	defer w.mu.RUnlock()
+	info, ok := w.wd[fd]
+	return info, ok
+}
+
+func (w *watches) byPath(path string) (watch, bool) {
+	w.mu.RLock()
+	defer w.mu.RUnlock()
+	info, ok := w.wd[w.path[path]]
+	return info, ok
+}
+
+func (w *watches) updateDirFlags(path string, flags uint32) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	fd := w.path[path]
+	info := w.wd[fd]
+	info.dirFlags = flags
+	w.wd[fd] = info
+}
+
+func (w *watches) remove(fd int, path string) bool {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+
+	isDir := w.wd[fd].isDir
+	delete(w.path, path)
+	delete(w.byUser, path)
+
+	parent := filepath.Dir(path)
+	delete(w.byDir[parent], fd)
+
+	if len(w.byDir[parent]) == 0 {
+		delete(w.byDir, parent)
+	}
+
+	delete(w.wd, fd)
+	delete(w.seen, path)
+	return isDir
+}
+
+func (w *watches) markSeen(path string, exists bool) {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	if exists {
+		w.seen[path] = struct{}{}
+	} else {
+		delete(w.seen, path)
+	}
+}
+
+func (w *watches) seenBefore(path string) bool {
+	w.mu.RLock()
+	defer w.mu.RUnlock()
+	_, ok := w.seen[path]
+	return ok
+}
+
+func newBackend(ev chan Event, errs chan error) (backend, error) {
+	return newBufferedBackend(0, ev, errs)
+}
+
+func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, 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, sz),
-		Errors:       make(chan error),
-		done:         make(chan struct{}),
+	w := &kqueue{
+		Events:    ev,
+		Errors:    errs,
+		kq:        kq,
+		closepipe: closepipe,
+		done:      make(chan struct{}),
+		watches:   newWatches(),
 	}
 
 	go w.readEvents()
@@ -203,6 +220,8 @@ func newKqueue() (kq int, closepipe [2]int, err error) {
 		unix.Close(kq)
 		return kq, closepipe, err
 	}
+	unix.CloseOnExec(closepipe[0])
+	unix.CloseOnExec(closepipe[1])
 
 	// Register changes to listen on the closepipe.
 	changes := make([]unix.Kevent_t, 1)
@@ -221,166 +240,108 @@ func newKqueue() (kq int, closepipe [2]int, err error) {
 }
 
 // Returns true if the event was sent, or false if watcher is closed.
-func (w *Watcher) sendEvent(e Event) bool {
+func (w *kqueue) sendEvent(e Event) bool {
 	select {
-	case w.Events <- e:
-		return true
 	case <-w.done:
 		return false
+	case w.Events <- e:
+		return true
 	}
 }
 
 // Returns true if the error was sent, or false if watcher is closed.
-func (w *Watcher) sendError(err error) bool {
+func (w *kqueue) sendError(err error) bool {
+	if err == nil {
+		return true
+	}
 	select {
+	case <-w.done:
+		return false
 	case w.Errors <- err:
 		return true
+	}
+}
+
+func (w *kqueue) 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()
+func (w *kqueue) Close() error {
+	w.doneMu.Lock()
+	if w.isClosed() {
+		w.doneMu.Unlock()
 		return nil
 	}
-	w.isClosed = true
+	close(w.done)
+	w.doneMu.Unlock()
 
-	// 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
+	pathsToRemove := w.watches.listPaths(false)
 	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; watching it more than once is a no-op and will
-// not return an error. Paths that do not yet exist on the filesystem cannot be
-// watched.
-//
-// A watch will be automatically removed if the watched path is deleted or
-// renamed. The exception is the Windows backend, which doesn't remove the
-// watcher on renames.
-//
-// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
-// filesystems (/proc, /sys, etc.) generally don't work.
-//
-// Returns [ErrClosed] if [Watcher.Close] was called.
-//
-// See [Watcher.AddWith] for a version that allows adding options.
-//
-// # 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 programs (especially editors) update files atomically: it
-// will write to a temporary file which is then moved to to destination,
-// overwriting the original (or some variant thereof). The watcher on the
-// original file is now lost, as that no longer exists.
-//
-// The upshot of this is that a power failure or crash won't leave a
-// half-written file.
-//
-// 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 w.AddWith(name) }
+func (w *kqueue) Add(name string) error { return w.AddWith(name) }
 
-// AddWith is like [Watcher.Add], but allows adding options. When using Add()
-// the defaults described below are used.
-//
-// Possible options are:
-//
-//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
-//     other platforms. The default is 64K (65536 bytes).
-func (w *Watcher) AddWith(name string, opts ...addOpt) error {
-	_ = getOptions(opts...)
+func (w *kqueue) AddWith(name string, opts ...addOpt) error {
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  AddWith(%q)\n",
+			time.Now().Format("15:04:05.000000000"), name)
+	}
+
+	with := getOptions(opts...)
+	if !w.xSupports(with.op) {
+		return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
+	}
 
-	w.mu.Lock()
-	w.userWatches[name] = struct{}{}
-	w.mu.Unlock()
 	_, err := w.addWatch(name, noteAllEvents)
-	return err
+	if err != nil {
+		return err
+	}
+	w.watches.addUserWatch(name)
+	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].
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) Remove(name string) error {
+func (w *kqueue) Remove(name string) error {
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  Remove(%q)\n",
+			time.Now().Format("15:04:05.000000000"), name)
+	}
 	return w.remove(name, true)
 }
 
-func (w *Watcher) remove(name string, unwatchFiles bool) error {
-	name = filepath.Clean(name)
-	w.mu.Lock()
-	if w.isClosed {
-		w.mu.Unlock()
+func (w *kqueue) remove(name string, unwatchFiles bool) error {
+	if w.isClosed() {
 		return nil
 	}
-	watchfd, ok := w.watches[name]
-	w.mu.Unlock()
+
+	name = filepath.Clean(name)
+	info, ok := w.watches.byPath(name)
 	if !ok {
 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name)
 	}
 
-	err := w.register([]int{watchfd}, unix.EV_DELETE, 0)
+	err := w.register([]int{info.wd}, 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)
-	}
+	unix.Close(info.wd)
 
-	delete(w.paths, watchfd)
-	delete(w.dirFlags, name)
-	delete(w.fileExists, name)
-	w.mu.Unlock()
+	isDir := w.watches.remove(info.wd, name)
 
 	// Find all watched paths that are in this directory that are not external.
 	if unwatchFiles && 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()
+		pathsToRemove := w.watches.watchesInDir(name)
 		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
@@ -391,23 +352,11 @@ func (w *Watcher) remove(name string, unwatchFiles bool) error {
 	return nil
 }
 
-// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
-// yet removed).
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) WatchList() []string {
-	w.mu.Lock()
-	defer w.mu.Unlock()
-	if w.isClosed {
+func (w *kqueue) WatchList() []string {
+	if w.isClosed() {
 		return nil
 	}
-
-	entries := make([]string, 0, len(w.userWatches))
-	for pathname := range w.userWatches {
-		entries = append(entries, pathname)
-	}
-
-	return entries
+	return w.watches.listPaths(true)
 }
 
 // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE)
@@ -417,34 +366,26 @@ const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | un
 // described in kevent(2).
 //
 // Returns the real path to the file which was added, with symlinks resolved.
-func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
-	var isDir bool
-	name = filepath.Clean(name)
-
-	w.mu.Lock()
-	if w.isClosed {
-		w.mu.Unlock()
+func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
+	if w.isClosed() {
 		return "", ErrClosed
 	}
-	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()
 
+	name = filepath.Clean(name)
+
+	info, alreadyWatching := w.watches.byPath(name)
 	if !alreadyWatching {
 		fi, err := os.Lstat(name)
 		if err != nil {
 			return "", err
 		}
 
-		// Don't watch sockets or named pipes
+		// Don't watch sockets or named pipes.
 		if (fi.Mode()&os.ModeSocket == os.ModeSocket) || (fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe) {
 			return "", nil
 		}
 
-		// Follow Symlinks.
+		// Follow symlinks.
 		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
 			link, err := os.Readlink(name)
 			if err != nil {
@@ -455,18 +396,15 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 				return "", nil
 			}
 
-			w.mu.Lock()
-			_, alreadyWatching = w.watches[link]
-			w.mu.Unlock()
-
+			_, alreadyWatching = w.watches.byPath(link)
 			if alreadyWatching {
 				// Add to watches so we don't get spurious Create events later
 				// on when we diff the directories.
-				w.watches[name] = 0
-				w.fileExists[name] = struct{}{}
+				w.watches.addLink(name, 0)
 				return link, nil
 			}
 
+			info.linkName = name
 			name = link
 			fi, err = os.Lstat(name)
 			if err != nil {
@@ -477,7 +415,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 		// 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)
+			info.wd, err = unix.Open(name, openMode, 0)
 			if err == nil {
 				break
 			}
@@ -488,40 +426,25 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 			return "", err
 		}
 
-		isDir = fi.IsDir()
+		info.isDir = fi.IsDir()
 	}
 
-	err := w.register([]int{watchfd}, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE, flags)
+	err := w.register([]int{info.wd}, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE, flags)
 	if err != nil {
-		unix.Close(watchfd)
+		unix.Close(info.wd)
 		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()
+		w.watches.add(name, info.linkName, info.wd, info.isDir)
 	}
 
-	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()
-
+	// Watch the directory if it has not been watched before, or if it was
+	// watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles)
+	if info.isDir {
 		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()
+			(!alreadyWatching || (info.dirFlags&unix.NOTE_WRITE) != unix.NOTE_WRITE)
+		w.watches.updateDirFlags(name, flags)
 
 		if watchDir {
 			if err := w.watchDirectoryFiles(name); err != nil {
@@ -534,7 +457,7 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
 
 // readEvents reads from kqueue and converts the received kevents into
 // Event values that it sends down the Events channel.
-func (w *Watcher) readEvents() {
+func (w *kqueue) readEvents() {
 	defer func() {
 		close(w.Events)
 		close(w.Errors)
@@ -543,50 +466,65 @@ func (w *Watcher) readEvents() {
 	}()
 
 	eventBuffer := make([]unix.Kevent_t, 10)
-	for closed := false; !closed; {
+	for {
 		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
+				return
 			}
-			continue
 		}
 
-		// Flush the events we received to the Events channel
 		for _, kevent := range kevents {
 			var (
-				watchfd = int(kevent.Ident)
-				mask    = uint32(kevent.Fflags)
+				wd   = 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
+			if wd == w.closepipe[0] {
+				return
 			}
 
-			w.mu.Lock()
-			path := w.paths[watchfd]
-			w.mu.Unlock()
+			path, ok := w.watches.byWd(wd)
+			if debug {
+				internal.Debug(path.name, &kevent)
+			}
 
-			event := w.newEvent(path.name, mask)
+			// On macOS it seems that sometimes an event with Ident=0 is
+			// delivered, and no other flags/information beyond that, even
+			// though we never saw such a file descriptor. For example in
+			// TestWatchSymlink/277 (usually at the end, but sometimes sooner):
+			//
+			// fmt.Printf("READ: %2d  %#v\n", kevent.Ident, kevent)
+			// unix.Kevent_t{Ident:0x2a, Filter:-4, Flags:0x25, Fflags:0x2, Data:0, Udata:(*uint8)(nil)}
+			// unix.Kevent_t{Ident:0x0,  Filter:-4, Flags:0x25, Fflags:0x2, Data:0, Udata:(*uint8)(nil)}
+			//
+			// The first is a normal event, the second with Ident 0. No error
+			// flag, no data, no ... nothing.
+			//
+			// I read a bit through bsd/kern_event.c from the xnu source, but I
+			// don't really see an obvious location where this is triggered –
+			// this doesn't seem intentional, but idk...
+			//
+			// Technically fd 0 is a valid descriptor, so only skip it if
+			// there's no path, and if we're on macOS.
+			if !ok && kevent.Ident == 0 && runtime.GOOS == "darwin" {
+				continue
+			}
+
+			event := w.newEvent(path.name, path.linkName, mask)
 
 			if event.Has(Rename) || event.Has(Remove) {
 				w.remove(event.Name, false)
-				w.mu.Lock()
-				delete(w.fileExists, event.Name)
-				w.mu.Unlock()
+				w.watches.markSeen(event.Name, false)
 			}
 
 			if path.isDir && event.Has(Write) && !event.Has(Remove) {
-				w.sendDirectoryChangeEvents(event.Name)
-			} else {
-				if !w.sendEvent(event) {
-					closed = true
-					continue
-				}
+				w.dirChange(event.Name)
+			} else if !w.sendEvent(event) {
+				return
 			}
 
 			if event.Has(Remove) {
@@ -594,25 +532,34 @@ func (w *Watcher) readEvents() {
 				// 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()
+					_, found := w.watches.byPath(fileDir)
 					if found {
-						err := w.sendDirectoryChangeEvents(fileDir)
-						if err != nil {
-							if !w.sendError(err) {
-								closed = true
-							}
+						// TODO: this branch is never triggered in any test.
+						// Added in d6220df (2012).
+						// isDir check added in 8611c35 (2016): https://github.com/fsnotify/fsnotify/pull/111
+						//
+						// I don't really get how this can be triggered either.
+						// And it wasn't triggered in the patch that added it,
+						// either.
+						//
+						// Original also had a comment:
+						//   make sure the directory exists before we watch for
+						//   changes. When we do a recursive watch and perform
+						//   rm -rf, the parent directory might have gone
+						//   missing, ignore the missing directory and let the
+						//   upcoming delete event remove the watch from the
+						//   parent directory.
+						err := w.dirChange(fileDir)
+						if !w.sendError(err) {
+							return
 						}
 					}
 				} else {
-					filePath := filepath.Clean(event.Name)
-					if fi, err := os.Lstat(filePath); err == nil {
-						err := w.sendFileCreatedEventIfNew(filePath, fi)
-						if err != nil {
-							if !w.sendError(err) {
-								closed = true
-							}
+					path := filepath.Clean(event.Name)
+					if fi, err := os.Lstat(path); err == nil {
+						err := w.sendCreateIfNew(path, fi)
+						if !w.sendError(err) {
+							return
 						}
 					}
 				}
@@ -622,8 +569,14 @@ func (w *Watcher) readEvents() {
 }
 
 // newEvent returns an platform-independent Event based on kqueue Fflags.
-func (w *Watcher) newEvent(name string, mask uint32) Event {
+func (w *kqueue) newEvent(name, linkName string, mask uint32) Event {
 	e := Event{Name: name}
+	if linkName != "" {
+		// If the user watched "/path/link" then emit events as "/path/link"
+		// rather than "/path/target".
+		e.Name = linkName
+	}
+
 	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE {
 		e.Op |= Remove
 	}
@@ -645,8 +598,7 @@ func (w *Watcher) newEvent(name string, mask uint32) Event {
 }
 
 // watchDirectoryFiles to mimic inotify when adding a watch on a directory
-func (w *Watcher) watchDirectoryFiles(dirPath string) error {
-	// Get all files
+func (w *kqueue) watchDirectoryFiles(dirPath string) error {
 	files, err := os.ReadDir(dirPath)
 	if err != nil {
 		return err
@@ -674,9 +626,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 			}
 		}
 
-		w.mu.Lock()
-		w.fileExists[cleanPath] = struct{}{}
-		w.mu.Unlock()
+		w.watches.markSeen(cleanPath, true)
 	}
 
 	return nil
@@ -686,7 +636,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
 //
 // 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) error {
+func (w *kqueue) dirChange(dir string) error {
 	files, err := os.ReadDir(dir)
 	if err != nil {
 		// Directory no longer exists: we can ignore this safely. kqueue will
@@ -694,61 +644,51 @@ func (w *Watcher) sendDirectoryChangeEvents(dir string) error {
 		if errors.Is(err, os.ErrNotExist) {
 			return nil
 		}
-		return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
+		return fmt.Errorf("fsnotify.dirChange: %w", err)
 	}
 
 	for _, f := range files {
 		fi, err := f.Info()
 		if err != nil {
-			return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
+			return fmt.Errorf("fsnotify.dirChange: %w", err)
 		}
 
-		err = w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi)
+		err = w.sendCreateIfNew(filepath.Join(dir, fi.Name()), fi)
 		if err != nil {
 			// Don't need to send an error if this file isn't readable.
 			if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
 				return nil
 			}
-			return fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)
+			return fmt.Errorf("fsnotify.dirChange: %w", err)
 		}
 	}
 	return nil
 }
 
-// sendFileCreatedEvent sends a create event if the file isn't already being tracked.
-func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fi 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
+// Send a create event if the file isn't already being tracked, and start
+// watching this file.
+func (w *kqueue) sendCreateIfNew(path string, fi os.FileInfo) error {
+	if !w.watches.seenBefore(path) {
+		if !w.sendEvent(Event{Name: path, Op: Create}) {
+			return nil
 		}
 	}
 
-	// like watchDirectoryFiles (but without doing another ReadDir)
-	filePath, err = w.internalWatch(filePath, fi)
+	// Like watchDirectoryFiles, but without doing another ReadDir.
+	path, err := w.internalWatch(path, fi)
 	if err != nil {
 		return err
 	}
-
-	w.mu.Lock()
-	w.fileExists[filePath] = struct{}{}
-	w.mu.Unlock()
-
+	w.watches.markSeen(path, true)
 	return nil
 }
 
-func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
+func (w *kqueue) internalWatch(name string, fi os.FileInfo) (string, error) {
 	if fi.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)
+		info, _ := w.watches.byPath(name)
+		return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME)
 	}
 
 	// watch file to mimic Linux inotify
@@ -756,7 +696,7 @@ func (w *Watcher) internalWatch(name string, fi os.FileInfo) (string, error) {
 }
 
 // Register events with the queue.
-func (w *Watcher) register(fds []int, flags int, fflags uint32) error {
+func (w *kqueue) 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.
@@ -773,10 +713,21 @@ func (w *Watcher) register(fds []int, flags int, fflags uint32) error {
 }
 
 // read retrieves pending events, or waits until an event occurs.
-func (w *Watcher) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) {
+func (w *kqueue) 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
 }
+
+func (w *kqueue) xSupports(op Op) bool {
+	if runtime.GOOS == "freebsd" {
+		//return true // Supports everything.
+	}
+	if op.Has(xUnportableOpen) || op.Has(xUnportableRead) ||
+		op.Has(xUnportableCloseWrite) || op.Has(xUnportableCloseRead) {
+		return false
+	}
+	return true
+}
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_other.go b/vendor/github.com/fsnotify/fsnotify/backend_other.go
index d34a23c015f8c8ac7be53276b1f2dd706d3c9dea..5eb5dbc66f264b5ec9b92d6e960ebedde1cbf79f 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_other.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_other.go
@@ -1,205 +1,23 @@
 //go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
-// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
-
-// Note: the documentation on the Watcher type and methods is generated from
-// mkdoc.zsh
 
 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.
-//
-// # Windows notes
-//
-// Paths can be added as "C:\path\to\dir", but forward slashes
-// ("C:/path/to/dir") will also work.
-//
-// When a watched directory is removed it will always send an event for the
-// directory itself, but may not send events for all files in that directory.
-// Sometimes it will send events for all times, sometimes it will send no
-// events, and often only for some files.
-//
-// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
-// value that is guaranteed to work with SMB filesystems. If you have many
-// events in quick succession this may not be enough, and you will have to use
-// [WithBufferSize] to increase the value.
-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, and you may
-	//                      want to wait until you've stopped receiving them
-	//                      (see the dedup example in cmd/fsnotify).
-	//
-	//                      Some systems may send Write event for directories
-	//                      when the directory content changes.
-	//
-	//   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
-	//                      when a file is truncated. On Windows it's never
-	//                      sent.
+type other struct {
 	Events chan Event
-
-	// Errors sends any errors.
-	//
-	// ErrEventOverflow is used to indicate there are too many events:
-	//
-	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
-	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
-	//  - kqueue, fen:  Not used.
 	Errors chan error
 }
 
-// NewWatcher creates a new Watcher.
-func NewWatcher() (*Watcher, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
 	return nil, errors.New("fsnotify not supported on the current platform")
 }
-
-// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
-// channel.
-//
-// The main use case for this is situations with a very large number of events
-// where the kernel buffer size can't be increased (e.g. due to lack of
-// permissions). An unbuffered Watcher will perform better for almost all use
-// cases, and whenever possible you will be better off increasing the kernel
-// buffers instead of adding a large userspace buffer.
-func NewBufferedWatcher(sz uint) (*Watcher, error) { return NewWatcher() }
-
-// Close removes all watches and closes the Events channel.
-func (w *Watcher) Close() error { return nil }
-
-// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
-// yet removed).
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) WatchList() []string { return nil }
-
-// Add starts monitoring the path for changes.
-//
-// A path can only be watched once; watching it more than once is a no-op and will
-// not return an error. Paths that do not yet exist on the filesystem cannot be
-// watched.
-//
-// A watch will be automatically removed if the watched path is deleted or
-// renamed. The exception is the Windows backend, which doesn't remove the
-// watcher on renames.
-//
-// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
-// filesystems (/proc, /sys, etc.) generally don't work.
-//
-// Returns [ErrClosed] if [Watcher.Close] was called.
-//
-// See [Watcher.AddWith] for a version that allows adding options.
-//
-// # 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 programs (especially editors) update files atomically: it
-// will write to a temporary file which is then moved to to destination,
-// overwriting the original (or some variant thereof). The watcher on the
-// original file is now lost, as that no longer exists.
-//
-// The upshot of this is that a power failure or crash won't leave a
-// half-written file.
-//
-// 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 }
-
-// AddWith is like [Watcher.Add], but allows adding options. When using Add()
-// the defaults described below are used.
-//
-// Possible options are:
-//
-//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
-//     other platforms. The default is 64K (65536 bytes).
-func (w *Watcher) AddWith(name string, opts ...addOpt) 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].
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) Remove(name string) error { return nil }
+func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+	return newBackend(ev, errs)
+}
+func (w *other) Close() error                              { return nil }
+func (w *other) WatchList() []string                       { return nil }
+func (w *other) Add(name string) error                     { return nil }
+func (w *other) AddWith(name string, opts ...addOpt) error { return nil }
+func (w *other) Remove(name string) error                  { return nil }
+func (w *other) xSupports(op Op) bool                      { return false }
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go
index 9bc91e5d613f55e4dc8e654501ad19479db2e34d..c54a630838358c5fd0c596f4e54335b15805ca83 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_windows.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go
@@ -1,12 +1,8 @@
 //go:build windows
-// +build windows
 
 // Windows backend based on ReadDirectoryChangesW()
 //
 // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw
-//
-// Note: the documentation on the Watcher type and methods is generated from
-// mkdoc.zsh
 
 package fsnotify
 
@@ -19,123 +15,15 @@ import (
 	"runtime"
 	"strings"
 	"sync"
+	"time"
 	"unsafe"
 
+	"github.com/fsnotify/fsnotify/internal"
 	"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.
-//
-// # Windows notes
-//
-// Paths can be added as "C:\path\to\dir", but forward slashes
-// ("C:/path/to/dir") will also work.
-//
-// When a watched directory is removed it will always send an event for the
-// directory itself, but may not send events for all files in that directory.
-// Sometimes it will send events for all times, sometimes it will send no
-// events, and often only for some files.
-//
-// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
-// value that is guaranteed to work with SMB filesystems. If you have many
-// events in quick succession this may not be enough, and you will have to use
-// [WithBufferSize] to increase the value.
-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, and you may
-	//                      want to wait until you've stopped receiving them
-	//                      (see the dedup example in cmd/fsnotify).
-	//
-	//                      Some systems may send Write event for directories
-	//                      when the directory content changes.
-	//
-	//   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
-	//                      when a file is truncated. On Windows it's never
-	//                      sent.
+type readDirChangesW struct {
 	Events chan Event
-
-	// Errors sends any errors.
-	//
-	// ErrEventOverflow is used to indicate there are too many events:
-	//
-	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
-	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
-	//  - kqueue, fen:  Not used.
 	Errors chan error
 
 	port  windows.Handle // Handle to completion port
@@ -147,48 +35,40 @@ type Watcher struct {
 	closed  bool       // Set to true when Close() is first called
 }
 
-// NewWatcher creates a new Watcher.
-func NewWatcher() (*Watcher, error) {
-	return NewBufferedWatcher(50)
+func newBackend(ev chan Event, errs chan error) (backend, error) {
+	return newBufferedBackend(50, ev, errs)
 }
 
-// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
-// channel.
-//
-// The main use case for this is situations with a very large number of events
-// where the kernel buffer size can't be increased (e.g. due to lack of
-// permissions). An unbuffered Watcher will perform better for almost all use
-// cases, and whenever possible you will be better off increasing the kernel
-// buffers instead of adding a large userspace buffer.
-func NewBufferedWatcher(sz uint) (*Watcher, error) {
+func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
 	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
 	if err != nil {
 		return nil, os.NewSyscallError("CreateIoCompletionPort", err)
 	}
-	w := &Watcher{
+	w := &readDirChangesW{
+		Events:  ev,
+		Errors:  errs,
 		port:    port,
 		watches: make(watchMap),
 		input:   make(chan *input, 1),
-		Events:  make(chan Event, sz),
-		Errors:  make(chan error),
 		quit:    make(chan chan<- error, 1),
 	}
 	go w.readEvents()
 	return w, nil
 }
 
-func (w *Watcher) isClosed() bool {
+func (w *readDirChangesW) isClosed() bool {
 	w.mu.Lock()
 	defer w.mu.Unlock()
 	return w.closed
 }
 
-func (w *Watcher) sendEvent(name string, mask uint64) bool {
+func (w *readDirChangesW) sendEvent(name, renamedFrom string, mask uint64) bool {
 	if mask == 0 {
 		return false
 	}
 
 	event := w.newEvent(name, uint32(mask))
+	event.renamedFrom = renamedFrom
 	select {
 	case ch := <-w.quit:
 		w.quit <- ch
@@ -198,17 +78,19 @@ func (w *Watcher) sendEvent(name string, mask uint64) bool {
 }
 
 // Returns true if the error was sent, or false if watcher is closed.
-func (w *Watcher) sendError(err error) bool {
+func (w *readDirChangesW) sendError(err error) bool {
+	if err == nil {
+		return true
+	}
 	select {
 	case w.Errors <- err:
 		return true
 	case <-w.quit:
+		return false
 	}
-	return false
 }
 
-// Close removes all watches and closes the Events channel.
-func (w *Watcher) Close() error {
+func (w *readDirChangesW) Close() error {
 	if w.isClosed() {
 		return nil
 	}
@@ -226,57 +108,21 @@ func (w *Watcher) Close() error {
 	return <-ch
 }
 
-// Add starts monitoring the path for changes.
-//
-// A path can only be watched once; watching it more than once is a no-op and will
-// not return an error. Paths that do not yet exist on the filesystem cannot be
-// watched.
-//
-// A watch will be automatically removed if the watched path is deleted or
-// renamed. The exception is the Windows backend, which doesn't remove the
-// watcher on renames.
-//
-// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
-// filesystems (/proc, /sys, etc.) generally don't work.
-//
-// Returns [ErrClosed] if [Watcher.Close] was called.
-//
-// See [Watcher.AddWith] for a version that allows adding options.
-//
-// # 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 programs (especially editors) update files atomically: it
-// will write to a temporary file which is then moved to to destination,
-// overwriting the original (or some variant thereof). The watcher on the
-// original file is now lost, as that no longer exists.
-//
-// The upshot of this is that a power failure or crash won't leave a
-// half-written file.
-//
-// 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 w.AddWith(name) }
+func (w *readDirChangesW) Add(name string) error { return w.AddWith(name) }
 
-// AddWith is like [Watcher.Add], but allows adding options. When using Add()
-// the defaults described below are used.
-//
-// Possible options are:
-//
-//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
-//     other platforms. The default is 64K (65536 bytes).
-func (w *Watcher) AddWith(name string, opts ...addOpt) error {
+func (w *readDirChangesW) AddWith(name string, opts ...addOpt) error {
 	if w.isClosed() {
 		return ErrClosed
 	}
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  AddWith(%q)\n",
+			time.Now().Format("15:04:05.000000000"), filepath.ToSlash(name))
+	}
 
 	with := getOptions(opts...)
+	if !w.xSupports(with.op) {
+		return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
+	}
 	if with.bufsize < 4096 {
 		return fmt.Errorf("fsnotify.WithBufferSize: buffer size cannot be smaller than 4096 bytes")
 	}
@@ -295,18 +141,14 @@ func (w *Watcher) AddWith(name string, opts ...addOpt) error {
 	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].
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) Remove(name string) error {
+func (w *readDirChangesW) Remove(name string) error {
 	if w.isClosed() {
 		return nil
 	}
+	if debug {
+		fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s  Remove(%q)\n",
+			time.Now().Format("15:04:05.000000000"), filepath.ToSlash(name))
+	}
 
 	in := &input{
 		op:    opRemoveWatch,
@@ -320,11 +162,7 @@ func (w *Watcher) Remove(name string) error {
 	return <-in.reply
 }
 
-// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
-// yet removed).
-//
-// Returns nil if [Watcher.Close] was called.
-func (w *Watcher) WatchList() []string {
+func (w *readDirChangesW) WatchList() []string {
 	if w.isClosed() {
 		return nil
 	}
@@ -335,7 +173,13 @@ func (w *Watcher) WatchList() []string {
 	entries := make([]string, 0, len(w.watches))
 	for _, entry := range w.watches {
 		for _, watchEntry := range entry {
-			entries = append(entries, watchEntry.path)
+			for name := range watchEntry.names {
+				entries = append(entries, filepath.Join(watchEntry.path, name))
+			}
+			// the directory itself is being watched
+			if watchEntry.mask != 0 {
+				entries = append(entries, watchEntry.path)
+			}
 		}
 	}
 
@@ -361,7 +205,7 @@ const (
 	sysFSIGNORED    = 0x8000
 )
 
-func (w *Watcher) newEvent(name string, mask uint32) Event {
+func (w *readDirChangesW) newEvent(name string, mask uint32) Event {
 	e := Event{Name: name}
 	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO {
 		e.Op |= Create
@@ -417,7 +261,7 @@ type (
 	watchMap map[uint32]indexMap
 )
 
-func (w *Watcher) wakeupReader() error {
+func (w *readDirChangesW) wakeupReader() error {
 	err := windows.PostQueuedCompletionStatus(w.port, 0, 0, nil)
 	if err != nil {
 		return os.NewSyscallError("PostQueuedCompletionStatus", err)
@@ -425,7 +269,7 @@ func (w *Watcher) wakeupReader() error {
 	return nil
 }
 
-func (w *Watcher) getDir(pathname string) (dir string, err error) {
+func (w *readDirChangesW) getDir(pathname string) (dir string, err error) {
 	attr, err := windows.GetFileAttributes(windows.StringToUTF16Ptr(pathname))
 	if err != nil {
 		return "", os.NewSyscallError("GetFileAttributes", err)
@@ -439,7 +283,7 @@ func (w *Watcher) getDir(pathname string) (dir string, err error) {
 	return
 }
 
-func (w *Watcher) getIno(path string) (ino *inode, err error) {
+func (w *readDirChangesW) 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,
@@ -482,9 +326,8 @@ func (m watchMap) set(ino *inode, watch *watch) {
 }
 
 // Must run within the I/O thread.
-func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
-	//pathname, recurse := recursivePath(pathname)
-	recurse := false
+func (w *readDirChangesW) addWatch(pathname string, flags uint64, bufsize int) error {
+	pathname, recurse := recursivePath(pathname)
 
 	dir, err := w.getDir(pathname)
 	if err != nil {
@@ -538,7 +381,7 @@ func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
 }
 
 // Must run within the I/O thread.
-func (w *Watcher) remWatch(pathname string) error {
+func (w *readDirChangesW) remWatch(pathname string) error {
 	pathname, recurse := recursivePath(pathname)
 
 	dir, err := w.getDir(pathname)
@@ -566,11 +409,11 @@ func (w *Watcher) remWatch(pathname string) error {
 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname)
 	}
 	if pathname == dir {
-		w.sendEvent(watch.path, watch.mask&sysFSIGNORED)
+		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)
+		w.sendEvent(filepath.Join(watch.path, name), "", watch.names[name]&sysFSIGNORED)
 		delete(watch.names, name)
 	}
 
@@ -578,23 +421,23 @@ func (w *Watcher) remWatch(pathname string) error {
 }
 
 // Must run within the I/O thread.
-func (w *Watcher) deleteWatch(watch *watch) {
+func (w *readDirChangesW) deleteWatch(watch *watch) {
 	for name, mask := range watch.names {
 		if mask&provisional == 0 {
-			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED)
+			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)
+			w.sendEvent(watch.path, "", watch.mask&sysFSIGNORED)
 		}
 		watch.mask = 0
 	}
 }
 
 // Must run within the I/O thread.
-func (w *Watcher) startRead(watch *watch) error {
+func (w *readDirChangesW) startRead(watch *watch) error {
 	err := windows.CancelIo(watch.ino.handle)
 	if err != nil {
 		w.sendError(os.NewSyscallError("CancelIo", err))
@@ -624,7 +467,7 @@ func (w *Watcher) startRead(watch *watch) error {
 		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)
+			w.sendEvent(watch.path, "", watch.mask&sysFSDELETESELF)
 			err = nil
 		}
 		w.deleteWatch(watch)
@@ -637,7 +480,7 @@ func (w *Watcher) startRead(watch *watch) error {
 // 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() {
+func (w *readDirChangesW) readEvents() {
 	var (
 		n   uint32
 		key uintptr
@@ -700,7 +543,7 @@ func (w *Watcher) readEvents() {
 			}
 		case windows.ERROR_ACCESS_DENIED:
 			// Watched directory was probably removed
-			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF)
+			w.sendEvent(watch.path, "", watch.mask&sysFSDELETESELF)
 			w.deleteWatch(watch)
 			w.startRead(watch)
 			continue
@@ -733,6 +576,10 @@ func (w *Watcher) readEvents() {
 			name := windows.UTF16ToString(buf)
 			fullname := filepath.Join(watch.path, name)
 
+			if debug {
+				internal.Debug(fullname, raw.Action)
+			}
+
 			var mask uint64
 			switch raw.Action {
 			case windows.FILE_ACTION_REMOVED:
@@ -761,21 +608,22 @@ func (w *Watcher) readEvents() {
 				}
 			}
 
-			sendNameEvent := func() {
-				w.sendEvent(fullname, watch.names[name]&mask)
-			}
 			if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME {
-				sendNameEvent()
+				w.sendEvent(fullname, "", watch.names[name]&mask)
 			}
 			if raw.Action == windows.FILE_ACTION_REMOVED {
-				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED)
+				w.sendEvent(fullname, "", watch.names[name]&sysFSIGNORED)
 				delete(watch.names, name)
 			}
 
-			w.sendEvent(fullname, watch.mask&w.toFSnotifyFlags(raw.Action))
+			if watch.rename != "" && raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME {
+				w.sendEvent(fullname, filepath.Join(watch.path, watch.rename), watch.mask&w.toFSnotifyFlags(raw.Action))
+			} else {
+				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()
+				w.sendEvent(filepath.Join(watch.path, watch.rename), "", watch.names[name]&mask)
 			}
 
 			// Move to the next event in the buffer
@@ -787,8 +635,7 @@ func (w *Watcher) readEvents() {
 			// Error!
 			if offset >= n {
 				//lint:ignore ST1005 Windows should be capitalized
-				w.sendError(errors.New(
-					"Windows system assumed buffer larger than it is, events have likely been missed"))
+				w.sendError(errors.New("Windows system assumed buffer larger than it is, events have likely been missed"))
 				break
 			}
 		}
@@ -799,7 +646,7 @@ func (w *Watcher) readEvents() {
 	}
 }
 
-func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
+func (w *readDirChangesW) toWindowsFlags(mask uint64) uint32 {
 	var m uint32
 	if mask&sysFSMODIFY != 0 {
 		m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE
@@ -810,7 +657,7 @@ func (w *Watcher) toWindowsFlags(mask uint64) uint32 {
 	return m
 }
 
-func (w *Watcher) toFSnotifyFlags(action uint32) uint64 {
+func (w *readDirChangesW) toFSnotifyFlags(action uint32) uint64 {
 	switch action {
 	case windows.FILE_ACTION_ADDED:
 		return sysFSCREATE
@@ -825,3 +672,11 @@ func (w *Watcher) toFSnotifyFlags(action uint32) uint64 {
 	}
 	return 0
 }
+
+func (w *readDirChangesW) xSupports(op Op) bool {
+	if op.Has(xUnportableOpen) || op.Has(xUnportableRead) ||
+		op.Has(xUnportableCloseWrite) || op.Has(xUnportableCloseRead) {
+		return false
+	}
+	return true
+}
diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
index 24c99cc4999e9d429961b6c8caa12f30203abd43..0760efe91600fa4b7d428c66d5d5629a3df2ae77 100644
--- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
@@ -3,19 +3,146 @@
 //
 // Currently supported systems:
 //
-//	Linux 2.6.32+    via inotify
-//	BSD, macOS       via kqueue
-//	Windows          via ReadDirectoryChangesW
-//	illumos          via FEN
+//   - Linux      via inotify
+//   - BSD, macOS via kqueue
+//   - Windows    via ReadDirectoryChangesW
+//   - illumos    via FEN
+//
+// # FSNOTIFY_DEBUG
+//
+// Set the FSNOTIFY_DEBUG environment variable to "1" to print debug messages to
+// stderr. This can be useful to track down some problems, especially in cases
+// where fsnotify is used as an indirect dependency.
+//
+// Every event will be printed as soon as there's something useful to print,
+// with as little processing from fsnotify.
+//
+// Example output:
+//
+//	FSNOTIFY_DEBUG: 11:34:23.633087586   256:IN_CREATE            → "/tmp/file-1"
+//	FSNOTIFY_DEBUG: 11:34:23.633202319     4:IN_ATTRIB            → "/tmp/file-1"
+//	FSNOTIFY_DEBUG: 11:34:28.989728764   512:IN_DELETE            → "/tmp/file-1"
 package fsnotify
 
 import (
 	"errors"
 	"fmt"
+	"os"
 	"path/filepath"
 	"strings"
 )
 
+// 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.
+//
+// # Windows notes
+//
+// Paths can be added as "C:\\path\\to\\dir", but forward slashes
+// ("C:/path/to/dir") will also work.
+//
+// When a watched directory is removed it will always send an event for the
+// directory itself, but may not send events for all files in that directory.
+// Sometimes it will send events for all files, sometimes it will send no
+// events, and often only for some files.
+//
+// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
+// value that is guaranteed to work with SMB filesystems. If you have many
+// events in quick succession this may not be enough, and you will have to use
+// [WithBufferSize] to increase the value.
+type Watcher struct {
+	b backend
+
+	// 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, and you may
+	//                      want to wait until you've stopped receiving them
+	//                      (see the dedup example in cmd/fsnotify).
+	//
+	//                      Some systems may send Write event for directories
+	//                      when the directory content changes.
+	//
+	//   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
+	//                      when a file is truncated. On Windows it's never
+	//                      sent.
+	Events chan Event
+
+	// Errors sends any errors.
+	Errors chan error
+}
+
 // Event represents a file system notification.
 type Event struct {
 	// Path to the file or directory.
@@ -30,6 +157,16 @@ type Event struct {
 	// This is a bitmask and some systems may send multiple operations at once.
 	// Use the Event.Has() method instead of comparing with ==.
 	Op Op
+
+	// Create events will have this set to the old path if it's a rename. This
+	// only works when both the source and destination are watched. It's not
+	// reliable when watching individual files, only directories.
+	//
+	// For example "mv /tmp/file /tmp/rename" will emit:
+	//
+	//   Event{Op: Rename, Name: "/tmp/file"}
+	//   Event{Op: Create, Name: "/tmp/rename", RenamedFrom: "/tmp/file"}
+	renamedFrom string
 }
 
 // Op describes a set of file operations.
@@ -50,7 +187,7 @@ const (
 	// example "remove to trash" is often a rename).
 	Remove
 
-	// The path was renamed to something else; any watched on it will be
+	// The path was renamed to something else; any watches on it will be
 	// removed.
 	Rename
 
@@ -60,15 +197,155 @@ const (
 	// get triggered very frequently by some software. For example, Spotlight
 	// indexing on macOS, anti-virus software, backup software, etc.
 	Chmod
+
+	// File descriptor was opened.
+	//
+	// Only works on Linux and FreeBSD.
+	xUnportableOpen
+
+	// File was read from.
+	//
+	// Only works on Linux and FreeBSD.
+	xUnportableRead
+
+	// File opened for writing was closed.
+	//
+	// Only works on Linux and FreeBSD.
+	//
+	// The advantage of using this over Write is that it's more reliable than
+	// waiting for Write events to stop. It's also faster (if you're not
+	// listening to Write events): copying a file of a few GB can easily
+	// generate tens of thousands of Write events in a short span of time.
+	xUnportableCloseWrite
+
+	// File opened for reading was closed.
+	//
+	// Only works on Linux and FreeBSD.
+	xUnportableCloseRead
 )
 
-// Common errors that can be reported.
 var (
+	// ErrNonExistentWatch is used when Remove() is called on a path that's not
+	// added.
 	ErrNonExistentWatch = errors.New("fsnotify: can't remove non-existent watch")
-	ErrEventOverflow    = errors.New("fsnotify: queue or buffer overflow")
-	ErrClosed           = errors.New("fsnotify: watcher already closed")
+
+	// ErrClosed is used when trying to operate on a closed Watcher.
+	ErrClosed = errors.New("fsnotify: watcher already closed")
+
+	// ErrEventOverflow is reported from the Errors channel when there are too
+	// many events:
+	//
+	//  - inotify:      inotify returns IN_Q_OVERFLOW – because there are too
+	//                  many queued events (the fs.inotify.max_queued_events
+	//                  sysctl can be used to increase this).
+	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
+	//  - kqueue, fen:  Not used.
+	ErrEventOverflow = errors.New("fsnotify: queue or buffer overflow")
+
+	// ErrUnsupported is returned by AddWith() when WithOps() specified an
+	// Unportable event that's not supported on this platform.
+	xErrUnsupported = errors.New("fsnotify: not supported with this backend")
 )
 
+// NewWatcher creates a new Watcher.
+func NewWatcher() (*Watcher, error) {
+	ev, errs := make(chan Event), make(chan error)
+	b, err := newBackend(ev, errs)
+	if err != nil {
+		return nil, err
+	}
+	return &Watcher{b: b, Events: ev, Errors: errs}, nil
+}
+
+// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
+// channel.
+//
+// The main use case for this is situations with a very large number of events
+// where the kernel buffer size can't be increased (e.g. due to lack of
+// permissions). An unbuffered Watcher will perform better for almost all use
+// cases, and whenever possible you will be better off increasing the kernel
+// buffers instead of adding a large userspace buffer.
+func NewBufferedWatcher(sz uint) (*Watcher, error) {
+	ev, errs := make(chan Event), make(chan error)
+	b, err := newBufferedBackend(sz, ev, errs)
+	if err != nil {
+		return nil, err
+	}
+	return &Watcher{b: b, Events: ev, Errors: errs}, nil
+}
+
+// Add starts monitoring the path for changes.
+//
+// A path can only be watched once; watching it more than once is a no-op and will
+// not return an error. Paths that do not yet exist on the filesystem cannot be
+// watched.
+//
+// A watch will be automatically removed if the watched path is deleted or
+// renamed. The exception is the Windows backend, which doesn't remove the
+// watcher on renames.
+//
+// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
+// filesystems (/proc, /sys, etc.) generally don't work.
+//
+// Returns [ErrClosed] if [Watcher.Close] was called.
+//
+// See [Watcher.AddWith] for a version that allows adding options.
+//
+// # 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 programs (especially editors) update files atomically: it
+// will write to a temporary file which is then moved to destination,
+// overwriting the original (or some variant thereof). The watcher on the
+// original file is now lost, as that no longer exists.
+//
+// The upshot of this is that a power failure or crash won't leave a
+// half-written file.
+//
+// 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(path string) error { return w.b.Add(path) }
+
+// AddWith is like [Watcher.Add], but allows adding options. When using Add()
+// the defaults described below are used.
+//
+// Possible options are:
+//
+//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
+//     other platforms. The default is 64K (65536 bytes).
+func (w *Watcher) AddWith(path string, opts ...addOpt) error { return w.b.AddWith(path, opts...) }
+
+// 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].
+//
+// Returns nil if [Watcher.Close] was called.
+func (w *Watcher) Remove(path string) error { return w.b.Remove(path) }
+
+// Close removes all watches and closes the Events channel.
+func (w *Watcher) Close() error { return w.b.Close() }
+
+// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
+// yet removed).
+//
+// Returns nil if [Watcher.Close] was called.
+func (w *Watcher) WatchList() []string { return w.b.WatchList() }
+
+// Supports reports if all the listed operations are supported by this platform.
+//
+// Create, Write, Remove, Rename, and Chmod are always supported. It can only
+// return false for an Op starting with Unportable.
+func (w *Watcher) xSupports(op Op) bool { return w.b.xSupports(op) }
+
 func (o Op) String() string {
 	var b strings.Builder
 	if o.Has(Create) {
@@ -80,6 +357,18 @@ func (o Op) String() string {
 	if o.Has(Write) {
 		b.WriteString("|WRITE")
 	}
+	if o.Has(xUnportableOpen) {
+		b.WriteString("|OPEN")
+	}
+	if o.Has(xUnportableRead) {
+		b.WriteString("|READ")
+	}
+	if o.Has(xUnportableCloseWrite) {
+		b.WriteString("|CLOSE_WRITE")
+	}
+	if o.Has(xUnportableCloseRead) {
+		b.WriteString("|CLOSE_READ")
+	}
 	if o.Has(Rename) {
 		b.WriteString("|RENAME")
 	}
@@ -100,24 +389,48 @@ 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 {
+	if e.renamedFrom != "" {
+		return fmt.Sprintf("%-13s %q ← %q", e.Op.String(), e.Name, e.renamedFrom)
+	}
 	return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name)
 }
 
 type (
+	backend interface {
+		Add(string) error
+		AddWith(string, ...addOpt) error
+		Remove(string) error
+		WatchList() []string
+		Close() error
+		xSupports(Op) bool
+	}
 	addOpt   func(opt *withOpts)
 	withOpts struct {
-		bufsize int
+		bufsize    int
+		op         Op
+		noFollow   bool
+		sendCreate bool
 	}
 )
 
+var debug = func() bool {
+	// Check for exactly "1" (rather than mere existence) so we can add
+	// options/flags in the future. I don't know if we ever want that, but it's
+	// nice to leave the option open.
+	return os.Getenv("FSNOTIFY_DEBUG") == "1"
+}()
+
 var defaultOpts = withOpts{
 	bufsize: 65536, // 64K
+	op:      Create | Write | Remove | Rename | Chmod,
 }
 
 func getOptions(opts ...addOpt) withOpts {
 	with := defaultOpts
 	for _, o := range opts {
-		o(&with)
+		if o != nil {
+			o(&with)
+		}
 	}
 	return with
 }
@@ -136,9 +449,44 @@ func WithBufferSize(bytes int) addOpt {
 	return func(opt *withOpts) { opt.bufsize = bytes }
 }
 
+// WithOps sets which operations to listen for. The default is [Create],
+// [Write], [Remove], [Rename], and [Chmod].
+//
+// Excluding operations you're not interested in can save quite a bit of CPU
+// time; in some use cases there may be hundreds of thousands of useless Write
+// or Chmod operations per second.
+//
+// This can also be used to add unportable operations not supported by all
+// platforms; unportable operations all start with "Unportable":
+// [UnportableOpen], [UnportableRead], [UnportableCloseWrite], and
+// [UnportableCloseRead].
+//
+// AddWith returns an error when using an unportable operation that's not
+// supported. Use [Watcher.Support] to check for support.
+func withOps(op Op) addOpt {
+	return func(opt *withOpts) { opt.op = op }
+}
+
+// WithNoFollow disables following symlinks, so the symlinks themselves are
+// watched.
+func withNoFollow() addOpt {
+	return func(opt *withOpts) { opt.noFollow = true }
+}
+
+// "Internal" option for recursive watches on inotify.
+func withCreate() addOpt {
+	return func(opt *withOpts) { opt.sendCreate = true }
+}
+
+var enableRecurse = false
+
 // Check if this path is recursive (ends with "/..." or "\..."), and return the
 // path with the /... stripped.
 func recursivePath(path string) (string, bool) {
+	path = filepath.Clean(path)
+	if !enableRecurse { // Only enabled in tests for now.
+		return path, false
+	}
 	if filepath.Base(path) == "..." {
 		return filepath.Dir(path), true
 	}
diff --git a/vendor/github.com/fsnotify/fsnotify/mkdoc.zsh b/vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
deleted file mode 100644
index 99012ae6539e34df89e1eea154db561658dfa945..0000000000000000000000000000000000000000
--- a/vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
+++ /dev/null
@@ -1,259 +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 so you don't need
-# to update the same comment 5 times.
-
-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.
-//
-// # Windows notes
-//
-// Paths can be added as "C:\\path\\to\\dir", but forward slashes
-// ("C:/path/to/dir") will also work.
-//
-// When a watched directory is removed it will always send an event for the
-// directory itself, but may not send events for all files in that directory.
-// Sometimes it will send events for all times, sometimes it will send no
-// events, and often only for some files.
-//
-// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
-// value that is guaranteed to work with SMB filesystems. If you have many
-// events in quick succession this may not be enough, and you will have to use
-// [WithBufferSize] to increase the value.
-EOF
-)
-
-new=$(<<EOF
-// NewWatcher creates a new Watcher.
-EOF
-)
-
-newbuffered=$(<<EOF
-// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
-// channel.
-//
-// The main use case for this is situations with a very large number of events
-// where the kernel buffer size can't be increased (e.g. due to lack of
-// permissions). An unbuffered Watcher will perform better for almost all use
-// cases, and whenever possible you will be better off increasing the kernel
-// buffers instead of adding a large userspace buffer.
-EOF
-)
-
-add=$(<<EOF
-// Add starts monitoring the path for changes.
-//
-// A path can only be watched once; watching it more than once is a no-op and will
-// not return an error. Paths that do not yet exist on the filesystem cannot be
-// watched.
-//
-// A watch will be automatically removed if the watched path is deleted or
-// renamed. The exception is the Windows backend, which doesn't remove the
-// watcher on renames.
-//
-// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
-// filesystems (/proc, /sys, etc.) generally don't work.
-//
-// Returns [ErrClosed] if [Watcher.Close] was called.
-//
-// See [Watcher.AddWith] for a version that allows adding options.
-//
-// # 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 programs (especially editors) update files atomically: it
-// will write to a temporary file which is then moved to to destination,
-// overwriting the original (or some variant thereof). The watcher on the
-// original file is now lost, as that no longer exists.
-//
-// The upshot of this is that a power failure or crash won't leave a
-// half-written file.
-//
-// 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
-)
-
-addwith=$(<<EOF
-// AddWith is like [Watcher.Add], but allows adding options. When using Add()
-// the defaults described below are used.
-//
-// Possible options are:
-//
-//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
-//     other platforms. The default is 64K (65536 bytes).
-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].
-//
-// Returns nil if [Watcher.Close] was called.
-EOF
-)
-
-close=$(<<EOF
-// Close removes all watches and closes the Events channel.
-EOF
-)
-
-watchlist=$(<<EOF
-// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
-// yet removed).
-//
-// Returns nil if [Watcher.Close] was called.
-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, and you may
-	//                      want to wait until you've stopped receiving them
-	//                      (see the dedup example in cmd/fsnotify).
-	//
-	//                      Some systems may send Write event for directories
-	//                      when the directory content changes.
-	//
-	//   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
-	//                      when a file is truncated. On Windows it's never
-	//                      sent.
-EOF
-)
-
-errors=$(<<EOF
-	// Errors sends any errors.
-	//
-	// ErrEventOverflow is used to indicate there are too many events:
-	//
-	//  - inotify:      There are too many queued events (fs.inotify.max_queued_events sysctl)
-	//  - windows:      The buffer size is too small; WithBufferSize() can be used to increase it.
-	//  - kqueue, fen:  Not used.
-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 NewBufferedWatcher('         $newbuffered
-set-cmt '^func (w \*Watcher) Add('          $add
-set-cmt '^func (w \*Watcher) AddWith('      $addwith
-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
diff --git a/vendor/github.com/fsnotify/fsnotify/system_bsd.go b/vendor/github.com/fsnotify/fsnotify/system_bsd.go
index 4322b0b8855710d3ad624498b42c3494f705e188..f65e8fe3edce58f005d88de1e0bddeddc0de1a17 100644
--- a/vendor/github.com/fsnotify/fsnotify/system_bsd.go
+++ b/vendor/github.com/fsnotify/fsnotify/system_bsd.go
@@ -1,5 +1,4 @@
 //go:build freebsd || openbsd || netbsd || dragonfly
-// +build freebsd openbsd netbsd dragonfly
 
 package fsnotify
 
diff --git a/vendor/github.com/fsnotify/fsnotify/system_darwin.go b/vendor/github.com/fsnotify/fsnotify/system_darwin.go
index 5da5ffa78fe71a8b9fb33c75c53b1bb5a523bd22..a29fc7aab62011188fecd131fdcf654e7f77976c 100644
--- a/vendor/github.com/fsnotify/fsnotify/system_darwin.go
+++ b/vendor/github.com/fsnotify/fsnotify/system_darwin.go
@@ -1,5 +1,4 @@
 //go:build darwin
-// +build darwin
 
 package fsnotify
 
diff --git a/vendor/github.com/pkg/sftp/attrs.go b/vendor/github.com/pkg/sftp/attrs.go
index 758cd4ff97fc552639bbe7a8b79e6b4aad2a05fb..74ac03b7863b231304e74a5bc825d2708fbccc6b 100644
--- a/vendor/github.com/pkg/sftp/attrs.go
+++ b/vendor/github.com/pkg/sftp/attrs.go
@@ -32,10 +32,10 @@ func (fi *fileInfo) Name() string { return fi.name }
 func (fi *fileInfo) Size() int64 { return int64(fi.stat.Size) }
 
 // Mode returns file mode bits.
-func (fi *fileInfo) Mode() os.FileMode { return toFileMode(fi.stat.Mode) }
+func (fi *fileInfo) Mode() os.FileMode { return fi.stat.FileMode() }
 
 // ModTime returns the last modification time of the file.
-func (fi *fileInfo) ModTime() time.Time { return time.Unix(int64(fi.stat.Mtime), 0) }
+func (fi *fileInfo) ModTime() time.Time { return fi.stat.ModTime() }
 
 // IsDir returns true if the file is a directory.
 func (fi *fileInfo) IsDir() bool { return fi.Mode().IsDir() }
@@ -56,6 +56,21 @@ type FileStat struct {
 	Extended []StatExtended
 }
 
+// ModTime returns the Mtime SFTP file attribute converted to a time.Time
+func (fs *FileStat) ModTime() time.Time {
+	return time.Unix(int64(fs.Mtime), 0)
+}
+
+// AccessTime returns the Atime SFTP file attribute converted to a time.Time
+func (fs *FileStat) AccessTime() time.Time {
+	return time.Unix(int64(fs.Atime), 0)
+}
+
+// FileMode returns the Mode SFTP file attribute converted to an os.FileMode
+func (fs *FileStat) FileMode() os.FileMode {
+	return toFileMode(fs.Mode)
+}
+
 // StatExtended contains additional, extended information for a FileStat.
 type StatExtended struct {
 	ExtType string
diff --git a/vendor/github.com/pkg/sftp/attrs_unix.go b/vendor/github.com/pkg/sftp/attrs_unix.go
index 371ae9b9b8191d1e71c3326a7bae34891e283a89..96ffc03df9610a3a1f5bdbccb6f3ccd2cf886b5d 100644
--- a/vendor/github.com/pkg/sftp/attrs_unix.go
+++ b/vendor/github.com/pkg/sftp/attrs_unix.go
@@ -1,5 +1,5 @@
-//go:build darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || aix || js
-// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js
+//go:build darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || aix || js || zos
+// +build darwin dragonfly freebsd !android,linux netbsd openbsd solaris aix js zos
 
 package sftp
 
diff --git a/vendor/github.com/pkg/sftp/client.go b/vendor/github.com/pkg/sftp/client.go
index 0df125e15d4c452a3293d407b5633d70b0b48a9d..83047d0a3bd294d2eaa4899ea2f74ab87bd42ad9 100644
--- a/vendor/github.com/pkg/sftp/client.go
+++ b/vendor/github.com/pkg/sftp/client.go
@@ -2,6 +2,7 @@ package sftp
 
 import (
 	"bytes"
+	"context"
 	"encoding/binary"
 	"errors"
 	"fmt"
@@ -256,7 +257,7 @@ func NewClientPipe(rd io.Reader, wr io.WriteCloser, opts ...ClientOption) (*Clie
 // read/write at the same time. For those services you will need to use
 // `client.OpenFile(os.O_WRONLY|os.O_CREATE|os.O_TRUNC)`.
 func (c *Client) Create(path string) (*File, error) {
-	return c.open(path, flags(os.O_RDWR|os.O_CREATE|os.O_TRUNC))
+	return c.open(path, toPflags(os.O_RDWR|os.O_CREATE|os.O_TRUNC))
 }
 
 const sftpProtocolVersion = 3 // https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
@@ -321,19 +322,27 @@ func (c *Client) Walk(root string) *fs.Walker {
 	return fs.WalkFS(root, c)
 }
 
-// ReadDir reads the directory named by dirname and returns a list of
-// directory entries.
+// ReadDir reads the directory named by p
+// and returns a list of directory entries.
 func (c *Client) ReadDir(p string) ([]os.FileInfo, error) {
-	handle, err := c.opendir(p)
+	return c.ReadDirContext(context.Background(), p)
+}
+
+// ReadDirContext reads the directory named by p
+// and returns a list of directory entries.
+// The passed context can be used to cancel the operation
+// returning all entries listed up to the cancellation.
+func (c *Client) ReadDirContext(ctx context.Context, p string) ([]os.FileInfo, error) {
+	handle, err := c.opendir(ctx, p)
 	if err != nil {
 		return nil, err
 	}
 	defer c.close(handle) // this has to defer earlier than the lock below
-	var attrs []os.FileInfo
+	var entries []os.FileInfo
 	var done = false
 	for !done {
 		id := c.nextID()
-		typ, data, err1 := c.sendPacket(nil, &sshFxpReaddirPacket{
+		typ, data, err1 := c.sendPacket(ctx, nil, &sshFxpReaddirPacket{
 			ID:     id,
 			Handle: handle,
 		})
@@ -354,11 +363,14 @@ func (c *Client) ReadDir(p string) ([]os.FileInfo, error) {
 				filename, data = unmarshalString(data)
 				_, data = unmarshalString(data) // discard longname
 				var attr *FileStat
-				attr, data = unmarshalAttrs(data)
+				attr, data, err = unmarshalAttrs(data)
+				if err != nil {
+					return nil, err
+				}
 				if filename == "." || filename == ".." {
 					continue
 				}
-				attrs = append(attrs, fileInfoFromStat(attr, path.Base(filename)))
+				entries = append(entries, fileInfoFromStat(attr, path.Base(filename)))
 			}
 		case sshFxpStatus:
 			// TODO(dfc) scope warning!
@@ -371,12 +383,12 @@ func (c *Client) ReadDir(p string) ([]os.FileInfo, error) {
 	if err == io.EOF {
 		err = nil
 	}
-	return attrs, err
+	return entries, err
 }
 
-func (c *Client) opendir(path string) (string, error) {
+func (c *Client) opendir(ctx context.Context, path string) (string, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpOpendirPacket{
+	typ, data, err := c.sendPacket(ctx, nil, &sshFxpOpendirPacket{
 		ID:   id,
 		Path: path,
 	})
@@ -412,7 +424,7 @@ func (c *Client) Stat(p string) (os.FileInfo, error) {
 // If 'p' is a symbolic link, the returned FileInfo structure describes the symbolic link.
 func (c *Client) Lstat(p string) (os.FileInfo, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpLstatPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpLstatPacket{
 		ID:   id,
 		Path: p,
 	})
@@ -425,7 +437,11 @@ func (c *Client) Lstat(p string) (os.FileInfo, error) {
 		if sid != id {
 			return nil, &unexpectedIDErr{id, sid}
 		}
-		attr, _ := unmarshalAttrs(data)
+		attr, _, err := unmarshalAttrs(data)
+		if err != nil {
+			// avoid returning a valid value from fileInfoFromStats if err != nil.
+			return nil, err
+		}
 		return fileInfoFromStat(attr, path.Base(p)), nil
 	case sshFxpStatus:
 		return nil, normaliseError(unmarshalStatus(id, data))
@@ -437,7 +453,7 @@ func (c *Client) Lstat(p string) (os.FileInfo, error) {
 // ReadLink reads the target of a symbolic link.
 func (c *Client) ReadLink(p string) (string, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpReadlinkPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpReadlinkPacket{
 		ID:   id,
 		Path: p,
 	})
@@ -466,7 +482,7 @@ func (c *Client) ReadLink(p string) (string, error) {
 // Link creates a hard link at 'newname', pointing at the same inode as 'oldname'
 func (c *Client) Link(oldname, newname string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpHardlinkPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpHardlinkPacket{
 		ID:      id,
 		Oldpath: oldname,
 		Newpath: newname,
@@ -485,7 +501,7 @@ func (c *Client) Link(oldname, newname string) error {
 // Symlink creates a symbolic link at 'newname', pointing at target 'oldname'
 func (c *Client) Symlink(oldname, newname string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpSymlinkPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpSymlinkPacket{
 		ID:         id,
 		Linkpath:   newname,
 		Targetpath: oldname,
@@ -501,9 +517,9 @@ func (c *Client) Symlink(oldname, newname string) error {
 	}
 }
 
-func (c *Client) setfstat(handle string, flags uint32, attrs interface{}) error {
+func (c *Client) fsetstat(handle string, flags uint32, attrs interface{}) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpFsetstatPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpFsetstatPacket{
 		ID:     id,
 		Handle: handle,
 		Flags:  flags,
@@ -523,7 +539,7 @@ func (c *Client) setfstat(handle string, flags uint32, attrs interface{}) error
 // setstat is a convience wrapper to allow for changing of various parts of the file descriptor.
 func (c *Client) setstat(path string, flags uint32, attrs interface{}) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpSetstatPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpSetstatPacket{
 		ID:    id,
 		Path:  path,
 		Flags: flags,
@@ -577,23 +593,37 @@ func (c *Client) Truncate(path string, size int64) error {
 	return c.setstat(path, sshFileXferAttrSize, uint64(size))
 }
 
+// SetExtendedData sets extended attributes of the named file. It uses the
+// SSH_FILEXFER_ATTR_EXTENDED flag in the setstat request.
+//
+// This flag provides a general extension mechanism for vendor-specific extensions.
+// Names of the attributes should be a string of the format "name@domain", where "domain"
+// is a valid, registered domain name and "name" identifies the method. Server
+// implementations SHOULD ignore extended data fields that they do not understand.
+func (c *Client) SetExtendedData(path string, extended []StatExtended) error {
+	attrs := &FileStat{
+		Extended: extended,
+	}
+	return c.setstat(path, sshFileXferAttrExtended, attrs)
+}
+
 // Open opens the named file for reading. If successful, methods on the
 // returned file can be used for reading; the associated file descriptor
 // has mode O_RDONLY.
 func (c *Client) Open(path string) (*File, error) {
-	return c.open(path, flags(os.O_RDONLY))
+	return c.open(path, toPflags(os.O_RDONLY))
 }
 
 // OpenFile is the generalized open call; most users will use Open or
 // Create instead. It opens the named file with specified flag (O_RDONLY
 // etc.). If successful, methods on the returned File can be used for I/O.
 func (c *Client) OpenFile(path string, f int) (*File, error) {
-	return c.open(path, flags(f))
+	return c.open(path, toPflags(f))
 }
 
 func (c *Client) open(path string, pflags uint32) (*File, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpOpenPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpOpenPacket{
 		ID:     id,
 		Path:   path,
 		Pflags: pflags,
@@ -621,7 +651,7 @@ func (c *Client) open(path string, pflags uint32) (*File, error) {
 // immediately after this request has been sent.
 func (c *Client) close(handle string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpClosePacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpClosePacket{
 		ID:     id,
 		Handle: handle,
 	})
@@ -638,7 +668,7 @@ func (c *Client) close(handle string) error {
 
 func (c *Client) stat(path string) (*FileStat, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpStatPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpStatPacket{
 		ID:   id,
 		Path: path,
 	})
@@ -651,8 +681,8 @@ func (c *Client) stat(path string) (*FileStat, error) {
 		if sid != id {
 			return nil, &unexpectedIDErr{id, sid}
 		}
-		attr, _ := unmarshalAttrs(data)
-		return attr, nil
+		attr, _, err := unmarshalAttrs(data)
+		return attr, err
 	case sshFxpStatus:
 		return nil, normaliseError(unmarshalStatus(id, data))
 	default:
@@ -662,7 +692,7 @@ func (c *Client) stat(path string) (*FileStat, error) {
 
 func (c *Client) fstat(handle string) (*FileStat, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpFstatPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpFstatPacket{
 		ID:     id,
 		Handle: handle,
 	})
@@ -675,8 +705,8 @@ func (c *Client) fstat(handle string) (*FileStat, error) {
 		if sid != id {
 			return nil, &unexpectedIDErr{id, sid}
 		}
-		attr, _ := unmarshalAttrs(data)
-		return attr, nil
+		attr, _, err := unmarshalAttrs(data)
+		return attr, err
 	case sshFxpStatus:
 		return nil, normaliseError(unmarshalStatus(id, data))
 	default:
@@ -691,7 +721,7 @@ func (c *Client) fstat(handle string) (*FileStat, error) {
 func (c *Client) StatVFS(path string) (*StatVFS, error) {
 	// send the StatVFS packet to the server
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpStatvfsPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpStatvfsPacket{
 		ID:   id,
 		Path: path,
 	})
@@ -746,7 +776,7 @@ func (c *Client) Remove(path string) error {
 
 func (c *Client) removeFile(path string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpRemovePacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpRemovePacket{
 		ID:       id,
 		Filename: path,
 	})
@@ -764,7 +794,7 @@ func (c *Client) removeFile(path string) error {
 // RemoveDirectory removes a directory path.
 func (c *Client) RemoveDirectory(path string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpRmdirPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpRmdirPacket{
 		ID:   id,
 		Path: path,
 	})
@@ -782,7 +812,7 @@ func (c *Client) RemoveDirectory(path string) error {
 // Rename renames a file.
 func (c *Client) Rename(oldname, newname string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpRenamePacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpRenamePacket{
 		ID:      id,
 		Oldpath: oldname,
 		Newpath: newname,
@@ -802,7 +832,7 @@ func (c *Client) Rename(oldname, newname string) error {
 // which will replace newname if it already exists.
 func (c *Client) PosixRename(oldname, newname string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpPosixRenamePacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpPosixRenamePacket{
 		ID:      id,
 		Oldpath: oldname,
 		Newpath: newname,
@@ -824,7 +854,7 @@ func (c *Client) PosixRename(oldname, newname string) error {
 // or relative pathnames without a leading slash into absolute paths.
 func (c *Client) RealPath(path string) (string, error) {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpRealpathPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpRealpathPacket{
 		ID:   id,
 		Path: path,
 	})
@@ -861,7 +891,7 @@ func (c *Client) Getwd() (string, error) {
 // parent folder does not exist (the method cannot create complete paths).
 func (c *Client) Mkdir(path string) error {
 	id := c.nextID()
-	typ, data, err := c.sendPacket(nil, &sshFxpMkdirPacket{
+	typ, data, err := c.sendPacket(context.Background(), nil, &sshFxpMkdirPacket{
 		ID:   id,
 		Path: path,
 	})
@@ -965,18 +995,34 @@ func (c *Client) RemoveAll(path string) error {
 
 // File represents a remote file.
 type File struct {
-	c      *Client
-	path   string
-	handle string
+	c    *Client
+	path string
 
-	mu     sync.Mutex
+	mu     sync.RWMutex
+	handle string
 	offset int64 // current offset within remote file
 }
 
 // Close closes the File, rendering it unusable for I/O. It returns an
 // error, if any.
 func (f *File) Close() error {
-	return f.c.close(f.handle)
+	f.mu.Lock()
+	defer f.mu.Unlock()
+
+	if f.handle == "" {
+		return os.ErrClosed
+	}
+
+	// The design principle here is that when `openssh-portable/sftp-server.c` is doing `handle_close`,
+	// it will unconditionally mark the handle as unused,
+	// so we need to also unconditionally mark this handle as invalid.
+	// By invalidating our local copy of the handle,
+	// we ensure that there cannot be any erroneous use-after-close requests sent after Close.
+
+	handle := f.handle
+	f.handle = ""
+
+	return f.c.close(handle)
 }
 
 // Name returns the name of the file as presented to Open or Create.
@@ -997,7 +1043,7 @@ func (f *File) Read(b []byte) (int, error) {
 	f.mu.Lock()
 	defer f.mu.Unlock()
 
-	n, err := f.ReadAt(b, f.offset)
+	n, err := f.readAt(b, f.offset)
 	f.offset += int64(n)
 	return n, err
 }
@@ -1007,7 +1053,7 @@ func (f *File) Read(b []byte) (int, error) {
 func (f *File) readChunkAt(ch chan result, b []byte, off int64) (n int, err error) {
 	for err == nil && n < len(b) {
 		id := f.c.nextID()
-		typ, data, err := f.c.sendPacket(ch, &sshFxpReadPacket{
+		typ, data, err := f.c.sendPacket(context.Background(), ch, &sshFxpReadPacket{
 			ID:     id,
 			Handle: f.handle,
 			Offset: uint64(off) + uint64(n),
@@ -1062,6 +1108,19 @@ func (f *File) readAtSequential(b []byte, off int64) (read int, err error) {
 // the number of bytes read and an error, if any. ReadAt follows io.ReaderAt semantics,
 // so the file offset is not altered during the read.
 func (f *File) ReadAt(b []byte, off int64) (int, error) {
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	return f.readAt(b, off)
+}
+
+// readAt must be called while holding either the Read or Write mutex in File.
+// This code is concurrent safe with itself, but not with Close.
+func (f *File) readAt(b []byte, off int64) (int, error) {
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
 	if len(b) <= f.c.maxPacket {
 		// This should be able to be serviced with 1/2 requests.
 		// So, just do it directly.
@@ -1179,7 +1238,9 @@ func (f *File) ReadAt(b []byte, off int64) (int, error) {
 				if err != nil {
 					// return the offset as the start + how much we read before the error.
 					errCh <- rErr{packet.off + int64(n), err}
-					return
+
+					// DO NOT return.
+					// We want to ensure that workCh is drained before wg.Wait returns.
 				}
 			}
 		}()
@@ -1258,6 +1319,10 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
 	f.mu.Lock()
 	defer f.mu.Unlock()
 
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
 	if f.c.disableConcurrentReads {
 		return f.writeToSequential(w)
 	}
@@ -1405,12 +1470,10 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
 				select {
 				case readWork.cur <- writeWork:
 				case <-cancel:
-					return
 				}
 
-				if err != nil {
-					return
-				}
+				// DO NOT return.
+				// We want to ensure that readCh is drained before wg.Wait returns.
 			}
 		}()
 	}
@@ -1450,6 +1513,17 @@ func (f *File) WriteTo(w io.Writer) (written int64, err error) {
 // Stat returns the FileInfo structure describing file. If there is an
 // error.
 func (f *File) Stat() (os.FileInfo, error) {
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	if f.handle == "" {
+		return nil, os.ErrClosed
+	}
+
+	return f.stat()
+}
+
+func (f *File) stat() (os.FileInfo, error) {
 	fs, err := f.c.fstat(f.handle)
 	if err != nil {
 		return nil, err
@@ -1469,13 +1543,17 @@ func (f *File) Write(b []byte) (int, error) {
 	f.mu.Lock()
 	defer f.mu.Unlock()
 
-	n, err := f.WriteAt(b, f.offset)
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
+	n, err := f.writeAt(b, f.offset)
 	f.offset += int64(n)
 	return n, err
 }
 
 func (f *File) writeChunkAt(ch chan result, b []byte, off int64) (int, error) {
-	typ, data, err := f.c.sendPacket(ch, &sshFxpWritePacket{
+	typ, data, err := f.c.sendPacket(context.Background(), ch, &sshFxpWritePacket{
 		ID:     f.c.nextID(),
 		Handle: f.handle,
 		Offset: uint64(off),
@@ -1627,6 +1705,19 @@ func (f *File) writeAtConcurrent(b []byte, off int64) (int, error) {
 // the number of bytes written and an error, if any. WriteAt follows io.WriterAt semantics,
 // so the file offset is not altered during the write.
 func (f *File) WriteAt(b []byte, off int64) (written int, err error) {
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
+	return f.writeAt(b, off)
+}
+
+// writeAt must be called while holding either the Read or Write mutex in File.
+// This code is concurrent safe with itself, but not with Close.
+func (f *File) writeAt(b []byte, off int64) (written int, err error) {
 	if len(b) <= f.c.maxPacket {
 		// We can do this in one write.
 		return f.writeChunkAt(nil, b, off)
@@ -1665,7 +1756,21 @@ func (f *File) WriteAt(b []byte, off int64) (written int, err error) {
 // Giving a concurrency of less than one will default to the Client’s max concurrency.
 //
 // Otherwise, the given concurrency will be capped by the Client's max concurrency.
+//
+// When one needs to guarantee concurrent reads/writes, this method is preferred
+// over ReadFrom.
 func (f *File) ReadFromWithConcurrency(r io.Reader, concurrency int) (read int64, err error) {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+
+	return f.readFromWithConcurrency(r, concurrency)
+}
+
+func (f *File) readFromWithConcurrency(r io.Reader, concurrency int) (read int64, err error) {
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
 	// Split the write into multiple maxPacket sized concurrent writes.
 	// This allows writes with a suitably large reader
 	// to transfer data at a much faster rate due to overlapping round trip times.
@@ -1757,6 +1862,9 @@ func (f *File) ReadFromWithConcurrency(r io.Reader, concurrency int) (read int64
 
 				if err != nil {
 					errCh <- rwErr{work.off, err}
+
+					// DO NOT return.
+					// We want to ensure that workCh is drained before wg.Wait returns.
 				}
 			}
 		}()
@@ -1811,10 +1919,26 @@ func (f *File) ReadFromWithConcurrency(r io.Reader, concurrency int) (read int64
 // This method is preferred over calling Write multiple times
 // to maximise throughput for transferring the entire file,
 // especially over high-latency links.
+//
+// To ensure concurrent writes, the given r needs to implement one of
+// the following receiver methods:
+//
+//	Len()  int
+//	Size() int64
+//	Stat() (os.FileInfo, error)
+//
+// or be an instance of [io.LimitedReader] to determine the number of possible
+// concurrent requests. Otherwise, reads/writes are performed sequentially.
+// ReadFromWithConcurrency can be used explicitly to guarantee concurrent
+// processing of the reader.
 func (f *File) ReadFrom(r io.Reader) (int64, error) {
 	f.mu.Lock()
 	defer f.mu.Unlock()
 
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
 	if f.c.useConcurrentWrites {
 		var remain int64
 		switch r := r.(type) {
@@ -1836,7 +1960,7 @@ func (f *File) ReadFrom(r io.Reader) (int64, error) {
 
 		if remain < 0 {
 			// We can strongly assert that we want default max concurrency here.
-			return f.ReadFromWithConcurrency(r, f.c.maxConcurrentRequests)
+			return f.readFromWithConcurrency(r, f.c.maxConcurrentRequests)
 		}
 
 		if remain > int64(f.c.maxPacket) {
@@ -1851,7 +1975,7 @@ func (f *File) ReadFrom(r io.Reader) (int64, error) {
 				concurrency64 = int64(f.c.maxConcurrentRequests)
 			}
 
-			return f.ReadFromWithConcurrency(r, int(concurrency64))
+			return f.readFromWithConcurrency(r, int(concurrency64))
 		}
 	}
 
@@ -1894,12 +2018,16 @@ func (f *File) Seek(offset int64, whence int) (int64, error) {
 	f.mu.Lock()
 	defer f.mu.Unlock()
 
+	if f.handle == "" {
+		return 0, os.ErrClosed
+	}
+
 	switch whence {
 	case io.SeekStart:
 	case io.SeekCurrent:
 		offset += f.offset
 	case io.SeekEnd:
-		fi, err := f.Stat()
+		fi, err := f.stat()
 		if err != nil {
 			return f.offset, err
 		}
@@ -1918,22 +2046,84 @@ func (f *File) Seek(offset int64, whence int) (int64, error) {
 
 // Chown changes the uid/gid of the current file.
 func (f *File) Chown(uid, gid int) error {
-	return f.c.Chown(f.path, uid, gid)
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	if f.handle == "" {
+		return os.ErrClosed
+	}
+
+	return f.c.fsetstat(f.handle, sshFileXferAttrUIDGID, &FileStat{
+		UID: uint32(uid),
+		GID: uint32(gid),
+	})
 }
 
 // Chmod changes the permissions of the current file.
 //
 // See Client.Chmod for details.
 func (f *File) Chmod(mode os.FileMode) error {
-	return f.c.setfstat(f.handle, sshFileXferAttrPermissions, toChmodPerm(mode))
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	if f.handle == "" {
+		return os.ErrClosed
+	}
+
+	return f.c.fsetstat(f.handle, sshFileXferAttrPermissions, toChmodPerm(mode))
+}
+
+// SetExtendedData sets extended attributes of the current file. It uses the
+// SSH_FILEXFER_ATTR_EXTENDED flag in the setstat request.
+//
+// This flag provides a general extension mechanism for vendor-specific extensions.
+// Names of the attributes should be a string of the format "name@domain", where "domain"
+// is a valid, registered domain name and "name" identifies the method. Server
+// implementations SHOULD ignore extended data fields that they do not understand.
+func (f *File) SetExtendedData(path string, extended []StatExtended) error {
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	if f.handle == "" {
+		return os.ErrClosed
+	}
+
+	attrs := &FileStat{
+		Extended: extended,
+	}
+
+	return f.c.fsetstat(f.handle, sshFileXferAttrExtended, attrs)
+}
+
+// Truncate sets the size of the current file. Although it may be safely assumed
+// that if the size is less than its current size it will be truncated to fit,
+// the SFTP protocol does not specify what behavior the server should do when setting
+// size greater than the current size.
+// We send a SSH_FXP_FSETSTAT here since we have a file handle
+func (f *File) Truncate(size int64) error {
+	f.mu.RLock()
+	defer f.mu.RUnlock()
+
+	if f.handle == "" {
+		return os.ErrClosed
+	}
+
+	return f.c.fsetstat(f.handle, sshFileXferAttrSize, uint64(size))
 }
 
 // Sync requests a flush of the contents of a File to stable storage.
 //
 // Sync requires the server to support the fsync@openssh.com extension.
 func (f *File) Sync() error {
+	f.mu.Lock()
+	defer f.mu.Unlock()
+
+	if f.handle == "" {
+		return os.ErrClosed
+	}
+
 	id := f.c.nextID()
-	typ, data, err := f.c.sendPacket(nil, &sshFxpFsyncPacket{
+	typ, data, err := f.c.sendPacket(context.Background(), nil, &sshFxpFsyncPacket{
 		ID:     id,
 		Handle: f.handle,
 	})
@@ -1948,15 +2138,6 @@ func (f *File) Sync() error {
 	}
 }
 
-// Truncate sets the size of the current file. Although it may be safely assumed
-// that if the size is less than its current size it will be truncated to fit,
-// the SFTP protocol does not specify what behavior the server should do when setting
-// size greater than the current size.
-// We send a SSH_FXP_FSETSTAT here since we have a file handle
-func (f *File) Truncate(size int64) error {
-	return f.c.setfstat(f.handle, sshFileXferAttrSize, uint64(size))
-}
-
 // normaliseError normalises an error into a more standard form that can be
 // checked against stdlib errors like io.EOF or os.ErrNotExist.
 func normaliseError(err error) error {
@@ -1981,15 +2162,14 @@ func normaliseError(err error) error {
 
 // flags converts the flags passed to OpenFile into ssh flags.
 // Unsupported flags are ignored.
-func flags(f int) uint32 {
+func toPflags(f int) uint32 {
 	var out uint32
-	switch f & os.O_WRONLY {
-	case os.O_WRONLY:
-		out |= sshFxfWrite
+	switch f & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) {
 	case os.O_RDONLY:
 		out |= sshFxfRead
-	}
-	if f&os.O_RDWR == os.O_RDWR {
+	case os.O_WRONLY:
+		out |= sshFxfWrite
+	case os.O_RDWR:
 		out |= sshFxfRead | sshFxfWrite
 	}
 	if f&os.O_APPEND == os.O_APPEND {
@@ -2013,7 +2193,7 @@ func flags(f int) uint32 {
 // setuid, setgid and sticky in m, because we've historically supported those
 // bits, and we mask off any non-permission bits.
 func toChmodPerm(m os.FileMode) (perm uint32) {
-	const mask = os.ModePerm | s_ISUID | s_ISGID | s_ISVTX
+	const mask = os.ModePerm | os.FileMode(s_ISUID|s_ISGID|s_ISVTX)
 	perm = uint32(m & mask)
 
 	if m&os.ModeSetuid != 0 {
diff --git a/vendor/github.com/pkg/sftp/conn.go b/vendor/github.com/pkg/sftp/conn.go
index 3bb2ba15f571d4dd90f0f5e7581ec7f013f58b17..93bc37bf71576738a10c00afbcf36521a8d66fe1 100644
--- a/vendor/github.com/pkg/sftp/conn.go
+++ b/vendor/github.com/pkg/sftp/conn.go
@@ -1,6 +1,7 @@
 package sftp
 
 import (
+	"context"
 	"encoding"
 	"fmt"
 	"io"
@@ -128,14 +129,19 @@ type idmarshaler interface {
 	encoding.BinaryMarshaler
 }
 
-func (c *clientConn) sendPacket(ch chan result, p idmarshaler) (byte, []byte, error) {
+func (c *clientConn) sendPacket(ctx context.Context, ch chan result, p idmarshaler) (byte, []byte, error) {
 	if cap(ch) < 1 {
 		ch = make(chan result, 1)
 	}
 
 	c.dispatchRequest(ch, p)
-	s := <-ch
-	return s.typ, s.data, s.err
+
+	select {
+	case <-ctx.Done():
+		return 0, nil, ctx.Err()
+	case s := <-ch:
+		return s.typ, s.data, s.err
+	}
 }
 
 // dispatchRequest should ideally only be called by race-detection tests outside of this file,
diff --git a/vendor/github.com/pkg/sftp/ls_unix.go b/vendor/github.com/pkg/sftp/ls_unix.go
index 0beba32b2c7f7bb88c417f8818164917a90cf3f1..aa230dc280a9d53478a9b2192b4da689fbe53337 100644
--- a/vendor/github.com/pkg/sftp/ls_unix.go
+++ b/vendor/github.com/pkg/sftp/ls_unix.go
@@ -1,5 +1,5 @@
-//go:build aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || js
-// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js
+//go:build aix || darwin || dragonfly || freebsd || (!android && linux) || netbsd || openbsd || solaris || js || zos
+// +build aix darwin dragonfly freebsd !android,linux netbsd openbsd solaris js zos
 
 package sftp
 
diff --git a/vendor/github.com/pkg/sftp/packet.go b/vendor/github.com/pkg/sftp/packet.go
index 1232ff1efe19956cf61e0122b584c8f84ac2a706..bfe6a3c9770e099d2571797334c6c859890de4e3 100644
--- a/vendor/github.com/pkg/sftp/packet.go
+++ b/vendor/github.com/pkg/sftp/packet.go
@@ -56,6 +56,11 @@ func marshalFileInfo(b []byte, fi os.FileInfo) []byte {
 	flags, fileStat := fileStatFromInfo(fi)
 
 	b = marshalUint32(b, flags)
+
+	return marshalFileStat(b, flags, fileStat)
+}
+
+func marshalFileStat(b []byte, flags uint32, fileStat *FileStat) []byte {
 	if flags&sshFileXferAttrSize != 0 {
 		b = marshalUint64(b, fileStat.Size)
 	}
@@ -91,10 +96,9 @@ func marshalStatus(b []byte, err StatusError) []byte {
 }
 
 func marshal(b []byte, v interface{}) []byte {
-	if v == nil {
-		return b
-	}
 	switch v := v.(type) {
+	case nil:
+		return b
 	case uint8:
 		return append(b, v)
 	case uint32:
@@ -103,6 +107,8 @@ func marshal(b []byte, v interface{}) []byte {
 		return marshalUint64(b, v)
 	case string:
 		return marshalString(b, v)
+	case []byte:
+		return append(b, v...)
 	case os.FileInfo:
 		return marshalFileInfo(b, v)
 	default:
@@ -168,38 +174,69 @@ func unmarshalStringSafe(b []byte) (string, []byte, error) {
 	return string(b[:n]), b[n:], nil
 }
 
-func unmarshalAttrs(b []byte) (*FileStat, []byte) {
-	flags, b := unmarshalUint32(b)
+func unmarshalAttrs(b []byte) (*FileStat, []byte, error) {
+	flags, b, err := unmarshalUint32Safe(b)
+	if err != nil {
+		return nil, b, err
+	}
 	return unmarshalFileStat(flags, b)
 }
 
-func unmarshalFileStat(flags uint32, b []byte) (*FileStat, []byte) {
+func unmarshalFileStat(flags uint32, b []byte) (*FileStat, []byte, error) {
 	var fs FileStat
+	var err error
+
 	if flags&sshFileXferAttrSize == sshFileXferAttrSize {
-		fs.Size, b, _ = unmarshalUint64Safe(b)
-	}
-	if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
-		fs.UID, b, _ = unmarshalUint32Safe(b)
+		fs.Size, b, err = unmarshalUint64Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
 	}
 	if flags&sshFileXferAttrUIDGID == sshFileXferAttrUIDGID {
-		fs.GID, b, _ = unmarshalUint32Safe(b)
+		fs.UID, b, err = unmarshalUint32Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
+		fs.GID, b, err = unmarshalUint32Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
 	}
 	if flags&sshFileXferAttrPermissions == sshFileXferAttrPermissions {
-		fs.Mode, b, _ = unmarshalUint32Safe(b)
+		fs.Mode, b, err = unmarshalUint32Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
 	}
 	if flags&sshFileXferAttrACmodTime == sshFileXferAttrACmodTime {
-		fs.Atime, b, _ = unmarshalUint32Safe(b)
-		fs.Mtime, b, _ = unmarshalUint32Safe(b)
+		fs.Atime, b, err = unmarshalUint32Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
+		fs.Mtime, b, err = unmarshalUint32Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
 	}
 	if flags&sshFileXferAttrExtended == sshFileXferAttrExtended {
 		var count uint32
-		count, b, _ = unmarshalUint32Safe(b)
+		count, b, err = unmarshalUint32Safe(b)
+		if err != nil {
+			return nil, b, err
+		}
+
 		ext := make([]StatExtended, count)
 		for i := uint32(0); i < count; i++ {
 			var typ string
 			var data string
-			typ, b, _ = unmarshalStringSafe(b)
-			data, b, _ = unmarshalStringSafe(b)
+			typ, b, err = unmarshalStringSafe(b)
+			if err != nil {
+				return nil, b, err
+			}
+			data, b, err = unmarshalStringSafe(b)
+			if err != nil {
+				return nil, b, err
+			}
 			ext[i] = StatExtended{
 				ExtType: typ,
 				ExtData: data,
@@ -207,7 +244,7 @@ func unmarshalFileStat(flags uint32, b []byte) (*FileStat, []byte) {
 		}
 		fs.Extended = ext
 	}
-	return &fs, b
+	return &fs, b, nil
 }
 
 func unmarshalStatus(id uint32, data []byte) error {
@@ -681,12 +718,13 @@ type sshFxpOpenPacket struct {
 	ID     uint32
 	Path   string
 	Pflags uint32
-	Flags  uint32 // ignored
+	Flags  uint32
+	Attrs  interface{}
 }
 
 func (p *sshFxpOpenPacket) id() uint32 { return p.ID }
 
-func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) {
+func (p *sshFxpOpenPacket) marshalPacket() ([]byte, []byte, error) {
 	l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id)
 		4 + len(p.Path) +
 		4 + 4
@@ -698,7 +736,22 @@ func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) {
 	b = marshalUint32(b, p.Pflags)
 	b = marshalUint32(b, p.Flags)
 
-	return b, nil
+	switch attrs := p.Attrs.(type) {
+	case []byte:
+		return b, attrs, nil // may as well short-ciruit this case.
+	case os.FileInfo:
+		_, fs := fileStatFromInfo(attrs) // we throw away the flags, and override with those in packet.
+		return b, marshalFileStat(nil, p.Flags, fs), nil
+	case *FileStat:
+		return b, marshalFileStat(nil, p.Flags, attrs), nil
+	}
+
+	return b, marshal(nil, p.Attrs), nil
+}
+
+func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) {
+	header, payload, err := p.marshalPacket()
+	return append(header, payload...), err
 }
 
 func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error {
@@ -709,12 +762,25 @@ func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error {
 		return err
 	} else if p.Pflags, b, err = unmarshalUint32Safe(b); err != nil {
 		return err
-	} else if p.Flags, _, err = unmarshalUint32Safe(b); err != nil {
+	} else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil {
 		return err
 	}
+	p.Attrs = b
 	return nil
 }
 
+func (p *sshFxpOpenPacket) unmarshalFileStat(flags uint32) (*FileStat, error) {
+	switch attrs := p.Attrs.(type) {
+	case *FileStat:
+		return attrs, nil
+	case []byte:
+		fs, _, err := unmarshalFileStat(flags, attrs)
+		return fs, err
+	default:
+		return nil, fmt.Errorf("invalid type in unmarshalFileStat: %T", attrs)
+	}
+}
+
 type sshFxpReadPacket struct {
 	ID     uint32
 	Len    uint32
@@ -757,7 +823,7 @@ func (p *sshFxpReadPacket) UnmarshalBinary(b []byte) error {
 // So, we need: uint32(length) + byte(type) + uint32(id) + uint32(data_length)
 const dataHeaderLen = 4 + 1 + 4 + 4
 
-func (p *sshFxpReadPacket) getDataSlice(alloc *allocator, orderID uint32) []byte {
+func (p *sshFxpReadPacket) getDataSlice(alloc *allocator, orderID uint32, maxTxPacket uint32) []byte {
 	dataLen := p.Len
 	if dataLen > maxTxPacket {
 		dataLen = maxTxPacket
@@ -943,9 +1009,17 @@ func (p *sshFxpSetstatPacket) marshalPacket() ([]byte, []byte, error) {
 	b = marshalString(b, p.Path)
 	b = marshalUint32(b, p.Flags)
 
-	payload := marshal(nil, p.Attrs)
+	switch attrs := p.Attrs.(type) {
+	case []byte:
+		return b, attrs, nil // may as well short-ciruit this case.
+	case os.FileInfo:
+		_, fs := fileStatFromInfo(attrs) // we throw away the flags, and override with those in packet.
+		return b, marshalFileStat(nil, p.Flags, fs), nil
+	case *FileStat:
+		return b, marshalFileStat(nil, p.Flags, attrs), nil
+	}
 
-	return b, payload, nil
+	return b, marshal(nil, p.Attrs), nil
 }
 
 func (p *sshFxpSetstatPacket) MarshalBinary() ([]byte, error) {
@@ -964,9 +1038,17 @@ func (p *sshFxpFsetstatPacket) marshalPacket() ([]byte, []byte, error) {
 	b = marshalString(b, p.Handle)
 	b = marshalUint32(b, p.Flags)
 
-	payload := marshal(nil, p.Attrs)
+	switch attrs := p.Attrs.(type) {
+	case []byte:
+		return b, attrs, nil // may as well short-ciruit this case.
+	case os.FileInfo:
+		_, fs := fileStatFromInfo(attrs) // we throw away the flags, and override with those in packet.
+		return b, marshalFileStat(nil, p.Flags, fs), nil
+	case *FileStat:
+		return b, marshalFileStat(nil, p.Flags, attrs), nil
+	}
 
-	return b, payload, nil
+	return b, marshal(nil, p.Attrs), nil
 }
 
 func (p *sshFxpFsetstatPacket) MarshalBinary() ([]byte, error) {
@@ -987,6 +1069,18 @@ func (p *sshFxpSetstatPacket) UnmarshalBinary(b []byte) error {
 	return nil
 }
 
+func (p *sshFxpSetstatPacket) unmarshalFileStat(flags uint32) (*FileStat, error) {
+	switch attrs := p.Attrs.(type) {
+	case *FileStat:
+		return attrs, nil
+	case []byte:
+		fs, _, err := unmarshalFileStat(flags, attrs)
+		return fs, err
+	default:
+		return nil, fmt.Errorf("invalid type in unmarshalFileStat: %T", attrs)
+	}
+}
+
 func (p *sshFxpFsetstatPacket) UnmarshalBinary(b []byte) error {
 	var err error
 	if p.ID, b, err = unmarshalUint32Safe(b); err != nil {
@@ -1000,6 +1094,18 @@ func (p *sshFxpFsetstatPacket) UnmarshalBinary(b []byte) error {
 	return nil
 }
 
+func (p *sshFxpFsetstatPacket) unmarshalFileStat(flags uint32) (*FileStat, error) {
+	switch attrs := p.Attrs.(type) {
+	case *FileStat:
+		return attrs, nil
+	case []byte:
+		fs, _, err := unmarshalFileStat(flags, attrs)
+		return fs, err
+	default:
+		return nil, fmt.Errorf("invalid type in unmarshalFileStat: %T", attrs)
+	}
+}
+
 type sshFxpHandlePacket struct {
 	ID     uint32
 	Handle string
diff --git a/vendor/github.com/pkg/sftp/request-attrs.go b/vendor/github.com/pkg/sftp/request-attrs.go
index b5c95b4ad6184cff9afa1e371fffc87a0cc5f97e..476c56518ce7ec298e26dd2d80bb94da59933dfb 100644
--- a/vendor/github.com/pkg/sftp/request-attrs.go
+++ b/vendor/github.com/pkg/sftp/request-attrs.go
@@ -3,7 +3,6 @@ package sftp
 // Methods on the Request object to make working with the Flags bitmasks and
 // Attr(ibutes) byte blob easier. Use Pflags() when working with an Open/Write
 // request and AttrFlags() and Attributes() when working with SetStat requests.
-import "os"
 
 // FileOpenFlags defines Open and Write Flags. Correlate directly with with os.OpenFile flags
 // (https://golang.org/pkg/os/#pkg-constants).
@@ -50,14 +49,9 @@ func (r *Request) AttrFlags() FileAttrFlags {
 	return newFileAttrFlags(r.Flags)
 }
 
-// FileMode returns the Mode SFTP file attributes wrapped as os.FileMode
-func (a FileStat) FileMode() os.FileMode {
-	return os.FileMode(a.Mode)
-}
-
 // Attributes parses file attributes byte blob and return them in a
 // FileStat object.
 func (r *Request) Attributes() *FileStat {
-	fs, _ := unmarshalFileStat(r.Flags, r.Attrs)
+	fs, _, _ := unmarshalFileStat(r.Flags, r.Attrs)
 	return fs
 }
diff --git a/vendor/github.com/pkg/sftp/request-interfaces.go b/vendor/github.com/pkg/sftp/request-interfaces.go
index 2090e3162782a0fb72ca1bc753752670636191db..13e7577e3d6d2a0781b5905614b9ca2831611822 100644
--- a/vendor/github.com/pkg/sftp/request-interfaces.go
+++ b/vendor/github.com/pkg/sftp/request-interfaces.go
@@ -30,7 +30,7 @@ type FileReader interface {
 // FileWriter should return an io.WriterAt for the filepath.
 //
 // The request server code will call Close() on the returned io.WriterAt
-// ojbect if an io.Closer type assertion succeeds.
+// object if an io.Closer type assertion succeeds.
 // Note in cases of an error, the error text will be sent to the client.
 // Note when receiving an Append flag it is important to not open files using
 // O_APPEND if you plan to use WriteAt, as they conflict.
@@ -144,6 +144,8 @@ type NameLookupFileLister interface {
 //
 // If a populated entry implements [FileInfoExtendedData], extended attributes will also be returned to the client.
 //
+// The request server code will call Close() on ListerAt if an io.Closer type assertion succeeds.
+//
 // Note in cases of an error, the error text will be sent to the client.
 type ListerAt interface {
 	ListAt([]os.FileInfo, int64) (int, error)
diff --git a/vendor/github.com/pkg/sftp/request-server.go b/vendor/github.com/pkg/sftp/request-server.go
index 7a99db648ca03de04885f58998ecc9f90a934ded..11047e6b10ada7ff2c7c873ffebb8a0c2f651b59 100644
--- a/vendor/github.com/pkg/sftp/request-server.go
+++ b/vendor/github.com/pkg/sftp/request-server.go
@@ -10,7 +10,7 @@ import (
 	"sync"
 )
 
-var maxTxPacket uint32 = 1 << 15
+const defaultMaxTxPacket uint32 = 1 << 15
 
 // Handlers contains the 4 SFTP server request handlers.
 type Handlers struct {
@@ -28,6 +28,7 @@ type RequestServer struct {
 	pktMgr *packetManager
 
 	startDirectory string
+	maxTxPacket    uint32
 
 	mu           sync.RWMutex
 	handleCount  int
@@ -57,6 +58,22 @@ func WithStartDirectory(startDirectory string) RequestServerOption {
 	}
 }
 
+// WithRSMaxTxPacket sets the maximum size of the payload returned to the client,
+// measured in bytes. The default value is 32768 bytes, and this option
+// can only be used to increase it. Setting this option to a larger value
+// should be safe, because the client decides the size of the requested payload.
+//
+// The default maximum packet size is 32768 bytes.
+func WithRSMaxTxPacket(size uint32) RequestServerOption {
+	return func(rs *RequestServer) {
+		if size < defaultMaxTxPacket {
+			return
+		}
+
+		rs.maxTxPacket = size
+	}
+}
+
 // NewRequestServer creates/allocates/returns new RequestServer.
 // Normally there will be one server per user-session.
 func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServerOption) *RequestServer {
@@ -73,6 +90,7 @@ func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServ
 		pktMgr:     newPktMgr(svrConn),
 
 		startDirectory: "/",
+		maxTxPacket:    defaultMaxTxPacket,
 
 		openRequests: make(map[string]*Request),
 	}
@@ -260,7 +278,7 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR
 					Method:   "Stat",
 					Filepath: cleanPathWithBase(rs.startDirectory, request.Filepath),
 				}
-				rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
+				rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
 			}
 		case *sshFxpFsetstatPacket:
 			handle := pkt.getHandle()
@@ -272,7 +290,7 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR
 					Method:   "Setstat",
 					Filepath: cleanPathWithBase(rs.startDirectory, request.Filepath),
 				}
-				rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
+				rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
 			}
 		case *sshFxpExtendedPacketPosixRename:
 			request := &Request{
@@ -280,24 +298,24 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR
 				Filepath: cleanPathWithBase(rs.startDirectory, pkt.Oldpath),
 				Target:   cleanPathWithBase(rs.startDirectory, pkt.Newpath),
 			}
-			rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
+			rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
 		case *sshFxpExtendedPacketStatVFS:
 			request := &Request{
 				Method:   "StatVFS",
 				Filepath: cleanPathWithBase(rs.startDirectory, pkt.Path),
 			}
-			rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
+			rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
 		case hasHandle:
 			handle := pkt.getHandle()
 			request, ok := rs.getRequest(handle)
 			if !ok {
 				rpkt = statusFromError(pkt.id(), EBADF)
 			} else {
-				rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
+				rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
 			}
 		case hasPath:
 			request := requestFromPacket(ctx, pkt, rs.startDirectory)
-			rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID)
+			rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID, rs.maxTxPacket)
 			request.close()
 		default:
 			rpkt = statusFromError(pkt.id(), ErrSSHFxOpUnsupported)
diff --git a/vendor/github.com/pkg/sftp/request.go b/vendor/github.com/pkg/sftp/request.go
index 57d788df64798f832da77c36dd86d82379c8d88c..e7c47a9c9b11cdf06283ab728a092a5ec12b0e46 100644
--- a/vendor/github.com/pkg/sftp/request.go
+++ b/vendor/github.com/pkg/sftp/request.go
@@ -121,6 +121,22 @@ func (s *state) getListerAt() ListerAt {
 	return s.listerAt
 }
 
+func (s *state) closeListerAt() error {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+
+	var err error
+
+	if s.listerAt != nil {
+		if c, ok := s.listerAt.(io.Closer); ok {
+			err = c.Close()
+		}
+		s.listerAt = nil
+	}
+
+	return err
+}
+
 // Request contains the data and state for the incoming service request.
 type Request struct {
 	// Get, Put, Setstat, Stat, Rename, Remove
@@ -178,6 +194,7 @@ func requestFromPacket(ctx context.Context, pkt hasPath, baseDir string) *Reques
 	switch p := pkt.(type) {
 	case *sshFxpOpenPacket:
 		request.Flags = p.Pflags
+		request.Attrs = p.Attrs.([]byte)
 	case *sshFxpSetstatPacket:
 		request.Flags = p.Flags
 		request.Attrs = p.Attrs.([]byte)
@@ -229,9 +246,9 @@ func (r *Request) close() error {
 		}
 	}()
 
-	rd, wr, rw := r.getAllReaderWriters()
+	err := r.state.closeListerAt()
 
-	var err error
+	rd, wr, rw := r.getAllReaderWriters()
 
 	// Close errors on a Writer are far more likely to be the important one.
 	// As they can be information that there was a loss of data.
@@ -283,14 +300,14 @@ func (r *Request) transferError(err error) {
 }
 
 // called from worker to handle packet/request
-func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
+func (r *Request) call(handlers Handlers, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
 	switch r.Method {
 	case "Get":
-		return fileget(handlers.FileGet, r, pkt, alloc, orderID)
+		return fileget(handlers.FileGet, r, pkt, alloc, orderID, maxTxPacket)
 	case "Put":
-		return fileput(handlers.FilePut, r, pkt, alloc, orderID)
+		return fileput(handlers.FilePut, r, pkt, alloc, orderID, maxTxPacket)
 	case "Open":
-		return fileputget(handlers.FilePut, r, pkt, alloc, orderID)
+		return fileputget(handlers.FilePut, r, pkt, alloc, orderID, maxTxPacket)
 	case "Setstat", "Rename", "Rmdir", "Mkdir", "Link", "Symlink", "Remove", "PosixRename", "StatVFS":
 		return filecmd(handlers.FileCmd, r, pkt)
 	case "List":
@@ -375,13 +392,13 @@ func (r *Request) opendir(h Handlers, pkt requestPacket) responsePacket {
 }
 
 // wrap FileReader handler
-func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
+func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
 	rd := r.getReaderAt()
 	if rd == nil {
 		return statusFromError(pkt.id(), errors.New("unexpected read packet"))
 	}
 
-	data, offset, _ := packetData(pkt, alloc, orderID)
+	data, offset, _ := packetData(pkt, alloc, orderID, maxTxPacket)
 
 	n, err := rd.ReadAt(data, offset)
 	// only return EOF error if no data left to read
@@ -397,20 +414,20 @@ func fileget(h FileReader, r *Request, pkt requestPacket, alloc *allocator, orde
 }
 
 // wrap FileWriter handler
-func fileput(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
+func fileput(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
 	wr := r.getWriterAt()
 	if wr == nil {
 		return statusFromError(pkt.id(), errors.New("unexpected write packet"))
 	}
 
-	data, offset, _ := packetData(pkt, alloc, orderID)
+	data, offset, _ := packetData(pkt, alloc, orderID, maxTxPacket)
 
 	_, err := wr.WriteAt(data, offset)
 	return statusFromError(pkt.id(), err)
 }
 
 // wrap OpenFileWriter handler
-func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32) responsePacket {
+func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) responsePacket {
 	rw := r.getWriterAtReaderAt()
 	if rw == nil {
 		return statusFromError(pkt.id(), errors.New("unexpected write and read packet"))
@@ -418,7 +435,7 @@ func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, o
 
 	switch p := pkt.(type) {
 	case *sshFxpReadPacket:
-		data, offset := p.getDataSlice(alloc, orderID), int64(p.Offset)
+		data, offset := p.getDataSlice(alloc, orderID, maxTxPacket), int64(p.Offset)
 
 		n, err := rw.ReadAt(data, offset)
 		// only return EOF error if no data left to read
@@ -444,10 +461,10 @@ func fileputget(h FileWriter, r *Request, pkt requestPacket, alloc *allocator, o
 }
 
 // file data for additional read/write packets
-func packetData(p requestPacket, alloc *allocator, orderID uint32) (data []byte, offset int64, length uint32) {
+func packetData(p requestPacket, alloc *allocator, orderID uint32, maxTxPacket uint32) (data []byte, offset int64, length uint32) {
 	switch p := p.(type) {
 	case *sshFxpReadPacket:
-		return p.getDataSlice(alloc, orderID), int64(p.Offset), p.Len
+		return p.getDataSlice(alloc, orderID, maxTxPacket), int64(p.Offset), p.Len
 	case *sshFxpWritePacket:
 		return p.Data, int64(p.Offset), p.Length
 	}
diff --git a/vendor/github.com/pkg/sftp/server.go b/vendor/github.com/pkg/sftp/server.go
index 2e419f5909a44e4f3bd94f1e86c19a370e109c63..fb474c4f316f9770bd3f698599d7be5139205a15 100644
--- a/vendor/github.com/pkg/sftp/server.go
+++ b/vendor/github.com/pkg/sftp/server.go
@@ -34,6 +34,7 @@ type Server struct {
 	openFilesLock sync.RWMutex
 	handleCount   int
 	workDir       string
+	maxTxPacket   uint32
 }
 
 func (svr *Server) nextHandle(f *os.File) string {
@@ -86,6 +87,7 @@ func NewServer(rwc io.ReadWriteCloser, options ...ServerOption) (*Server, error)
 		debugStream: ioutil.Discard,
 		pktMgr:      newPktMgr(svrConn),
 		openFiles:   make(map[string]*os.File),
+		maxTxPacket: defaultMaxTxPacket,
 	}
 
 	for _, o := range options {
@@ -139,6 +141,24 @@ func WithServerWorkingDirectory(workDir string) ServerOption {
 	}
 }
 
+// WithMaxTxPacket sets the maximum size of the payload returned to the client,
+// measured in bytes. The default value is 32768 bytes, and this option
+// can only be used to increase it. Setting this option to a larger value
+// should be safe, because the client decides the size of the requested payload.
+//
+// The default maximum packet size is 32768 bytes.
+func WithMaxTxPacket(size uint32) ServerOption {
+	return func(s *Server) error {
+		if size < defaultMaxTxPacket {
+			return errors.New("size must be greater than or equal to 32768")
+		}
+
+		s.maxTxPacket = size
+
+		return nil
+	}
+}
+
 type rxPacket struct {
 	pktType  fxp
 	pktBytes []byte
@@ -287,7 +307,7 @@ func handlePacket(s *Server, p orderedRequest) error {
 		f, ok := s.getHandle(p.Handle)
 		if ok {
 			err = nil
-			data := p.getDataSlice(s.pktMgr.alloc, orderID)
+			data := p.getDataSlice(s.pktMgr.alloc, orderID, s.maxTxPacket)
 			n, _err := f.ReadAt(data, int64(p.Offset))
 			if _err != nil && (_err != io.EOF || n == 0) {
 				err = _err
@@ -462,7 +482,18 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket {
 		osFlags |= os.O_EXCL
 	}
 
-	f, err := os.OpenFile(svr.toLocalPath(p.Path), osFlags, 0o644)
+	mode := os.FileMode(0o644)
+	// Like OpenSSH, we only handle permissions here, and only when the file is being created.
+	// Otherwise, the permissions are ignored.
+	if p.Flags&sshFileXferAttrPermissions != 0 {
+		fs, err := p.unmarshalFileStat(p.Flags)
+		if err != nil {
+			return statusFromError(p.ID, err)
+		}
+		mode = fs.FileMode() & os.ModePerm
+	}
+
+	f, err := os.OpenFile(svr.toLocalPath(p.Path), osFlags, mode)
 	if err != nil {
 		return statusFromError(p.ID, err)
 	}
@@ -496,44 +527,23 @@ func (p *sshFxpReaddirPacket) respond(svr *Server) responsePacket {
 }
 
 func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket {
-	// additional unmarshalling is required for each possibility here
-	b := p.Attrs.([]byte)
-	var err error
+	path := svr.toLocalPath(p.Path)
 
-	p.Path = svr.toLocalPath(p.Path)
+	debug("setstat name %q", path)
 
-	debug("setstat name \"%s\"", p.Path)
-	if (p.Flags & sshFileXferAttrSize) != 0 {
-		var size uint64
-		if size, b, err = unmarshalUint64Safe(b); err == nil {
-			err = os.Truncate(p.Path, int64(size))
-		}
+	fs, err := p.unmarshalFileStat(p.Flags)
+
+	if err == nil && (p.Flags&sshFileXferAttrSize) != 0 {
+		err = os.Truncate(path, int64(fs.Size))
 	}
-	if (p.Flags & sshFileXferAttrPermissions) != 0 {
-		var mode uint32
-		if mode, b, err = unmarshalUint32Safe(b); err == nil {
-			err = os.Chmod(p.Path, os.FileMode(mode))
-		}
+	if err == nil && (p.Flags&sshFileXferAttrPermissions) != 0 {
+		err = os.Chmod(path, fs.FileMode())
 	}
-	if (p.Flags & sshFileXferAttrACmodTime) != 0 {
-		var atime uint32
-		var mtime uint32
-		if atime, b, err = unmarshalUint32Safe(b); err != nil {
-		} else if mtime, b, err = unmarshalUint32Safe(b); err != nil {
-		} else {
-			atimeT := time.Unix(int64(atime), 0)
-			mtimeT := time.Unix(int64(mtime), 0)
-			err = os.Chtimes(p.Path, atimeT, mtimeT)
-		}
+	if err == nil && (p.Flags&sshFileXferAttrUIDGID) != 0 {
+		err = os.Chown(path, int(fs.UID), int(fs.GID))
 	}
-	if (p.Flags & sshFileXferAttrUIDGID) != 0 {
-		var uid uint32
-		var gid uint32
-		if uid, b, err = unmarshalUint32Safe(b); err != nil {
-		} else if gid, _, err = unmarshalUint32Safe(b); err != nil {
-		} else {
-			err = os.Chown(p.Path, int(uid), int(gid))
-		}
+	if err == nil && (p.Flags&sshFileXferAttrACmodTime) != 0 {
+		err = os.Chtimes(path, fs.AccessTime(), fs.ModTime())
 	}
 
 	return statusFromError(p.ID, err)
@@ -545,41 +555,32 @@ func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket {
 		return statusFromError(p.ID, EBADF)
 	}
 
-	// additional unmarshalling is required for each possibility here
-	b := p.Attrs.([]byte)
-	var err error
+	path := f.Name()
 
-	debug("fsetstat name \"%s\"", f.Name())
-	if (p.Flags & sshFileXferAttrSize) != 0 {
-		var size uint64
-		if size, b, err = unmarshalUint64Safe(b); err == nil {
-			err = f.Truncate(int64(size))
-		}
+	debug("fsetstat name %q", path)
+
+	fs, err := p.unmarshalFileStat(p.Flags)
+
+	if err == nil && (p.Flags&sshFileXferAttrSize) != 0 {
+		err = f.Truncate(int64(fs.Size))
 	}
-	if (p.Flags & sshFileXferAttrPermissions) != 0 {
-		var mode uint32
-		if mode, b, err = unmarshalUint32Safe(b); err == nil {
-			err = f.Chmod(os.FileMode(mode))
-		}
+	if err == nil && (p.Flags&sshFileXferAttrPermissions) != 0 {
+		err = f.Chmod(fs.FileMode())
 	}
-	if (p.Flags & sshFileXferAttrACmodTime) != 0 {
-		var atime uint32
-		var mtime uint32
-		if atime, b, err = unmarshalUint32Safe(b); err != nil {
-		} else if mtime, b, err = unmarshalUint32Safe(b); err != nil {
-		} else {
-			atimeT := time.Unix(int64(atime), 0)
-			mtimeT := time.Unix(int64(mtime), 0)
-			err = os.Chtimes(f.Name(), atimeT, mtimeT)
-		}
+	if err == nil && (p.Flags&sshFileXferAttrUIDGID) != 0 {
+		err = f.Chown(int(fs.UID), int(fs.GID))
 	}
-	if (p.Flags & sshFileXferAttrUIDGID) != 0 {
-		var uid uint32
-		var gid uint32
-		if uid, b, err = unmarshalUint32Safe(b); err != nil {
-		} else if gid, _, err = unmarshalUint32Safe(b); err != nil {
-		} else {
-			err = f.Chown(int(uid), int(gid))
+	if err == nil && (p.Flags&sshFileXferAttrACmodTime) != 0 {
+		type chtimer interface {
+			Chtimes(atime, mtime time.Time) error
+		}
+
+		switch f := interface{}(f).(type) {
+		case chtimer:
+			// future-compatible, for when/if *os.File supports Chtimes.
+			err = f.Chtimes(fs.AccessTime(), fs.ModTime())
+		default:
+			err = os.Chtimes(path, fs.AccessTime(), fs.ModTime())
 		}
 	}
 
diff --git a/vendor/github.com/pkg/sftp/stat_plan9.go b/vendor/github.com/pkg/sftp/stat_plan9.go
deleted file mode 100644
index 761abdf56bf19f70fecfd219454dda5de09fe819..0000000000000000000000000000000000000000
--- a/vendor/github.com/pkg/sftp/stat_plan9.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package sftp
-
-import (
-	"os"
-	"syscall"
-)
-
-var EBADF = syscall.NewError("fd out of range or not open")
-
-func wrapPathError(filepath string, err error) error {
-	if errno, ok := err.(syscall.ErrorString); ok {
-		return &os.PathError{Path: filepath, Err: errno}
-	}
-	return err
-}
-
-// translateErrno translates a syscall error number to a SFTP error code.
-func translateErrno(errno syscall.ErrorString) uint32 {
-	switch errno {
-	case "":
-		return sshFxOk
-	case syscall.ENOENT:
-		return sshFxNoSuchFile
-	case syscall.EPERM:
-		return sshFxPermissionDenied
-	}
-
-	return sshFxFailure
-}
-
-func translateSyscallError(err error) (uint32, bool) {
-	switch e := err.(type) {
-	case syscall.ErrorString:
-		return translateErrno(e), true
-	case *os.PathError:
-		debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
-		if errno, ok := e.Err.(syscall.ErrorString); ok {
-			return translateErrno(errno), true
-		}
-	}
-	return 0, false
-}
-
-// isRegular returns true if the mode describes a regular file.
-func isRegular(mode uint32) bool {
-	return mode&S_IFMT == syscall.S_IFREG
-}
-
-// toFileMode converts sftp filemode bits to the os.FileMode specification
-func toFileMode(mode uint32) os.FileMode {
-	var fm = os.FileMode(mode & 0777)
-
-	switch mode & S_IFMT {
-	case syscall.S_IFBLK:
-		fm |= os.ModeDevice
-	case syscall.S_IFCHR:
-		fm |= os.ModeDevice | os.ModeCharDevice
-	case syscall.S_IFDIR:
-		fm |= os.ModeDir
-	case syscall.S_IFIFO:
-		fm |= os.ModeNamedPipe
-	case syscall.S_IFLNK:
-		fm |= os.ModeSymlink
-	case syscall.S_IFREG:
-		// nothing to do
-	case syscall.S_IFSOCK:
-		fm |= os.ModeSocket
-	}
-
-	return fm
-}
-
-// fromFileMode converts from the os.FileMode specification to sftp filemode bits
-func fromFileMode(mode os.FileMode) uint32 {
-	ret := uint32(mode & os.ModePerm)
-
-	switch mode & os.ModeType {
-	case os.ModeDevice | os.ModeCharDevice:
-		ret |= syscall.S_IFCHR
-	case os.ModeDevice:
-		ret |= syscall.S_IFBLK
-	case os.ModeDir:
-		ret |= syscall.S_IFDIR
-	case os.ModeNamedPipe:
-		ret |= syscall.S_IFIFO
-	case os.ModeSymlink:
-		ret |= syscall.S_IFLNK
-	case 0:
-		ret |= syscall.S_IFREG
-	case os.ModeSocket:
-		ret |= syscall.S_IFSOCK
-	}
-
-	return ret
-}
-
-// Plan 9 doesn't have setuid, setgid or sticky, but a Plan 9 client should
-// be able to send these bits to a POSIX server.
-const (
-	s_ISUID = 04000
-	s_ISGID = 02000
-	s_ISVTX = 01000
-)
diff --git a/vendor/github.com/pkg/sftp/stat_posix.go b/vendor/github.com/pkg/sftp/stat_posix.go
deleted file mode 100644
index 5b870e23c24e44b2f1b9ae3af8ce0a7bcddb8dea..0000000000000000000000000000000000000000
--- a/vendor/github.com/pkg/sftp/stat_posix.go
+++ /dev/null
@@ -1,124 +0,0 @@
-//go:build !plan9
-// +build !plan9
-
-package sftp
-
-import (
-	"os"
-	"syscall"
-)
-
-const EBADF = syscall.EBADF
-
-func wrapPathError(filepath string, err error) error {
-	if errno, ok := err.(syscall.Errno); ok {
-		return &os.PathError{Path: filepath, Err: errno}
-	}
-	return err
-}
-
-// translateErrno translates a syscall error number to a SFTP error code.
-func translateErrno(errno syscall.Errno) uint32 {
-	switch errno {
-	case 0:
-		return sshFxOk
-	case syscall.ENOENT:
-		return sshFxNoSuchFile
-	case syscall.EACCES, syscall.EPERM:
-		return sshFxPermissionDenied
-	}
-
-	return sshFxFailure
-}
-
-func translateSyscallError(err error) (uint32, bool) {
-	switch e := err.(type) {
-	case syscall.Errno:
-		return translateErrno(e), true
-	case *os.PathError:
-		debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
-		if errno, ok := e.Err.(syscall.Errno); ok {
-			return translateErrno(errno), true
-		}
-	}
-	return 0, false
-}
-
-// isRegular returns true if the mode describes a regular file.
-func isRegular(mode uint32) bool {
-	return mode&S_IFMT == syscall.S_IFREG
-}
-
-// toFileMode converts sftp filemode bits to the os.FileMode specification
-func toFileMode(mode uint32) os.FileMode {
-	var fm = os.FileMode(mode & 0777)
-
-	switch mode & S_IFMT {
-	case syscall.S_IFBLK:
-		fm |= os.ModeDevice
-	case syscall.S_IFCHR:
-		fm |= os.ModeDevice | os.ModeCharDevice
-	case syscall.S_IFDIR:
-		fm |= os.ModeDir
-	case syscall.S_IFIFO:
-		fm |= os.ModeNamedPipe
-	case syscall.S_IFLNK:
-		fm |= os.ModeSymlink
-	case syscall.S_IFREG:
-		// nothing to do
-	case syscall.S_IFSOCK:
-		fm |= os.ModeSocket
-	}
-
-	if mode&syscall.S_ISUID != 0 {
-		fm |= os.ModeSetuid
-	}
-	if mode&syscall.S_ISGID != 0 {
-		fm |= os.ModeSetgid
-	}
-	if mode&syscall.S_ISVTX != 0 {
-		fm |= os.ModeSticky
-	}
-
-	return fm
-}
-
-// fromFileMode converts from the os.FileMode specification to sftp filemode bits
-func fromFileMode(mode os.FileMode) uint32 {
-	ret := uint32(mode & os.ModePerm)
-
-	switch mode & os.ModeType {
-	case os.ModeDevice | os.ModeCharDevice:
-		ret |= syscall.S_IFCHR
-	case os.ModeDevice:
-		ret |= syscall.S_IFBLK
-	case os.ModeDir:
-		ret |= syscall.S_IFDIR
-	case os.ModeNamedPipe:
-		ret |= syscall.S_IFIFO
-	case os.ModeSymlink:
-		ret |= syscall.S_IFLNK
-	case 0:
-		ret |= syscall.S_IFREG
-	case os.ModeSocket:
-		ret |= syscall.S_IFSOCK
-	}
-
-	if mode&os.ModeSetuid != 0 {
-		ret |= syscall.S_ISUID
-	}
-	if mode&os.ModeSetgid != 0 {
-		ret |= syscall.S_ISGID
-	}
-	if mode&os.ModeSticky != 0 {
-		ret |= syscall.S_ISVTX
-	}
-
-	return ret
-}
-
-const (
-	s_ISUID = syscall.S_ISUID
-	s_ISGID = syscall.S_ISGID
-	s_ISVTX = syscall.S_ISVTX
-)
diff --git a/vendor/github.com/pkg/sftp/syscall_fixed.go b/vendor/github.com/pkg/sftp/syscall_fixed.go
deleted file mode 100644
index e84430830ef2707a3763afb9d34d4daa0ff5105a..0000000000000000000000000000000000000000
--- a/vendor/github.com/pkg/sftp/syscall_fixed.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build plan9 || windows || (js && wasm)
-// +build plan9 windows js,wasm
-
-// Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of
-// 0xf000. None of the the other S_IFxyz values include the "1" (in 0x1f000)
-// which prevents them from matching the bitmask.
-
-package sftp
-
-const S_IFMT = 0xf000
diff --git a/vendor/github.com/pkg/sftp/syscall_good.go b/vendor/github.com/pkg/sftp/syscall_good.go
deleted file mode 100644
index 50052189e2333480ed8f789421a57de16478c096..0000000000000000000000000000000000000000
--- a/vendor/github.com/pkg/sftp/syscall_good.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build !plan9 && !windows && (!js || !wasm)
-// +build !plan9
-// +build !windows
-// +build !js !wasm
-
-package sftp
-
-import "syscall"
-
-const S_IFMT = syscall.S_IFMT
diff --git a/vendor/github.com/tklauser/numcpus/.cirrus.yml b/vendor/github.com/tklauser/numcpus/.cirrus.yml
index 33e6595cca36cfa8806199981dffd40987115665..b3091efdef67a17c253719eac46e20b5361329f5 100644
--- a/vendor/github.com/tklauser/numcpus/.cirrus.yml
+++ b/vendor/github.com/tklauser/numcpus/.cirrus.yml
@@ -1,10 +1,10 @@
 env:
   CIRRUS_CLONE_DEPTH: 1
-  GO_VERSION: go1.22.2
+  GO_VERSION: go1.23.0
 
 freebsd_13_task:
   freebsd_instance:
-    image_family: freebsd-13-2
+    image_family: freebsd-13-3
   install_script: |
     pkg install -y go
     GOBIN=$PWD/bin go install golang.org/dl/${GO_VERSION}@latest
diff --git a/vendor/github.com/tklauser/numcpus/numcpus.go b/vendor/github.com/tklauser/numcpus/numcpus.go
index af59983e7d347e3716a89e915b929677e8aeafb2..de206f0612e9c036ff021c69fee5e78356d5e782 100644
--- a/vendor/github.com/tklauser/numcpus/numcpus.go
+++ b/vendor/github.com/tklauser/numcpus/numcpus.go
@@ -73,3 +73,26 @@ func GetPossible() (int, error) {
 func GetPresent() (int, error) {
 	return getPresent()
 }
+
+// ListOffline returns the list of offline CPUs. See [GetOffline] for details on
+// when a CPU is considered offline.
+func ListOffline() ([]int, error) {
+	return listOffline()
+}
+
+// ListOnline returns the list of CPUs that are online and being scheduled.
+func ListOnline() ([]int, error) {
+	return listOnline()
+}
+
+// ListPossible returns the list of possible CPUs. See [GetPossible] for
+// details on when a CPU is considered possible.
+func ListPossible() ([]int, error) {
+	return listPossible()
+}
+
+// ListPresent returns the list of present CPUs. See [GetPresent] for
+// details on when a CPU is considered present.
+func ListPresent() ([]int, error) {
+	return listPresent()
+}
diff --git a/vendor/github.com/tklauser/numcpus/numcpus_linux.go b/vendor/github.com/tklauser/numcpus/numcpus_linux.go
index 7e75cb061640be2ab2378cb961d36d46297175ee..7b991da468f6ad8e292fd47fdb4b66d92176e423 100644
--- a/vendor/github.com/tklauser/numcpus/numcpus_linux.go
+++ b/vendor/github.com/tklauser/numcpus/numcpus_linux.go
@@ -15,6 +15,7 @@
 package numcpus
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"strconv"
@@ -23,7 +24,14 @@ import (
 	"golang.org/x/sys/unix"
 )
 
-const sysfsCPUBasePath = "/sys/devices/system/cpu"
+const (
+	sysfsCPUBasePath = "/sys/devices/system/cpu"
+
+	offline  = "offline"
+	online   = "online"
+	possible = "possible"
+	present  = "present"
+)
 
 func getFromCPUAffinity() (int, error) {
 	var cpuSet unix.CPUSet
@@ -33,19 +41,26 @@ func getFromCPUAffinity() (int, error) {
 	return cpuSet.Count(), nil
 }
 
-func readCPURange(file string) (int, error) {
+func readCPURangeWith[T any](file string, f func(cpus string) (T, error)) (T, error) {
+	var zero T
 	buf, err := os.ReadFile(filepath.Join(sysfsCPUBasePath, file))
 	if err != nil {
-		return 0, err
+		return zero, err
 	}
-	return parseCPURange(strings.Trim(string(buf), "\n "))
+	return f(strings.Trim(string(buf), "\n "))
 }
 
-func parseCPURange(cpus string) (int, error) {
+func countCPURange(cpus string) (int, error) {
+	// Treat empty file as valid. This might be the case if there are no offline CPUs in which
+	// case /sys/devices/system/cpu/offline is empty.
+	if cpus == "" {
+		return 0, nil
+	}
+
 	n := int(0)
 	for _, cpuRange := range strings.Split(cpus, ",") {
-		if len(cpuRange) == 0 {
-			continue
+		if cpuRange == "" {
+			return 0, fmt.Errorf("empty CPU range in CPU string %q", cpus)
 		}
 		from, to, found := strings.Cut(cpuRange, "-")
 		first, err := strconv.ParseUint(from, 10, 32)
@@ -60,11 +75,49 @@ func parseCPURange(cpus string) (int, error) {
 		if err != nil {
 			return 0, err
 		}
+		if last < first {
+			return 0, fmt.Errorf("last CPU in range (%d) less than first (%d)", last, first)
+		}
 		n += int(last - first + 1)
 	}
 	return n, nil
 }
 
+func listCPURange(cpus string) ([]int, error) {
+	// See comment in countCPURange.
+	if cpus == "" {
+		return []int{}, nil
+	}
+
+	list := []int{}
+	for _, cpuRange := range strings.Split(cpus, ",") {
+		if cpuRange == "" {
+			return nil, fmt.Errorf("empty CPU range in CPU string %q", cpus)
+		}
+		from, to, found := strings.Cut(cpuRange, "-")
+		first, err := strconv.ParseUint(from, 10, 32)
+		if err != nil {
+			return nil, err
+		}
+		if !found {
+			// range containing a single element
+			list = append(list, int(first))
+			continue
+		}
+		last, err := strconv.ParseUint(to, 10, 32)
+		if err != nil {
+			return nil, err
+		}
+		if last < first {
+			return nil, fmt.Errorf("last CPU in range (%d) less than first (%d)", last, first)
+		}
+		for cpu := int(first); cpu <= int(last); cpu++ {
+			list = append(list, cpu)
+		}
+	}
+	return list, nil
+}
+
 func getConfigured() (int, error) {
 	d, err := os.Open(sysfsCPUBasePath)
 	if err != nil {
@@ -100,20 +153,36 @@ func getKernelMax() (int, error) {
 }
 
 func getOffline() (int, error) {
-	return readCPURange("offline")
+	return readCPURangeWith(offline, countCPURange)
 }
 
 func getOnline() (int, error) {
 	if n, err := getFromCPUAffinity(); err == nil {
 		return n, nil
 	}
-	return readCPURange("online")
+	return readCPURangeWith(online, countCPURange)
 }
 
 func getPossible() (int, error) {
-	return readCPURange("possible")
+	return readCPURangeWith(possible, countCPURange)
 }
 
 func getPresent() (int, error) {
-	return readCPURange("present")
+	return readCPURangeWith(present, countCPURange)
+}
+
+func listOffline() ([]int, error) {
+	return readCPURangeWith(offline, listCPURange)
+}
+
+func listOnline() ([]int, error) {
+	return readCPURangeWith(online, listCPURange)
+}
+
+func listPossible() ([]int, error) {
+	return readCPURangeWith(possible, listCPURange)
+}
+
+func listPresent() ([]int, error) {
+	return readCPURangeWith(present, listCPURange)
 }
diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go
index 3ca9e89e22e3a5863023c647e565c1d198ee018f..c0d1c29e6f9b7963dd75a327dec521669bab4e3e 100644
--- a/vendor/golang.org/x/crypto/ssh/server.go
+++ b/vendor/golang.org/x/crypto/ssh/server.go
@@ -510,8 +510,8 @@ userAuthLoop:
 			if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
 				return nil, err
 			}
-
-			return nil, discMsg
+			authErrs = append(authErrs, discMsg)
+			return nil, &ServerAuthError{Errors: authErrs}
 		}
 
 		var userAuthReq userAuthRequestMsg
diff --git a/vendor/golang.org/x/sys/unix/README.md b/vendor/golang.org/x/sys/unix/README.md
index 7d3c060e12213c48b017dfd2846fb9b1cf413d31..6e08a76a716e9acd766c9cda4ea56d607b481470 100644
--- a/vendor/golang.org/x/sys/unix/README.md
+++ b/vendor/golang.org/x/sys/unix/README.md
@@ -156,7 +156,7 @@ from the generated architecture-specific files listed below, and merge these
 into a common file for each OS.
 
 The merge is performed in the following steps:
-1. Construct the set of common code that is idential in all architecture-specific files.
+1. Construct the set of common code that is identical in all architecture-specific files.
 2. Write this common code to the merged file.
 3. Remove the common code from all architecture-specific files.
 
diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh
index e14b766a32c5fb4b3f85d0ce5a7ddf4fce35b81a..ac54ecaba0a4bbd974659c5562d146424380a139 100644
--- a/vendor/golang.org/x/sys/unix/mkerrors.sh
+++ b/vendor/golang.org/x/sys/unix/mkerrors.sh
@@ -656,7 +656,7 @@ errors=$(
 signals=$(
 	echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
 	awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
-	grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' |
+	grep -E -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' |
 	sort
 )
 
@@ -666,7 +666,7 @@ echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
 	sort >_error.grep
 echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
 	awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
-	grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT\|SIGMAX64' |
+	grep -E -v '(SIGSTKSIZE|SIGSTKSZ|SIGRT|SIGMAX64)' |
 	sort >_signal.grep
 
 echo '// mkerrors.sh' "$@"
diff --git a/vendor/golang.org/x/sys/unix/syscall_aix.go b/vendor/golang.org/x/sys/unix/syscall_aix.go
index 67ce6cef2d5c4990629b90eb43d888b90612d671..6f15ba1eaff6571878742b4d058e084f30afa6f2 100644
--- a/vendor/golang.org/x/sys/unix/syscall_aix.go
+++ b/vendor/golang.org/x/sys/unix/syscall_aix.go
@@ -360,7 +360,7 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
 	var status _C_int
 	var r Pid_t
 	err = ERESTART
-	// AIX wait4 may return with ERESTART errno, while the processus is still
+	// AIX wait4 may return with ERESTART errno, while the process is still
 	// active.
 	for err == ERESTART {
 		r, err = wait4(Pid_t(pid), &status, options, rusage)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go
index 3f1d3d4cb2560b171bd7414fb6cbe6abcac17df6..f08abd434ff479a74567656a2fd01fbc1dc548db 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -1295,6 +1295,48 @@ func GetsockoptTCPInfo(fd, level, opt int) (*TCPInfo, error) {
 	return &value, err
 }
 
+// GetsockoptTCPCCVegasInfo returns algorithm specific congestion control information for a socket using the "vegas"
+// algorithm.
+//
+// The socket's congestion control algorighm can be retrieved via [GetsockoptString] with the [TCP_CONGESTION] option:
+//
+//	algo, err := unix.GetsockoptString(fd, unix.IPPROTO_TCP, unix.TCP_CONGESTION)
+func GetsockoptTCPCCVegasInfo(fd, level, opt int) (*TCPVegasInfo, error) {
+	var value [SizeofTCPCCInfo / 4]uint32 // ensure proper alignment
+	vallen := _Socklen(SizeofTCPCCInfo)
+	err := getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen)
+	out := (*TCPVegasInfo)(unsafe.Pointer(&value[0]))
+	return out, err
+}
+
+// GetsockoptTCPCCDCTCPInfo returns algorithm specific congestion control information for a socket using the "dctp"
+// algorithm.
+//
+// The socket's congestion control algorighm can be retrieved via [GetsockoptString] with the [TCP_CONGESTION] option:
+//
+//	algo, err := unix.GetsockoptString(fd, unix.IPPROTO_TCP, unix.TCP_CONGESTION)
+func GetsockoptTCPCCDCTCPInfo(fd, level, opt int) (*TCPDCTCPInfo, error) {
+	var value [SizeofTCPCCInfo / 4]uint32 // ensure proper alignment
+	vallen := _Socklen(SizeofTCPCCInfo)
+	err := getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen)
+	out := (*TCPDCTCPInfo)(unsafe.Pointer(&value[0]))
+	return out, err
+}
+
+// GetsockoptTCPCCBBRInfo returns algorithm specific congestion control information for a socket using the "bbr"
+// algorithm.
+//
+// The socket's congestion control algorighm can be retrieved via [GetsockoptString] with the [TCP_CONGESTION] option:
+//
+//	algo, err := unix.GetsockoptString(fd, unix.IPPROTO_TCP, unix.TCP_CONGESTION)
+func GetsockoptTCPCCBBRInfo(fd, level, opt int) (*TCPBBRInfo, error) {
+	var value [SizeofTCPCCInfo / 4]uint32 // ensure proper alignment
+	vallen := _Socklen(SizeofTCPCCInfo)
+	err := getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen)
+	out := (*TCPBBRInfo)(unsafe.Pointer(&value[0]))
+	return out, err
+}
+
 // GetsockoptString returns the string value of the socket option opt for the
 // socket associated with fd at the given socket level.
 func GetsockoptString(fd, level, opt int) (string, error) {
@@ -1959,7 +2001,26 @@ func Getpgrp() (pid int) {
 //sysnb	Getpid() (pid int)
 //sysnb	Getppid() (ppid int)
 //sys	Getpriority(which int, who int) (prio int, err error)
-//sys	Getrandom(buf []byte, flags int) (n int, err error)
+
+func Getrandom(buf []byte, flags int) (n int, err error) {
+	vdsoRet, supported := vgetrandom(buf, uint32(flags))
+	if supported {
+		if vdsoRet < 0 {
+			return 0, errnoErr(syscall.Errno(-vdsoRet))
+		}
+		return vdsoRet, nil
+	}
+	var p *byte
+	if len(buf) > 0 {
+		p = &buf[0]
+	}
+	r, _, e := Syscall(SYS_GETRANDOM, uintptr(unsafe.Pointer(p)), uintptr(len(buf)), uintptr(flags))
+	if e != 0 {
+		return 0, errnoErr(e)
+	}
+	return int(r), nil
+}
+
 //sysnb	Getrusage(who int, rusage *Rusage) (err error)
 //sysnb	Getsid(pid int) (sid int, err error)
 //sysnb	Gettid() (tid int)
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
index cf2ee6c75ef3d3905f8a6575b00500bc246d483d..745e5c7e6c0d5d6cde926549e394f8c541e7371b 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
@@ -182,3 +182,5 @@ func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error
 	}
 	return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
 }
+
+const SYS_FSTATAT = SYS_NEWFSTATAT
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
index 3d0e98451f8a78933ecf08740b1f6e744f0d627a..dd2262a40799a2cdeb02e40a4526aa8b3533c258 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_loong64.go
@@ -214,3 +214,5 @@ func KexecFileLoad(kernelFd int, initrdFd int, cmdline string, flags int) error
 	}
 	return kexecFileLoad(kernelFd, initrdFd, cmdlineLen, cmdline, flags)
 }
+
+const SYS_FSTATAT = SYS_NEWFSTATAT
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
index 6f5a288944dfe604dd0f33b5b32e5d91b827c3f1..8cf3670bda630e22ea33151e00c3895c72dda69c 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go
@@ -187,3 +187,5 @@ func RISCVHWProbe(pairs []RISCVHWProbePairs, set *CPUSet, flags uint) (err error
 	}
 	return riscvHWProbe(pairs, setSize, set, flags)
 }
+
+const SYS_FSTATAT = SYS_NEWFSTATAT
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go
index 01a70b24638e60cde45588ff39ee6bfd1d915a21..de3b462489c0b87f198e59a29f86a7b7fa383ac4 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go
@@ -495,6 +495,7 @@ const (
 	BPF_F_TEST_REG_INVARIANTS                   = 0x80
 	BPF_F_TEST_RND_HI32                         = 0x4
 	BPF_F_TEST_RUN_ON_CPU                       = 0x1
+	BPF_F_TEST_SKB_CHECKSUM_COMPLETE            = 0x4
 	BPF_F_TEST_STATE_FREQ                       = 0x8
 	BPF_F_TEST_XDP_LIVE_FRAMES                  = 0x2
 	BPF_F_XDP_DEV_BOUND_ONLY                    = 0x40
@@ -1922,6 +1923,7 @@ const (
 	MNT_EXPIRE                                  = 0x4
 	MNT_FORCE                                   = 0x1
 	MNT_ID_REQ_SIZE_VER0                        = 0x18
+	MNT_ID_REQ_SIZE_VER1                        = 0x20
 	MODULE_INIT_COMPRESSED_FILE                 = 0x4
 	MODULE_INIT_IGNORE_MODVERSIONS              = 0x1
 	MODULE_INIT_IGNORE_VERMAGIC                 = 0x2
@@ -2187,7 +2189,7 @@ const (
 	NFT_REG_SIZE                                = 0x10
 	NFT_REJECT_ICMPX_MAX                        = 0x3
 	NFT_RT_MAX                                  = 0x4
-	NFT_SECMARK_CTX_MAXLEN                      = 0x100
+	NFT_SECMARK_CTX_MAXLEN                      = 0x1000
 	NFT_SET_MAXNAMELEN                          = 0x100
 	NFT_SOCKET_MAX                              = 0x3
 	NFT_TABLE_F_MASK                            = 0x7
@@ -2356,9 +2358,11 @@ const (
 	PERF_MEM_LVLNUM_IO                          = 0xa
 	PERF_MEM_LVLNUM_L1                          = 0x1
 	PERF_MEM_LVLNUM_L2                          = 0x2
+	PERF_MEM_LVLNUM_L2_MHB                      = 0x5
 	PERF_MEM_LVLNUM_L3                          = 0x3
 	PERF_MEM_LVLNUM_L4                          = 0x4
 	PERF_MEM_LVLNUM_LFB                         = 0xc
+	PERF_MEM_LVLNUM_MSC                         = 0x6
 	PERF_MEM_LVLNUM_NA                          = 0xf
 	PERF_MEM_LVLNUM_PMEM                        = 0xe
 	PERF_MEM_LVLNUM_RAM                         = 0xd
@@ -2431,6 +2435,7 @@ const (
 	PRIO_PGRP                                   = 0x1
 	PRIO_PROCESS                                = 0x0
 	PRIO_USER                                   = 0x2
+	PROCFS_IOCTL_MAGIC                          = 'f'
 	PROC_SUPER_MAGIC                            = 0x9fa0
 	PROT_EXEC                                   = 0x4
 	PROT_GROWSDOWN                              = 0x1000000
@@ -2933,11 +2938,12 @@ const (
 	RUSAGE_SELF                                 = 0x0
 	RUSAGE_THREAD                               = 0x1
 	RWF_APPEND                                  = 0x10
+	RWF_ATOMIC                                  = 0x40
 	RWF_DSYNC                                   = 0x2
 	RWF_HIPRI                                   = 0x1
 	RWF_NOAPPEND                                = 0x20
 	RWF_NOWAIT                                  = 0x8
-	RWF_SUPPORTED                               = 0x3f
+	RWF_SUPPORTED                               = 0x7f
 	RWF_SYNC                                    = 0x4
 	RWF_WRITE_LIFE_NOT_SET                      = 0x0
 	SCHED_BATCH                                 = 0x3
@@ -3210,6 +3216,7 @@ const (
 	STATX_ATTR_MOUNT_ROOT                       = 0x2000
 	STATX_ATTR_NODUMP                           = 0x40
 	STATX_ATTR_VERITY                           = 0x100000
+	STATX_ATTR_WRITE_ATOMIC                     = 0x400000
 	STATX_BASIC_STATS                           = 0x7ff
 	STATX_BLOCKS                                = 0x400
 	STATX_BTIME                                 = 0x800
@@ -3226,6 +3233,7 @@ const (
 	STATX_SUBVOL                                = 0x8000
 	STATX_TYPE                                  = 0x1
 	STATX_UID                                   = 0x8
+	STATX_WRITE_ATOMIC                          = 0x10000
 	STATX__RESERVED                             = 0x80000000
 	SYNC_FILE_RANGE_WAIT_AFTER                  = 0x4
 	SYNC_FILE_RANGE_WAIT_BEFORE                 = 0x1
@@ -3624,6 +3632,7 @@ const (
 	XDP_UMEM_PGOFF_COMPLETION_RING              = 0x180000000
 	XDP_UMEM_PGOFF_FILL_RING                    = 0x100000000
 	XDP_UMEM_REG                                = 0x4
+	XDP_UMEM_TX_METADATA_LEN                    = 0x4
 	XDP_UMEM_TX_SW_CSUM                         = 0x2
 	XDP_UMEM_UNALIGNED_CHUNK_FLAG               = 0x1
 	XDP_USE_NEED_WAKEUP                         = 0x8
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
index 684a5168dac4e3a9cbc4bb6c0bc3dc8259371f06..8aa6d77c0184ec640481f6702f6d9752aa8ed6d0 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_386.go
@@ -153,9 +153,14 @@ const (
 	NFDBITS                          = 0x20
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
index 61d74b592d68647f5ec4daffe64daca8d8ff0870..da428f4253398f660159eb46bd5723fa7d43a62b 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go
@@ -153,9 +153,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go
index a28c9e3e893adb63c98bad863878601f8d2ae217..bf45bfec78a531ba8c9a577c63adea802d9a714f 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x20
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
index ab5d1fe8ead78b400d50ada17cc0a8c203e166fe..71c67162b737e2d35f0ea3899c3c12417aab23e7 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go
@@ -154,9 +154,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go
index c523090e7c17e0ada43acb248c94934f4f29a150..9476628fa02b8534f1c233930edac5dffa04cda5 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go
@@ -154,9 +154,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go
index 01e6ea7804b12a83847f59ac0464a0ecfa2f211a..b9e85f3cf0c05e70b31ae098fffb37e55a886eb4 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x20
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go
index 7aa610b1e717baf92c32d098beeb1df9cd1223fe..a48b68a7647ef83eeac6f5e353836a06d16c9f22 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go
index 92af771b44a35026dee5c90b4f28aeeacb69762a..ea00e8522a15904fa7a5bd3db7985db86655143b 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go
index b27ef5e6f11952ba929e6659970c6e55b9ee7d34..91c64687176a9e51798cf0ceaadc661570de4af4 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x20
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go
index 237a2cefb3e5a27bfb7dc72a560c94495270cbaa..8cbf38d639016a95651312cdb47ebfd7cc8af487 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go
@@ -152,9 +152,14 @@ const (
 	NL3                              = 0x300
 	NLDLY                            = 0x300
 	NOFLSH                           = 0x80000000
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x4
 	ONLCR                            = 0x2
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go
index 4a5c555a36e2bf0383f0094e84e7ced989597391..a2df7341917ec85da05ca8e737c5649e8fab2b55 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go
@@ -152,9 +152,14 @@ const (
 	NL3                              = 0x300
 	NLDLY                            = 0x300
 	NOFLSH                           = 0x80000000
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x4
 	ONLCR                            = 0x2
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go
index a02fb49a5f8adb3a400876aa44fde5e597205050..247913792333186b320b8f8f5a3be497b01f24e5 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go
@@ -152,9 +152,14 @@ const (
 	NL3                              = 0x300
 	NLDLY                            = 0x300
 	NOFLSH                           = 0x80000000
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x4
 	ONLCR                            = 0x2
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go
index e26a7c61b2b6fd8edfaf4feb989e013760c7965d..d265f146ee016c204ac17a28e669ec79dec8f97f 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go
index c48f7c2103b81350e8af69d8b83cc6fa25d1a7a1..3f2d6443964ff1ee59b02671f37743b9403b2764 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go
@@ -150,9 +150,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x8008b705
 	NS_GET_NSTYPE                    = 0xb703
 	NS_GET_OWNER_UID                 = 0xb704
 	NS_GET_PARENT                    = 0xb702
+	NS_GET_PID_FROM_PIDNS            = 0x8004b706
+	NS_GET_PID_IN_PIDNS              = 0x8004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x8004b707
+	NS_GET_TGID_IN_PIDNS             = 0x8004b709
 	NS_GET_USERNS                    = 0xb701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go
index ad4b9aace7bb6f7eb581f41398aa987f9436ef3a..5d8b727a1c83771bc80f5087d785e8cbc5e10cfb 100644
--- a/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go
+++ b/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go
@@ -155,9 +155,14 @@ const (
 	NFDBITS                          = 0x40
 	NLDLY                            = 0x100
 	NOFLSH                           = 0x80
+	NS_GET_MNTNS_ID                  = 0x4008b705
 	NS_GET_NSTYPE                    = 0x2000b703
 	NS_GET_OWNER_UID                 = 0x2000b704
 	NS_GET_PARENT                    = 0x2000b702
+	NS_GET_PID_FROM_PIDNS            = 0x4004b706
+	NS_GET_PID_IN_PIDNS              = 0x4004b708
+	NS_GET_TGID_FROM_PIDNS           = 0x4004b707
+	NS_GET_TGID_IN_PIDNS             = 0x4004b709
 	NS_GET_USERNS                    = 0x2000b701
 	OLCUC                            = 0x2
 	ONLCR                            = 0x4
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
index 1bc1a5adb25fde8aae979c74220db4ae26aea523..af30da5578031731efcbdc41618f3e786e4ccc6d 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
@@ -971,23 +971,6 @@ func Getpriority(which int, who int) (prio int, err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func Getrandom(buf []byte, flags int) (n int, err error) {
-	var _p0 unsafe.Pointer
-	if len(buf) > 0 {
-		_p0 = unsafe.Pointer(&buf[0])
-	} else {
-		_p0 = unsafe.Pointer(&_zero)
-	}
-	r0, _, e1 := Syscall(SYS_GETRANDOM, uintptr(_p0), uintptr(len(buf)), uintptr(flags))
-	n = int(r0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func Getrusage(who int, rusage *Rusage) (err error) {
 	_, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0)
 	if e1 != 0 {
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go
index d3e38f681ab0345eac069c79580f7f9cba3d4b7a..f485dbf4565671fd01bc685d7de256cbbac0dee2 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go
@@ -341,6 +341,7 @@ const (
 	SYS_STATX                   = 332
 	SYS_IO_PGETEVENTS           = 333
 	SYS_RSEQ                    = 334
+	SYS_URETPROBE               = 335
 	SYS_PIDFD_SEND_SIGNAL       = 424
 	SYS_IO_URING_SETUP          = 425
 	SYS_IO_URING_ENTER          = 426
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go
index 6c778c23278f923fc4424265cacc92280877460f..1893e2fe884044dc9724126bf8565ca9fca04748 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go
@@ -85,7 +85,7 @@ const (
 	SYS_SPLICE                  = 76
 	SYS_TEE                     = 77
 	SYS_READLINKAT              = 78
-	SYS_FSTATAT                 = 79
+	SYS_NEWFSTATAT              = 79
 	SYS_FSTAT                   = 80
 	SYS_SYNC                    = 81
 	SYS_FSYNC                   = 82
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go
index 37281cf51a80bbcae0342a567bad5da4e089d2d6..16a4017da0ab2fbb8868a34cf7e90df9b052577e 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go
@@ -84,6 +84,8 @@ const (
 	SYS_SPLICE                  = 76
 	SYS_TEE                     = 77
 	SYS_READLINKAT              = 78
+	SYS_NEWFSTATAT              = 79
+	SYS_FSTAT                   = 80
 	SYS_SYNC                    = 81
 	SYS_FSYNC                   = 82
 	SYS_FDATASYNC               = 83
diff --git a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go
index 9889f6a5591b62a375a69a718b692bcb4328852b..a5459e766f59dbfbd2751e1ecc5f7f1c4561bfb4 100644
--- a/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go
+++ b/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go
@@ -84,7 +84,7 @@ const (
 	SYS_SPLICE                  = 76
 	SYS_TEE                     = 77
 	SYS_READLINKAT              = 78
-	SYS_FSTATAT                 = 79
+	SYS_NEWFSTATAT              = 79
 	SYS_FSTAT                   = 80
 	SYS_SYNC                    = 81
 	SYS_FSYNC                   = 82
diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go
index 9f2550dc3120d0979c919fb324f44b02a59a438f..3a69e4549626895e42649743d5a21d9d19671f1e 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_linux.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go
@@ -87,31 +87,35 @@ type StatxTimestamp struct {
 }
 
 type Statx_t struct {
-	Mask             uint32
-	Blksize          uint32
-	Attributes       uint64
-	Nlink            uint32
-	Uid              uint32
-	Gid              uint32
-	Mode             uint16
-	_                [1]uint16
-	Ino              uint64
-	Size             uint64
-	Blocks           uint64
-	Attributes_mask  uint64
-	Atime            StatxTimestamp
-	Btime            StatxTimestamp
-	Ctime            StatxTimestamp
-	Mtime            StatxTimestamp
-	Rdev_major       uint32
-	Rdev_minor       uint32
-	Dev_major        uint32
-	Dev_minor        uint32
-	Mnt_id           uint64
-	Dio_mem_align    uint32
-	Dio_offset_align uint32
-	Subvol           uint64
-	_                [11]uint64
+	Mask                      uint32
+	Blksize                   uint32
+	Attributes                uint64
+	Nlink                     uint32
+	Uid                       uint32
+	Gid                       uint32
+	Mode                      uint16
+	_                         [1]uint16
+	Ino                       uint64
+	Size                      uint64
+	Blocks                    uint64
+	Attributes_mask           uint64
+	Atime                     StatxTimestamp
+	Btime                     StatxTimestamp
+	Ctime                     StatxTimestamp
+	Mtime                     StatxTimestamp
+	Rdev_major                uint32
+	Rdev_minor                uint32
+	Dev_major                 uint32
+	Dev_minor                 uint32
+	Mnt_id                    uint64
+	Dio_mem_align             uint32
+	Dio_offset_align          uint32
+	Subvol                    uint64
+	Atomic_write_unit_min     uint32
+	Atomic_write_unit_max     uint32
+	Atomic_write_segments_max uint32
+	_                         [1]uint32
+	_                         [9]uint64
 }
 
 type Fsid struct {
@@ -516,6 +520,29 @@ type TCPInfo struct {
 	Total_rto_time       uint32
 }
 
+type TCPVegasInfo struct {
+	Enabled uint32
+	Rttcnt  uint32
+	Rtt     uint32
+	Minrtt  uint32
+}
+
+type TCPDCTCPInfo struct {
+	Enabled  uint16
+	Ce_state uint16
+	Alpha    uint32
+	Ab_ecn   uint32
+	Ab_tot   uint32
+}
+
+type TCPBBRInfo struct {
+	Bw_lo       uint32
+	Bw_hi       uint32
+	Min_rtt     uint32
+	Pacing_gain uint32
+	Cwnd_gain   uint32
+}
+
 type CanFilter struct {
 	Id   uint32
 	Mask uint32
@@ -557,6 +584,7 @@ const (
 	SizeofICMPv6Filter      = 0x20
 	SizeofUcred             = 0xc
 	SizeofTCPInfo           = 0xf8
+	SizeofTCPCCInfo         = 0x14
 	SizeofCanFilter         = 0x8
 	SizeofTCPRepairOpt      = 0x8
 )
@@ -3766,7 +3794,7 @@ const (
 	ETHTOOL_MSG_PSE_GET                       = 0x24
 	ETHTOOL_MSG_PSE_SET                       = 0x25
 	ETHTOOL_MSG_RSS_GET                       = 0x26
-	ETHTOOL_MSG_USER_MAX                      = 0x2b
+	ETHTOOL_MSG_USER_MAX                      = 0x2c
 	ETHTOOL_MSG_KERNEL_NONE                   = 0x0
 	ETHTOOL_MSG_STRSET_GET_REPLY              = 0x1
 	ETHTOOL_MSG_LINKINFO_GET_REPLY            = 0x2
@@ -3806,7 +3834,7 @@ const (
 	ETHTOOL_MSG_MODULE_NTF                    = 0x24
 	ETHTOOL_MSG_PSE_GET_REPLY                 = 0x25
 	ETHTOOL_MSG_RSS_GET_REPLY                 = 0x26
-	ETHTOOL_MSG_KERNEL_MAX                    = 0x2b
+	ETHTOOL_MSG_KERNEL_MAX                    = 0x2c
 	ETHTOOL_FLAG_COMPACT_BITSETS              = 0x1
 	ETHTOOL_FLAG_OMIT_REPLY                   = 0x2
 	ETHTOOL_FLAG_STATS                        = 0x4
@@ -3951,7 +3979,7 @@ const (
 	ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL   = 0x17
 	ETHTOOL_A_COALESCE_USE_CQE_MODE_TX        = 0x18
 	ETHTOOL_A_COALESCE_USE_CQE_MODE_RX        = 0x19
-	ETHTOOL_A_COALESCE_MAX                    = 0x1c
+	ETHTOOL_A_COALESCE_MAX                    = 0x1e
 	ETHTOOL_A_PAUSE_UNSPEC                    = 0x0
 	ETHTOOL_A_PAUSE_HEADER                    = 0x1
 	ETHTOOL_A_PAUSE_AUTONEG                   = 0x2
@@ -4609,7 +4637,7 @@ const (
 	NL80211_ATTR_MAC_HINT                                   = 0xc8
 	NL80211_ATTR_MAC_MASK                                   = 0xd7
 	NL80211_ATTR_MAX_AP_ASSOC_STA                           = 0xca
-	NL80211_ATTR_MAX                                        = 0x14a
+	NL80211_ATTR_MAX                                        = 0x14c
 	NL80211_ATTR_MAX_CRIT_PROT_DURATION                     = 0xb4
 	NL80211_ATTR_MAX_CSA_COUNTERS                           = 0xce
 	NL80211_ATTR_MAX_MATCH_SETS                             = 0x85
@@ -5213,7 +5241,7 @@ const (
 	NL80211_FREQUENCY_ATTR_GO_CONCURRENT                    = 0xf
 	NL80211_FREQUENCY_ATTR_INDOOR_ONLY                      = 0xe
 	NL80211_FREQUENCY_ATTR_IR_CONCURRENT                    = 0xf
-	NL80211_FREQUENCY_ATTR_MAX                              = 0x20
+	NL80211_FREQUENCY_ATTR_MAX                              = 0x21
 	NL80211_FREQUENCY_ATTR_MAX_TX_POWER                     = 0x6
 	NL80211_FREQUENCY_ATTR_NO_10MHZ                         = 0x11
 	NL80211_FREQUENCY_ATTR_NO_160MHZ                        = 0xc
diff --git a/vendor/golang.org/x/sys/windows/dll_windows.go b/vendor/golang.org/x/sys/windows/dll_windows.go
index 115341fba66dab28536543805dbbdfd3a88500a1..4e613cf6335ceaaaf3075e177938ef11688461a4 100644
--- a/vendor/golang.org/x/sys/windows/dll_windows.go
+++ b/vendor/golang.org/x/sys/windows/dll_windows.go
@@ -65,7 +65,7 @@ func LoadDLL(name string) (dll *DLL, err error) {
 	return d, nil
 }
 
-// MustLoadDLL is like LoadDLL but panics if load operation failes.
+// MustLoadDLL is like LoadDLL but panics if load operation fails.
 func MustLoadDLL(name string) *DLL {
 	d, e := LoadDLL(name)
 	if e != nil {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 7ababf1debbd69568b29876d2cebae2c61a0712a..a0846cda8f57c18a6d0063e345e2cbc39d2933c6 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -49,9 +49,10 @@ github.com/docker/go-connections/tlsconfig
 # github.com/docker/go-units v0.5.0
 ## explicit
 github.com/docker/go-units
-# github.com/fsnotify/fsnotify v1.7.0
+# github.com/fsnotify/fsnotify v1.8.0
 ## explicit; go 1.17
 github.com/fsnotify/fsnotify
+github.com/fsnotify/fsnotify/internal
 # github.com/go-ole/go-ole v1.3.0
 ## explicit; go 1.12
 github.com/go-ole/go-ole
@@ -102,7 +103,7 @@ github.com/opencontainers/image-spec/specs-go/v1
 # github.com/pkg/errors v0.9.1
 ## explicit
 github.com/pkg/errors
-# github.com/pkg/sftp v1.13.6
+# github.com/pkg/sftp v1.13.7
 ## explicit; go 1.15
 github.com/pkg/sftp
 github.com/pkg/sftp/internal/encoding/ssh/filexfer
@@ -130,7 +131,7 @@ github.com/stretchr/testify/require
 # github.com/tklauser/go-sysconf v0.3.14
 ## explicit; go 1.18
 github.com/tklauser/go-sysconf
-# github.com/tklauser/numcpus v0.8.0
+# github.com/tklauser/numcpus v0.9.0
 ## explicit; go 1.18
 github.com/tklauser/numcpus
 # github.com/yusufpapurcu/wmi v1.2.4
@@ -150,7 +151,7 @@ go.uber.org/zap/internal/exit
 go.uber.org/zap/internal/pool
 go.uber.org/zap/internal/stacktrace
 go.uber.org/zap/zapcore
-# golang.org/x/crypto v0.27.0
+# golang.org/x/crypto v0.28.0
 ## explicit; go 1.20
 golang.org/x/crypto/blowfish
 golang.org/x/crypto/chacha20
@@ -169,12 +170,12 @@ golang.org/x/net/proxy
 # golang.org/x/sync v0.8.0
 ## explicit; go 1.18
 golang.org/x/sync/errgroup
-# golang.org/x/sys v0.25.0
+# golang.org/x/sys v0.26.0
 ## explicit; go 1.18
 golang.org/x/sys/cpu
 golang.org/x/sys/unix
 golang.org/x/sys/windows
-# golang.org/x/text v0.18.0
+# golang.org/x/text v0.19.0
 ## explicit; go 1.18
 golang.org/x/text/cases
 golang.org/x/text/internal